4 min readBy Muhammad Shahid
IDX10503 Signature Validation Failed in ASP.NET Core JWT
IDX10503: Signature validation failed — what it actually means in ASP.NET Core JWT bearer auth: kid vs symmetric keys, disposed RSA, Identity opaque tokens, and the checks that are not this error.
Part of Auth & Tokens
IDX10503: Signature validation failed is IdentityModel saying: none of the keys you configured could verify this token’s signature. The rest of the sentence is often a lie. “Token does not have a kid” shows up on symmetric HS256 APIs that never used a kid. Developers paste the whole line into Google. This URL is that paste.
Issuing JWTs, lifetimes, and policies stay in the JWT checklist. Opaque Identity API tokens vs JWT is MapIdentityApi vs JWT. Refresh rotation is refresh token rotation. I will not retell those. Here I only decode signature failures on AddJwtBearer.
Read the line before you add a kid
Typical log:
IDX10503: Signature validation failed. Token does not have a kid.
Keys tried: '[PII is hidden]'. Number of keys in TokenValidationParameters: '1'.
Three facts:
- Signature check failed — that part is true
- “No kid” is a key-selection hint, not a requirement of JWS. Symmetric keys often have no
kid - PII hidden — turn on IdentityModel PII logging in Development only (
IdentityModelEventSource.ShowPII = true) so you can see the key id it tried
Do not add a random kid header to “satisfy the error.” That does not fix a wrong secret.
Cause 1: The secret on issue is not the secret on validate
Angular 401s. jwt.io says the token is valid with the secret you typed there. The API uses Jwt:Key from App Service application settings, which is still the old 16-character string from the first tutorial.
HS256 with a key shorter than the algorithm expects, or UTF-8 vs Base64 mismatch (GetBytes vs FromBase64String), produces IDX10503, not a friendly “key too short.”
Fix: one secret, one encoding, staged and production slot settings in sync. Document the byte length. I do not put the key in appsettings.json in git — that is the checklist article.
Cause 2: You disposed the RSA key inside using
Manual ValidateToken in a helper:
using var rsa = RSA.Create();
rsa.ImportParameters(parameters);
var key = new RsaSecurityKey(rsa);
handler.ValidateToken(token, new TokenValidationParameters
{
IssuerSigningKey = key,
// ...
}, out _);
IdentityModel caches signature providers. The next request reuses a disposed RSA. First call works, second IDX10503, third works. That pattern is all over Stack Overflow.
Fix: keep the RsaSecurityKey for the app lifetime (singleton / AddJwtBearer options), or set:
options.TokenValidationParameters.CryptoProviderFactory = new CryptoProviderFactory
{
CacheSignatureProviders = false,
};
Prefer not disposing the key you registered with the host. Disabling cache is the hotfix when a library constructs keys per request.
Cause 3: Angular sent an opaque Identity token into JWT bearer
MapIdentityApi login returns an opaque access token. AddJwtBearer tries to parse it as a JWS. Signature validation is meaningless — it is not a JWT. The exception may still say IDX10503 (or a parse error one layer up).
If /login came from Identity API endpoints and [Authorize] uses JWT bearer, you mixed two token types. Pick one pipeline. Details: MapIdentityApi opaque vs JWT.
Cause 4: Access token vs refresh token in the Authorization header
The interceptor attached the refresh token (or the Identity cookie value) as Bearer. jwt.io shows three segments but a different signing key than IssuerSigningKey. IDX10503.
Fix the Angular attach path: JWT interceptors. Do not “fix” it by turning ValidateIssuerSigningKey off.
What IDX10503 is not
| Log / status | Look here instead |
|---|---|
| IDX10223 / lifetime | Clock skew, expired access token, refresh flow |
| IDX10214 / audience | ValidAudience vs aud claim |
| IDX10204 / issuer | ValidIssuer vs iss |
| 401 with no IdentityModel line | Missing Authorization header, wrong scheme, CORS preflight |
Audience and issuer failures are not signature failures. Do not rotate the signing key because aud was spa and the API expected api.
Checklist I run in ten minutes
- Decode the token (header + payload only) — is it a JWT at all?
- Compare iss / aud / alg to
TokenValidationParameters(if those are wrong you should see IDX102xx, not 10503 — unless the token is garbage) - Confirm the same key bytes used to sign in the issuer project
- If RSA/EC: no
usingaround a key the handler caches - If Identity API login: stop sending that string to
AddJwtBearer - Enable PII in Development, reproduce once, turn it off
Related reading
- ASP.NET Core JWT auth checklist
- MapIdentityApi opaque tokens vs JWT
- Angular JWT interceptors
- JWT refresh token rotation
401s that only happen on the second request after a key was constructed in a using? Contact me — bring the IdentityModel line, not a screenshot of jwt.io alone.