In setting up a web service to take in a token generated from a B2C call to https://login.microsoftonline.com/{tenant Id}/oauth2/v2.0/token
I keep getting the IDX10503 error.
I generate the token in Postman which uses the api's client Id
and client secret
and all is good. But when I send it in to the api's jwt token handling, I get the error detailed below. Note I reuse the client secret
as the key
in the example. Is that my issue? If so where do I actually get the secret in B2C?
How can I resolve this?
Error
SecurityTokenSignatureKeyNotFoundException: IDX10503: Signature validation failed. Token does not have a kid. Keys tried: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Number of keys in TokenValidationParameters: '1'. Number of keys in Configuration: '0'.
Minimally Reproducable Code
var secret = "{B2C Registered App Client Secret}";
var jwt = "eyJ0eXA...Z5zA";
var jwtSource = jwt.Replace("Bearer ", "");
var secretBytes = Encoding.ASCII.GetBytes(secret);
// Doesn't show the PII text, unclear why, but out of the scope for this error.
IdentityModelEventSource.LogCompleteSecurityArtifact = true;
// Validate
var TokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(secretBytes)
};
/* Exception thrown here */
var result = TokenHandler.ValidateToken(jwtSource,
validationParameters,
out Microsoft.IdentityModel.Tokens.SecurityToken validatedToken);
Postman Token Generation
Postman Access Url (998c is the proper TenantId): https://login.microsoftonline.com/998c...dbe3/oauth2/v2.0/token
Token Result - Jwt.IO Report f
{
"typ": "JWT",
"nonce": "MWR2cRS...CBAI_Bc",
"alg": "RS256",
"x5t": "9GmnyF...Lo7Y",
"kid": "9GmnyF...Lo7Y"
}
Discovery Document
https://{TenantName}.b2clogin.com/{TenantName}.onmicrosoft.com/b2c_1_signupsignin/discovery/v2.0/keys
"keys": [
{
"kid": "X5e...aNk",
"nbf": 1493763266,
"use": "sig",
"kty": "RSA",
"e": "AQAB",
"n": "tVKUt...QVXw"
}
Reply To Dave D
The metadataUrl
(login.microsoft...) reports an html page back as "You need to login". I believe the actual one is reported in B2C.
So for the B2C instance's discovery doc the actual open-id url is https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/b2c_1_signupsignin/v2.0/.well-known/openid-configuration";
and I changed it as such.
Updated Dave D Code
I had to update the issuers to not get an error when processing code about unknown issuer
var jwt = "eyJ...{Fresh Postman Token w/client id/secret}...5A";
var tenantId = "998...be3"; // tenantId in Dave D example
var tenant = "{b2C Tenant Name}";
IdentityModelEventSource.ShowPII = true;
IdentityModelEventSource.LogCompleteSecurityArtifact = true;
//var metadataUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
var metadataUrl = $"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/b2c_1_signupsignin/v2.0/.well-known/openid-configuration";
var oidcConfigurationManager
= new ConfigurationManager<OpenIdConnectConfiguration>
(metadataUrl,
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var metadataDocument = await oidcConfigurationManager.GetConfigurationAsync(CancellationToken.None);
var tokenHandler = new JwtSecurityTokenHandler();
var result = await tokenHandler.ValidateTokenAsync(jwt, new TokenValidationParameters
{
ValidIssuer = metadataDocument.Issuer,
ValidIssuers = new[] { $"https://{tenant}.b2clogin.com/{tenantId}/v2.0/",
$"https://sts.windows.net/{tenantId}/"},
ValidateIssuer = true,
IssuerSigningKeys = metadataDocument.SigningKeys,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidateAudience = false,
});
Exception Garnered upon call to Validate
IDX10503: Signature validation failed. Token does not have a kid.
Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey,
KeyId: 'X5e...{key1}...BwaNk', InternalId: 'X5e...{key1}...BwaNk'. ,
KeyId: X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk
'. Number of keys in TokenValidationParameters: '1'.
Number of keys in Configuration: '0'.
Exceptions caught: ''.
token: 'eyJ0...W_AOPHAqo5A'.
See https://aka.ms/IDX10503 for details.
The value used to sign the token isn't your app secret defined in the app registration, that secret is just to secure the calls your app makes to B2C/Entra ID.
You get the keys to verify signing from the
jwks_uri
endpoint defined in the OpenID Connect metadata document.In your case I can see you're using the token endpoint
https://login.microsoftonline.com/{tenant Id}/oauth2/v2.0/token
, which is defined in the metadata document athttps://login.microsoftonline.com/{tenant Id}/v2.0/.well-known/openid-configuration
, so it's that document'sjwks_uri
you'll need to use to get the signing keys.Given that you're using .NET there are libraries to help with loading & caching the OpenID Connect metadata doc, so you should be able to use the following code:
That example uses
System.IdentityModel.Tokens.Jwt 7.0.0
andMicrosoft.IdentityModel.Protocols.OpenIdConnect 7.0.0
PS: I'm assuming you need the Entra ID v2 metadata document based on the token endpoint you posted. If you're passing a policy to the token endpoint (making it a B2C token request, rather than an Entra ID token request) then you need to use the B2C metadata which would be
https://{tenantName}.b2clogin.com/{tenantName}.onmicrosoft.com/{policyName}/v2.0/.well-known/openid-configuration
.PPS: if you want to show the info hidden by the PII warning then the line you need is
IdentityModelEventSource.ShowPII = true
.