Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
Ask Question
I have the below JWT token,
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnRpZCIsImF1ZCI6ImNsaWVudGlkIiwic3ViIjoiMTIzIiwiYSI6IjQ1NiIsImlhdCI6MTYyMTc5OTU5OCwiZXhwIjoxNjIxNzk5NjU4fQ.hglbX63zhPwTOsB-zSiOMfxEKl5OaIk6zX1o9-LEhP3nro8fa5_3QyIH7I5971j-xuO1bccX1TOh0kNcQ-ACAg
Which is generated using,
public static string GenerateToken(string key, string a1, string a2)
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var token = new JwtSecurityToken(
claims: new Claim[]
new Claim(JwtRegisteredClaimNames.Iss, "clientid"),
new Claim(JwtRegisteredClaimNames.Aud, "clientid"),
new Claim(JwtRegisteredClaimNames.Sub, a1),
new Claim("a", a2),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
//notBefore: new DateTimeOffset(DateTime.Now).DateTime,
expires: new DateTimeOffset(DateTime.Now.AddMinutes(1)).DateTime,
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha512)
return new JwtSecurityTokenHandler().WriteToken(token);
var key = "Ym7AD3OT2kpuIRcVAXCweYhV64B0Oi9ETAO6XRbqB8LDL3tF4bMk9x/59PljcGbP5v38BSzCjD1VTwuO6iWA8uzDVAjw2fMNfcT2/LyRlMOsynblo3envlivtgHnKkZj6HqRrG5ltgwy5NsCQ7WwwYPkldhLTF+wUYAnq28+QnU=";
// Key is test
var token = GenerateToken(key, "123", "456");
After getting token I am validating using below code,
var key = "Ym7AD3OT2kpuIRcVAXCweYhV64B0Oi9ETAO6XRbqB8LDL3tF4bMk9x/59PljcGbP5v38BSzCjD1VTwuO6iWA8uzDVAjw2fMNfcT2/LyRlMOsynblo3envlivtgHnKkZj6HqRrG5ltgwy5NsCQ7WwwYPkldhLTF+wUYAnq28+QnU=";
// key is test
var hmac = new HMACSHA512(Convert.FromBase64String(key));
var validationParameters = new TokenValidationParameters
ValidAudience = "clientid",
ValidIssuer = "clientid",
IssuerSigningKey = new SymmetricSecurityKey(hmac.Key)
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.ValidateToken(token, validationParameters, out var validToken);
But I am getting below error,
IDX10503: Signature validation failed. Token does not have a kid. Keys tried: 'System.Text.StringBuilder'.
Exceptions caught:
'System.Text.StringBuilder'.
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
–
In addition to being able to use a base64 encoded key value as demonstrated above, you can also use a string. There are some caveats though.
The handlers that use a SymmetricSecurityKey to create a HMACSHA256 or HMACSHA512, don't perform the zero-bit padding out to the hashsize (As per RFC2104, Step 1), which causes the signing algorithm to fail. Everything works fine if the string which is used as the key is greater than the keysize (256 or 512 bit).
Below are two ways to generate an appropriate SymmetricSecurityKey for a shorter signing string:
First Approach: Manually pad the string with zero bytes and generate a base64 key. Something like CyberChef could be used to append /0 chars out to either 32 chars for 256 bit or 64 chars for 512 bit:
Manually generating a base64 key via CyberChef
These keys will produce an identical JWT
Equivalent keys in JWT.io
This can be used as demonstrated by Imran above...
// Set up the signingKey for HS256
// Base64 signingKey
//SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Convert.FromBase64String("c2VjcmV0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="));
Second Approach: Without doing the base64 conversion, the SymmetricSecurityKey will take an ascii byte[], so long as you have already done the padding:
// Short String signingKey
// Note: Not advised. Short keys can be bruteforced, allowing tokens to be forged.
// Note: manually padding to 256 bits if it is a short key, as the SymmetricSignatureProvider does not do the HMACSHA256 RFC2104 padding for you.
// SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("secret".PadRight((256/8), '\0')));
SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("secret".PadRight((512/8), '\0')));
With either approach, you can use the resulting SymmetricSecurityKey in the default JwtBearer handler:
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
ValidateAudience = true,
ValidateIssuer = true,
ValidIssuer = "https://localhost:7046/",
ValidAudience = "https://localhost:7046/",
RequireSignedTokens = true,
IssuerSigningKey = signingKey,
ValidateLifetime = true
builder.Services.AddAuthorization(auth =>
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
As a final note, I had some issues in one test where I had added the Microsoft.IdentityModel.Tokens package. I have still to run this down, but there may be a conflicting class in that package...
I have a sample using this approach here: Example project in GitHub
It is worth noting that using a short symmetric key for your HMAC is not advised, as the secret can be bruteforced with relative ease, especially if it show up in a known password list. Worth checking your key past HaveIBeenPwned - PwnedPasswords to have some confidence of whether it has been used before. This is mainly provided for people creating demonstrations of short HMAC's. If you are uncomfortable with using PwnedPasswords, see the following about the K-anonymity model they use, and experiment while watching your network tab in browser tools to see how it works.
I receive same error message, but my issue was different. Problem was with this line:
new Ed25519PublicKeyParameters(Encoding.UTF8.GetBytes(key), 0);
So I change to this:
new Ed25519PublicKeyParameters(Convert.FromBase64String(key), 0);
Key was saved to file as base64, but I was loading it as UTF8 bytes.
–
This is a pretty generic error with regards to debugging. What's basically happening:
You tried to sign in to the api
You got redirected to IdentityServer to log in.
You succesfully logged in, IdentityServer gives you a token.
You try to log in to the api with your token
The api tries to verify your token.
The error is telling you the api tried to verify the token, but failed. The "kid" the api is complaining about is the key ID
of whatever secret it used to verify the token. The kid is probably a thumbprint of the certificate used by IdentityServer as a signing credential. To verify your token, your api will call IdentityServer independently, and retrieve the needed data to verify it. Specifically, your api will try to go to youridentityserver.com/.well-known/openid-configuration/jwks
. There, it should find the kid with public key to verify the token.
It helps to find the cause of your issue if you enable PII in the application throwing the error by adding this to Startup.ConfigureServices
:
IdentityModelEventSource.ShowPII = true;
This will show you:
The kid of the value the api tried to validate the passed in token with.
The token itself, which in my case also includes a kid, in my case the thumbprint of the signing credential certificate used (if you used a certificate).
Using this information, you can hopefully find the cause. Things to check:
Is the IdentityServer that gave you the token the same one that the api is calling to get the kid from? If you have multiple IdentityServers behind a load balancer you may run into issues.
Does the JWKS endpoint on all IdentityServer instances return the same kid? If you are using different certificates on different machines, or don't have the certificate installed, you may run into issues.
More info here: https://docs.wso2.com/display/IS540/JSON+Web+Key+Set+Endpoint
This link is for a different identity provider, but IdentityServer returns the same type of data.
I had this same error message and the underlying cause for me was that the JWT key was mismatched between the API project and the web/client project. this should be obvious but they appeared to be the same based on the appsettings.json and web.config files, however the API project was loading an alternative dev/staging/prod configuration from a publish profile.
As soon as the keys were identical the code worked as expected. I use UTF8.GetBytes for the key on server + client and it works fine.
In my case, I was using a random string as JWT signing key in app setting, later I simply updated it with encoded Base 64 format in app setting only.
//in app settings json
Jwt:Key = {encoded base 64 string}
// in generating token & validating token section
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["Jwt:Key"]));
var tokenDescriptor = new SecurityTokenDescriptor
Subject = new ClaimsIdentity(new[] { new Claim("Name", user.UserName) }),
Expires = DateTime.UtcNow.AddMinutes(double.Parse(configuration["Token:ExpiryTimeInMinutes"])),
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature),
Issuer = configuration["Jwt:Issuer"],
Audience = configuration["Jwt:Audience"]
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.