admin 管理员组文章数量: 1086019
I'm encountering an issue with validating a perfectly valid access_token
issued by Azure Entra ID. Despite using the correct RSA public key that was used to sign the token, the signature validation is failing.
Environment & Context
- Identity Provider: Azure Entra ID
- Token Type: Access token (JWT)
- Algorithm: RS256 (RSA with SHA-256 and PKCS#1 v1.5 padding)
The token itself is unmodified, and manual inspection confirms that the header, payload, and signature are all properly formed. The problem arises when attempting to validate the token's signature.
Config
The following is the config I have so far. My config for application tokens works fine, but user tokens are failing.
.AddScheme<JwtBearerOptions, CISJwtBearerHandler>(
CISAuthorityTypes.UserAuthenticationType,
(o) => {
//var validAudience = cisAuthenticationOptions.ApplicationIdentityAudience;
o.Challenge = CISAuthorityTypes.UserAuthenticationType;
//o.Audience = validAudience;
o.IncludeErrorDetails = isDevelopmentMode;
o.RequireHttpsMetadata = isDevelopmentMode;
o.Authority = msOnlineAuthority.ToString();
o.ConfigurationManager = configurationManager;
string[] allowedIssuers = ["/", "/"];
o.TokenValidationParameters = new TokenValidationParameters() {
ValidateAudience = false,
//ValidAudience = validAudience,
//ValidateLifetime = true,
ValidateIssuer = false,
IssuerSigningKeys = keys,
TryAllIssuerSigningKeys = false,
ValidateIssuerSigningKey = false,
LogValidationExceptions = isDevelopmentMode,
ValidateSignatureLast = false,
NameClaimType = CISClaimTypes.NameClaimName,
RoleClaimType = CISClaimTypes.RoleClaimName,
////IssuerSigningKeyValidatorUsingConfiguration = null, // TODO
////IssuerSigningKeyResolver = null, // TODO
AuthenticationType = CISAuthorityTypes.UserAuthenticationType,
#region TODO REMOVE DEBUG BLOCK
//SignatureValidator = (token, parameters) => {
// var tokenHandler = new JwtSecurityTokenHandler();
// var innerJwt = new JwtSecurityToken(token);
// if (parameters.IssuerSigningKeys != null) {
// var keys = (from i in parameters.IssuerSigningKeys
// where i.KeyId == innerJwt.Header.Kid
// select i).ToArray();
// foreach (var key in keys) {
// if (key is RsaSecurityKey keyAsRsaSecKey) {
// JwtSignatureValidator.ValidateSignature(keyAsRsaSecKey, token, null);
// } else {
// JwtSignatureValidator.ValidateSignature((X509SecurityKey)key, token, null);
// }
// //try {
// // // Create a copy of the parameters with only this key
// // var singleKeyParameters = parameters.Clone();
// // singleKeyParameters.IssuerSigningKey = key;
// // singleKeyParameters.IssuerSigningKeys = new[] { key };
// // singleKeyParameters.SignatureValidator = null;
// // // This will throw if the signature validation fails.
// // SecurityToken validatedToken;
// // tokenHandler.ValidateToken(token, singleKeyParameters, out validatedToken);
// // // If validation succeeded, return the validated token.
// // return validatedToken;
// //} catch (SecurityTokenInvalidSignatureException) {
// // // Try the next key if the current key fails.
// //}
// }
// }
// return innerJwt;
//},
#endregion
IssuerValidator = (issuer, securityToken, parameters) => {
var q = from i in allowedIssuers
where issuer.StartsWith(i, StringComparison.OrdinalIgnoreCase)
select i;
if (q.Any()) {
var jwtToken = (JwtSecurityToken)securityToken;
var tempPrincipal = new ClaimsPrincipal(new ClaimsIdentity(jwtToken.Claims, CISAuthorityTypes.UserAuthenticationType));
if (CISJwtSecurityTokenValidator.ValidateUser(tempPrincipal))
return issuer;
}
throw new SecurityTokenInvalidIssuerException("Invalid issuer.");
}
};
}
)
I've added a test class to double check the validation of the token.
It also confirms that the validation is failing. Here are the two validation methods that handle both RsaSecurityKey
and X509SecurityKey
keys:
NOTE: the code shown here is provided as a troublshooting class to double check the access_token against its key. It is applied via the TokenValidationParameters.SignatureValidator
delegate.
// TODO: Remove this debug class
public static class JwtSignatureValidator
{
/// <summary>
/// Validates the signature of a JWT using an RsaSecurityKey.
/// </summary>
/// <param name="key">The RsaSecurityKey to use for validation.</param>
/// <param name="token">The JWT token string.</param>
/// <returns>True if the signature is valid; otherwise, false.</returns>
public static bool ValidateSignature(RsaSecurityKey key, string token)
{
var parts = token.Split('.');
if (parts.Length != 3)
throw new ArgumentException("Invalid JWT token format.", nameof(token));
// JWT signing input is the header and payload parts concatenated with a period.
string signingInput = $"{parts[0]}.{parts[1]}";
byte[] signingBytes = Encoding.UTF8.GetBytes(signingInput);
byte[] signature = Base64UrlDecode(parts[2]);
// Get the RSA instance from the key.
RSA rsa = key.Rsa;
if (rsa == null)
{
rsa = RSA.Create();
rsa.ImportParameters(key.Parameters);
}
// Verify the signature with SHA256 and PKCS#1 v1.5 padding.
var returnValue = rsa.VerifyData(signingBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Redundant re-verification with the same padding.
returnValue = rsa.VerifyData(signingBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return returnValue;
}
/// <summary>
/// Validates the signature of a JWT using an X509SecurityKey.
/// </summary>
/// <param name="key">The X509SecurityKey to use for validation.</param>
/// <param name="token">The JWT token string.</param>
/// <returns>True if the signature is valid; otherwise, false.</returns>
public static bool ValidateSignature(X509SecurityKey key, string token)
{
var parts = token.Split('.');
if (parts.Length != 3)
throw new ArgumentException("Invalid JWT token format.", nameof(token));
string signingInput = $"{parts[0]}.{parts[1]}";
byte[] signingBytes = Encoding.UTF8.GetBytes(signingInput);
byte[] signature = Base64UrlDecode(parts[2]);
// Extract the RSA public key from the certificate.
var certificate = key.Certificate;
using (RSA rsa = certificate.GetRSAPublicKey())
{
var returnValue = rsa.VerifyData(signingBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Additional verification using RSASignaturePadding.Pss is performed here.
returnValue = rsa.VerifyData(signingBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
return returnValue;
}
}
/// <summary>
/// Decodes a Base64Url-encoded string.
/// </summary>
/// <param name="input">The Base64Url-encoded string.</param>
/// <returns>The decoded byte array.</returns>
private static byte[] Base64UrlDecode(string input)
{
string padded = input.Replace('-', '+').Replace('_', '/');
switch (padded.Length % 4)
{
case 2: padded += "=="; break;
case 3: padded += "="; break;
}
return Convert.FromBase64String(padded);
}
}
Even though the token is correctly generated and signed by Azure Entra ID, the signature validation fails when using the above code.
Potential problem: in the ValidateSignature(X509SecurityKey, string token)
method, the signature is first verified using RSASignaturePadding.Pkcs1
(which is correct for RS256), but then immediately re-verified using RSASignaturePadding.Pss
. Since RS256 tokens are signed with PKCS#1 v1.5 padding, the second check using PSS fails, and its result overrides the previous verification result.
Additional note: the redundant re-verification in the RsaSecurityKey
method (using the same padding twice) is unnecessary and could potentially be confusing, although it doesn't cause the failure seen with the X509 key.
Has anyone experienced a similar issue with Azure Entra ID tokens failing validation despite the keys matching? Could there be any nuances in the token signing process or the way the certificate is being used that might cause this? Any guidance on best practices for verifying these tokens would be greatly appreciated.
Thanks in advance for your help!
本文标签: cUser accesstokens are failing validationStack Overflow
版权声明:本文标题:c# - User access_tokens are failing validation - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1744057348a2526090.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论