I am trying to validate a valid JWT using this code below but am getting a strange error
[Error] IDX10516: Signature validation failed. Unable to match key:
kid: '-KI3Q9nNR7bRofxmeZoXqbHZGew'.
Number of keys in TokenValidationParameters: '1'.
Number of keys in Configuration: '0'.
Exceptions caught:
'[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Valid Lifetime: 'True'. Valid Issuer: 'False'
Code:
I had seen a lot of reference links on this issue but still not getting what am I missing?
I tried to test locally, its worked perfectly but after deployed to Azure Function its failing.
Configuration Settings:
"JwtSettings:TenantName": "OSHDev.onmicrosoft.com",
"JwtSettings:TenantId": "5492b240-96ee-44a1-bdcb-fa0ba0200111",
"JwtSettings:AadB2cInstance": "https://OSHDev.b2clogin.com/{0}/v2.0/",
"JwtSettings:OpeinConfigUrl":"https://OSHDev.b2clogin.com/OSHDev.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/v2.0/.well-known/openid-configuration",
public async Task<ClaimsPrincipal> ValidateAccessToken(string accessToken, JwtSettings jwtSettings, ILogger logger)
{
var audience = jwtSettings.Audience;
var tenant = jwtSettings.TenantName;
var tenantid = jwtSettings.TenantId;
var aadb2cInstance = jwtSettings.AadB2cInstance;
var openidconfigurl = jwtSettings.OpeinConfigUrl;
//Debugging purposes only, set this to false for production
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = false;
ConfigurationManager<OpenIdConnectConfiguration> configManager =
new ConfigurationManager<OpenIdConnectConfiguration>(
openidconfigurl,
new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config;
config = await configManager.GetConfigurationAsync();
//Microsoft Identity to override claim names . If we remove below code line, "sub" claim will not be visible. Its visible under "nameidentifier"
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler? tokenValidator = new JwtSecurityTokenHandler();
// Initialize the token validation parameters
TokenValidationParameters validationParameters = new TokenValidationParameters
{
// App Id URI and AppId of this service application are both valid audiences.
ValidateAudience = true,
ValidAudiences = new[] { audience },
ValidateIssuer= true,
ValidIssuers = new List<string>()
{
string.Format(CultureInfo.InvariantCulture, aadb2cInstance, tenantid),
string.Format(CultureInfo.InvariantCulture, aadb2cInstance, tenant)
},
ValidateIssuerSigningKey = true,
// Support Azure AD V1 and V2 endpoints.
IssuerSigningKeys = config.SigningKeys,
RequireSignedTokens = true,
//Debugging purposes only, set this to true for production
ValidateLifetime = true
};
try
{
//Validate JwTToken and return Claims Prinicpals
ClaimsPrincipal? claimsPrincipal = tokenValidator.ValidateToken(accessToken, validationParameters, out SecurityToken securityToken);
return claimsPrincipal;
}
catch (Exception ex)
{
logger.LogError(ex.Message);
}
return null;
}
Like the error said, your token validator try to find the public key that is used to sign your token in the jwks_uri, you can find it in your well-known URL.
In each token there is a header "KID", this header indicate the ID of the public key used to sign your token.
When i check your jwks_uri there is no KID with ID :
"-KI3Q9nNR7bRofxmeZoXqbHZGew"
But:
"veMP2TrHLgs4XvKYJhumvhPW6O-WpbdSGqKdetRmvxI"
Related
I have an angular app that I added Azure Active directory authentication and I got azure to return an access token with the user details.
I pass the ad token to my web api (running in .net 4.6.1 ) to check if the user has already been registered and I want to validate that the token is valid.
I've used the following code. But I am getting an error whenever I Tried to recover my configuration from ConfigurationManager
string tenantId = "someguid"; //not including these guids in here
var audience = "anotherguid";
var issuer = $"https://login.microsoftonline.com/{tenantId}/v2.0";
var stsDiscoveryEndpoint = $"https://login.microsoft.com/{tenantId}/v2.0/.well-known/openid-configuration";
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
try
{
CancellationToken cancellationToken = new CancellationToken();
var config = await configManager.GetConfigurationAsync(cancellationToken);
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningKeys = config.SigningKeys,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidateLifetime = true
};
SecurityToken validatedToken = new JwtSecurityToken();
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
return validatedToken;
}
IOException: IDX20807: Unable to retrieve document from: 'https://login.microsoft.com/145fa4fc-d5f6-489c-affn-6407cca77ef0/v2.0/.well-known/openid-configuration'. HttpResponseMessage: 'StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
Am I missing something in azure or should I be getting the config some other way? any direction would be appreciated.
The error IDX20807: Unable to retrieve document from: 'https://login.microsoft.com/145xxxxxxx7ccxxxxf0/v2.0/.well-known/openid-configuration
means : OIDC metadata
https://login.microsoftonline.com/.well-known/openid-configuration is
not valid due to something being wrong in web configuration or has
not been correctly configured in the application.Probably ,the
authority parameter is not recognized properly.
I see you have given wrong metadata url in place of https://login.microsoftonline.com as I see you have given wrong metadata url in place of https://login.microsoft.com
Also you need need to add "Authority" to the appsettings.json. which is the meta data url
Or else instance and domain must be given
(authority format: " https://login.microsoftonline.com/<tenantId>" )
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "yourtenantdomain",
"ClientId": "My Client Id",
"TenantId": "<common or organizations>", // must be there in Multi Tenant application
"CallbackPath": "/signin-oidc"
},
And try with dotnet version 4.7 or later and make sure to run on the tls version is 1.2 or later
I can't read token claims from Bearer JWT token.
Login is working, the HTTP request comes with a valid JWT token to the backend.
Login returns the token.
Here is my code on server side:
Program.cs
builder.Services.AddAuthentication(m => {
//m.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
m.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
m.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(conf => {
conf.RequireHttpsMetadata = false;
conf.SaveToken = true;
conf.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = Configuration["JWT-Issuer"],
ValidAudience = Configuration["JWT-Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT-Key"])),
ClockSkew = TimeSpan.Zero,
};
});
When I uncomment this line //m.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; postman says unauthorized. When it is kept commented, authorization succeeds.
Token is generated here.
GenerateToken Method:
private object GenerateJwtToken(string Id, string email, ApplicationUser appUser, string appUserRole, string FirstName)
{
List<Claim> claims = null;
claims = new List<Claim> {
new Claim(JwtRegisteredClaimNames.Email,email),
new Claim(JwtRegisteredClaimNames.Jti, appUser.Id),
new Claim("Role",appUserRole),
new Claim("UserName",appUser.UserName),
new Claim("TEMP", FirstName)
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT-Key"]));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expire = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JWT-Expiry"]));
var token = new JwtSecurityToken(
issuer: _configuration["JWT-Issuer"],
audience: _configuration["JWT-Issuer"],
claims: claims,
expires: expire,
signingCredentials: cred
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
When JWT Bearer token is passed to the API call decorated with [Authorize] and tested with debugger it shows that custom claim like TEMP is not present in the User.Claims List.
Reading Claims From Token
string Email = User.Claims.SingleOrDefault(x => x.Type.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")).Value;
string FirstName= User.Claims.SingleOrDefault(x => x.Type.Equals("TEMP")).Value;
Here, the email can be read successfully but I can't read the FirstName from the token. In fact User.Claims doesn't have the FirstName claim (I mean all the custom claims leaving the Registered JWT Default ClaimTypes), it only has the default token parameters which are emailaddress, id, role etc.
What should I do?
should I need to create custom authentication scheme for this purpose?
Internally in AddJwtBearer, there are some remapping done by the Token hander, where claims are renamed, like
email -> http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
This mapping can be turned of using:
// Or set this flag to false
.AddJwtBearer(opt =>
{
...
opt.MapInboundClaims = false;
});
or setting:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
The actual mapping can be found here
However, I am not sure why the TEMP claim is not found. What claims do you actually see in the authenticated User in your controller? Some claims in some cases are also ignored.
we are using Azure Active Directory for our company. We have an internal web project with Asp.Net Core 3.
We are using a React as a frontend. The user is able to login with his AAD Credentials and the React app gets a token. The Token should be used to access functions in the ASP.Net Core project. Therefore the token should be validated in ASP.Net Core. Here is my problem. I was able to call the token validation but I'm getting different errors.
The ASP.Net app: https://localhost:44350/
The react app: https://localhost:44395/
This is one of the ASP actions:
[HttpGet]
[Route("GetArticles")]
public async Task<JsonResult> GetArticles()
{
if (!Request.Headers.TryGetValue("Authorization", out var authorizationToken))
{
return Json(Unauthorized());
}
if (!authorizationToken.Any())
{
return Json(Unauthorized());
}
var jwt = await Validate(authorizationToken.First());
return Json(await _unitOfWork.ArticlesRepos.GetAllAsync());
}
The token is passed and the validation is triggered. The validation is in the Validate method:
public async Task<JwtSecurityToken> Validate(string token)
{
string tenant = _configuration.GetValue<string>("Tenant");
string publicKey = _configuration.GetValue<string>("AadPubKey");
//string stsDiscoveryEndpoint = $"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
//string stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token";
string stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = await configManager.GetConfigurationAsync();
var IssuerSigningKeys = config.SigningKeys;
var x = new X509Certificate2(Encoding.ASCII.GetBytes(publicKey));
var y = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(publicKey));
var rsa = new RSACryptoServiceProvider();
string exponentvalue = "AQAB";
var e = Base64UrlEncoder.DecodeBytes(exponentvalue);
var N = publicKey;
var modulus = Base64UrlEncoder.DecodeBytes(N);
rsa.ImportParameters(
new RSAParameters()
{
Modulus = modulus,
Exponent = e
});
var signingKey = new RsaSecurityKey(rsa);
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
ValidIssuer = stsDiscoveryEndpoint,
//ValidIssuer = "https://localhost:44350/",
//ValidAudience = "https://localhost:44395/",
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
var result = tokendHandler.ValidateToken(token, validationParameters, out SecurityToken jwt);
return jwt as JwtSecurityToken;
}
As you can see, we tried diffferent things. We are getting following errors at the line with
var result = tokendHandler.ValidateToken(token, validationParameters, out SecurityToken jwt);:
When we use IssuerSigningKey = y, =>
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException
HResult=0x80131500
Message = IDX10501: Signature validation failed. Unable to match key:
kid: 'someId'.
Exceptions caught:
'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: ''.'
is not supported
When we use IssuerSigningKey = signingKey, =>
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException
HResult=0x80131500
Message = IDX10501: Signature validation failed. Unable to match key:
kid: 'someId'.
Exceptions caught:
''.
I have no idea how to configure the TokenValidationParameters. When I look in https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys I see the key which is stated in the Exception. But there are numerous informations.
{"keys":
[{"kty":"RSA"
,"use":"sig"
,"kid":"someId"
,"x5t":"someId"
,"n":"longString"
,"e":"AQAB"
,"x5c":["otherLongString"]
,"issuer":"https://login.microsoftonline.com/myTenant/v2.0"},
{...},{...}]
How can we make the validation work?
Thanks in advance
Getting an IDX10634 error from an RS256 algorithm using a SymmetricSecurityKey looks like behavior that has been fixed in newer versions of IdentityModel. Which version of the IdentityModel assemblies are you using? Please try updating to the latest version (currently 6.7.1) and see if your issue persists.
As a few side notes :
JwtSecurityTokenHandler / TokenValidationParameters are meant to be long lived objects. TokenValidationParameters for example, has a cache that can be leveraged when reused.
Is this a SPA app? If it is, this sample should be useful:
https://github.com/Azure-Samples/ms-identity-javascript-react-spa-dotnetcore-webapi-obo
I am trying to validate a valid JWT using this code below but am getting a strange error
"IDX10501: Signature validation failed. Unable to match key:
kid: 'System.String'.
Exceptions caught:
'System.Text.StringBuilder'.
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'."
Here is my validation method
ClaimsPrincipal principal = null;
var token = "JWT GOES HERE"
try
{
string sec = "000uVmTXj5EzRjlnqruWF78JQZMT";
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var now = DateTime.UtcNow;
SecurityToken securityToken;
string tokenIssuer = "https://MyIssuer.com";
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidIssuer = tokenIssuer,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = securityKey
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
principal = handler.ValidateToken(token, validationParameters, out securityToken); <---Errors here
}
Here is the value of my JWT. I am using the correct issuer
{
"alg": "RS256",
"kid": "dev",
"x5t": "Sm7aAUSt4Fdv7X1b9jQDf8XwbvQ",
"pi.atm": "xxe8"
}.{
"scope": [],
"client_id": "ClientABC",
"iss": "https://MyIssuer.com",
"jti": "1JLDz",
"sub": "ClientABC",
"exp": 1601609852
}.[Signature]
What am I missing here? Is the the SymmetricSecurityKey since this algorithm is RS256? Am I missing something in my TokenValidationParameter?
Update
After futher investigation I am getting the error of.
IDX10501: Signature validation failed. Unable to match key:
kid: 'dev'.
Exceptions caught:
'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'TdfWgWjCVeM60F3C5TOogJuka1aR5FA_xchwhY9MHH4'.'
is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)
try to use SecurityAlgorithms.HmacSha256
Example when you issue the token:
Users user = _context.Users.FirstOrDefault(c => c.UserName == userName && c.Password == password);
if(user == null)
{
return Unauthorized();
}
Claim[] claims = new Claim[]
{
new Claim("Id", user.Id.ToString()),
new Claim("Name", user.Name),
new Claim("Email", user.Email),
};
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("000uVmTXj5EzRjlnqruWF78JQZMT"));
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new
JwtSecurityToken(
"MyProject",
"MyClient",
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: signingCredentials);
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
If you are using .net core app, then in Startup.cs, in ConfigureServices method write this code to validate the token:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "MyClient",
ValidIssuer = "MyProject",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("000uVmTXj5EzRjlnqruWF78JQZMT"))
};
});
Also don't forget to add the following lines to the Configure method in Startup.cs
app.UseAuthentication();
app.UseAuthorization();
The issue is that you are trying to use a symmetric key with an asymmetric algorithm. The RSA algorithm requires both a public and a private key.
Try using a symmetric algorithm instead, like HS256 (HMAC-SHA256).
Having come here looking for answers myself, let me share another scenario which may crop up in a production setting. So we were having this same cryptic authentication error. In our case, the client application was using our development environment's Token service to generate their bearer token and then sending it to production.
I don't know why it didn't outright just deny instead of failing on "Unable to match Key kid", but using the production token service solved it for us. So be sure to verify that first, since it's a relatively quick and simple thing to do.
in my .net core 2.2 microservice, I try to extract claims from a JWT token to do some authorization. authentication is done on another part of the system so I don't need to do it at this point.
I am using this code in the Startup.cs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var signingKey = Encoding.UTF8.GetBytes("SECRET_KEY");
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(signingKey)
};
});
On the controller I have this code:
[Authorize]
[HttpPost]
public async Task<ActionResult<CreateResponse>> Create()
{
var userIdClaim = HttpContext.User.Claims.Where(x => x.Type == "empId").SingleOrDefault();
return Ok($"Your User ID is {userIdClaim.Value} and you can create invoices!");
}
I always get this error message and "Unauthorized" response:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: '[PII is hidden]'.
Exceptions caught:
'[PII is hidden]'.
token: '[PII is hidden]'.
You can see the hidden details in development by adding the following to Configure() in the Startup class:
if (env.IsDevelopment())
{
IdentityModelEventSource.ShowPII = true;
}
Once you have the full message check the key being used is correct for the token.