具有多种身份验证方案的 SecurityTokenSignatureKeyNotFoundException

时间:2021-07-05 10:34:09

标签: asp.net-core

我有一个已设置应用注册的 Azure Active Directory。我有一个 B2C 租户,已注册应用并设置了多个用户流程。

在 Startup.cs 中,我配置了多个身份验证方案,如下所示。

services
    .AddAuthentication()
    .AddJwtBearer("Azure", options => 
    {
        options.Audience = "clientId";
        options.Authority = "https://login.microsoftonline.com/tenantId";
    })
    .AddJwtBearer("AzureB2cCustomerSignIn", options => 
    {
        options.Audience = "clientIdInB2c";
        options.Authority = "https://tentantName.b2clogin.com/tenantName.onmicrosoft.com/userFlowName/v2.0";
    })
    .AddJwtBearer("AzureB2cCustomerSignUp", options => 
    {
        options.Audience = "clientIdInB2c";
        options.Authority = "https://tentantName.b2clogin.com/tenantName.onmicrosoft.com/userFlowName/v2.0";
    })
    .AddJwtBearer("AzureB2cEmployeeSignUpAndSignIn", options => 
    {
        options.Audience = "clientIdInB2c";
        options.Authority = "https://tentantName.b2clogin.com/tenantName.onmicrosoft.com/userFlowName/v2.0";
    });

services
    .AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Azure", "AzureB2cCustomerSignIn", "AzureB2cCustomerSignUp", "AzureB2cEmployeeSignUpAndSignIn")
            .Build();
    }

当我尝试使用来自 B2C 流程的令牌进行身份验证时,我收到以下单个错误。应用程序可以继续,API 方法被执行,一切正常。但这会严重混淆应用程序洞察和错误。更重要的是,很可能还会影响性能。我一定缺少一些配置...

Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match key: 
kid: 'System.String'.
Exceptions caught:
 'System.Text.StringBuilder'. 
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: Azure was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key: 
kid: 'System.String'.
Exceptions caught:
 'System.Text.StringBuilder'. 
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.

它指出无法根据我为 Azure Active Directory 配置的身份验证方案验证令牌,这是显而易见的。由于它是来自 B2C 身份验证流程的令牌,我什至不希望它尝试使用此身份验证方案进行身份验证。从逻辑上讲,当我使用来自 AAD 流的令牌进行身份验证时,我会得到三次 SecurityTokenSignatureKeyNotFoundException

我怎样才能避免这个错误?

1 个答案:

答案 0 :(得分:0)

该问题的一个解决方案是添加一个自定义 JwtBearerHandler,它知道令牌的颁发者,并且仅在令牌颁发者与身份验证方案颁发者相同时才验证令牌。

自定义处理程序的代码如下。

public class CustomJwtHandler : JwtBearerHandler
{
    public CustomJwtHandler(
        IOptionsMonitor<JwtBearerOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authenticationSchemeConfiguration = await this.Options.ConfigurationManager.GetConfigurationAsync(this.Context.RequestAborted);
        var jwt = this.ReadTokenFromHeader();
        var jwtHandler = new JwtSecurityTokenHandler();

        if (jwtHandler.CanReadToken(jwt))
        {
            var token = jwtHandler.ReadJwtToken(jwt);
            if (string.Equals(token.Issuer, authenticationSchemeConfiguration.Issuer, StringComparison.OrdinalIgnoreCase))
            {
                return await base.HandleAuthenticateAsync();
            }
            else
            {
                return AuthenticateResult.NoResult();
            }
        }

        return await base.HandleAuthenticateAsync();
    }

    private string ReadTokenFromHeader()
    {
        var authorization = Request.Headers["Authorization"].ToString();

        if (string.IsNullOrEmpty(authorization))
        {
            return null;
        }

        if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
        {
            return authorization.Substring(7).Trim();
        }

        return null;
    }
}

自定义处理程序的接线如下。请注意必须添加的附加服务,如果没有这些服务,自定义处理程序中的 JwtBearerOptions 将为 null,从而在调用 GetConfigurationAsync 时导致 NullReferenceException。

services
    .AddAuthentication()
    .AddScheme<JwtBearerOptions, CustomJwtHandler>("Azure", options => Configuration.Bind("AzureJwtBearerOptions", options))
    .AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cCustomerSignIn", options => Configuration.Bind("AzureB2cCustomerSignInJwtBearerOptions", options))
    .AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cCustomerSignUp", options => Configuration.Bind("AzureB2cCustomerSignUpJwtBearerOptions", options))
    .AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cEmployeeSignUpAndSignIn", options => Configuration.Bind("AzureB2cEmployeeSignUpAndSignInJwtBearerOptions", options))
    .Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>()); // Without this JwtBearerOptions.ConfigurationManager is null.