You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure AD B2C:ASP.NET MVC应用内置与自定义用户流签名密钥适配问题

解决Azure AD B2C内置流与自定义流令牌验证兼容问题

我之前也碰到过这个问题,Azure AD B2C的内置用户流和自定义策略确实会使用不同的签名密钥——因为它们各自的OpenID元数据端点是独立的。只配置内置流的MetadataAddress的话,应用的令牌验证逻辑只会信任内置流的签名密钥,自然无法通过自定义流令牌的校验。下面给你两种可行的解决方案:

方案一:配置多认证方案分别处理两种流

这种方式最直接,给内置流和自定义流各自配置独立的OpenID Connect认证方案,让应用能识别并验证两种来源的令牌。

在Startup.cs的ConfigureServices方法里,添加两个AddOpenIdConnect配置:

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
// 内置流认证方案
.AddOpenIdConnect("BuiltInFlow", options =>
{
    options.ClientId = "你的客户端ID";
    options.MetadataAddress = "https://contoso.b2clogin.com/contoso.onmicrosoft.com/[built-in-flow]/v2.0/.well-known/openid-configuration";
    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    options.CallbackPath = "/signin-builtin";
    options.SignedOutCallbackPath = "/signout-callback-builtin";
    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name",
        RoleClaimType = "role"
    };
})
// 自定义流认证方案
.AddOpenIdConnect("CustomFlow", options =>
{
    options.ClientId = "你的客户端ID";
    options.MetadataAddress = "https://contoso.b2clogin.com/contoso.onmicrosoft.com/[custom-policy]/v2.0/.well-known/openid-configuration";
    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    options.CallbackPath = "/signin-custom";
    options.SignedOutCallbackPath = "/signout-callback-custom";
    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name",
        RoleClaimType = "role"
    };
});

之后在控制器或者视图里,你可以通过[Authorize(AuthenticationSchemes = "BuiltInFlow,CustomFlow")]来允许两种方案的令牌通过验证,或者根据不同的场景指定对应的认证方案。

方案二:合并签名密钥,让单个认证方案信任两种流的密钥

如果你希望用同一个认证方案处理两种令牌,可以手动加载两个流的签名密钥,合并到令牌验证参数中。

步骤如下:

  1. 编写一个方法,从指定的Metadata地址获取签名密钥
  2. 在配置认证方案时,将两个流的密钥合并到IssuerSigningKeys中

示例代码:

private async Task<IEnumerable<SecurityKey>> GetSigningKeysFromMetadata(string metadataUrl)
{
    var httpClient = new HttpClient();
    var metadataResponse = await httpClient.GetStringAsync(metadataUrl);
    var metadata = JsonDocument.Parse(metadataResponse).RootElement;
    var jwksUri = metadata.GetProperty("jwks_uri").GetString();
    var jwksResponse = await httpClient.GetStringAsync(jwksUri);
    var jwks = JsonWebKeySet.Create(jwksResponse);
    return jwks.GetSigningKeys();
}

// 在ConfigureServices中
services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("CombinedFlow", options =>
{
    options.ClientId = "你的客户端ID";
    // 可以任选一个流的MetadataAddress作为基础
    options.MetadataAddress = "https://contoso.b2clogin.com/contoso.onmicrosoft.com/[built-in-flow]/v2.0/.well-known/openid-configuration";
    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    options.CallbackPath = "/signin-oidc";

    // 手动加载并合并两个流的签名密钥
    var builtInKeys = await GetSigningKeysFromMetadata("https://contoso.b2clogin.com/contoso.onmicrosoft.com/[built-in-flow]/v2.0/.well-known/openid-configuration");
    var customKeys = await GetSigningKeysFromMetadata("https://contoso.b2clogin.com/contoso.onmicrosoft.com/[custom-policy]/v2.0/.well-known/openid-configuration");
    
    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name",
        RoleClaimType = "role",
        IssuerSigningKeys = builtInKeys.Concat(customKeys)
    };
});

额外说明

Azure AD B2C的内置流和自定义策略的签名密钥是由服务端管理的,无法强制让它们使用同一个密钥。所以核心思路就是让应用同时信任这两个流的签名密钥,上面两种方案都是围绕这个核心展开的。

内容的提问来源于stack exchange,提问作者Mark Prins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 21:17:37