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

Azure AD B2C中OpenIdConnectOptions不生效及id_token验证问询

问题背景

基于Microsoft.Identity.Web模板构建Azure AD B2C Web应用,通过B2C用户流和OpenIDConnect实现身份验证/授权,需要验证隐式流返回的id_token。当前配置的OpenIdConnectOptions未生效(事件断点不触发、ResponseMode修改无效果),注释配置后登录登出功能不受影响。

相关代码如下:

ConfigureServices 代码

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    // 添加B2C认证
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration, Constants.AzureAdB2C);

    services.AddOptions();

    // Cookie策略配置
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
        options.HandleSameSiteCookieCompatibility();
    });

    // 配置OpenIDConnect选项
    services.Configure<OpenIdConnectOptions>(AzureADB2CDefaults.OpenIdScheme, options => {
        Configuration.GetSection("AzureAdB2C");
        options.SaveTokens = true;
        options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
        options.ResponseMode = "fragment";
        options.TokenValidationParameters.ValidateAudience = true;
        options.TokenValidationParameters.ValidAudience = "my-app-id";
        options.Events = new OpenIdConnectEvents
        {
            OnTokenValidated = ctx => Task.CompletedTask,
            OnTokenResponseReceived = ctx => Task.CompletedTask,
            OnAccessDenied = ctx => Task.CompletedTask,
            OnMessageReceived = ctx => Task.CompletedTask,
            OnAuthorizationCodeReceived = ctx => Task.CompletedTask,
            OnAuthenticationFailed = ctx => Task.CompletedTask,
            OnRedirectToIdentityProvider = ctx => Task.CompletedTask,
        };
        options.Validate();
    });
}

Configure 代码

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapRazorPages();
    });
}

AccountController 核心代码

[AllowAnonymous]
[Route("[controller]/[action]")]
public class AccountController : Controller
{
    private readonly IOptionsMonitor<MicrosoftIdentityOptions> _optionsMonitor;

    public AccountController(IOptionsMonitor<MicrosoftIdentityOptions> microsoftIdentityOptionsMonitor)
    {
        _optionsMonitor = microsoftIdentityOptionsMonitor;
    }

    [HttpGet("{scheme?}")]
    public IActionResult SignIn(
        [FromRoute] string scheme,
        [FromQuery] string redirectUri)
    {
        scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
        string redirect = !string.IsNullOrEmpty(redirectUri) && Url.IsLocalUrl(redirectUri) 
            ? redirectUri 
            : Url.Content("~/")!;

        return Challenge(
            new AuthenticationProperties { RedirectUri = redirect },
            scheme);
    }

    // 其他SignOut、ResetPassword等方法略
}
核心问题
  1. 如何在代码中接收id_token?
  2. 如何使用OpenIdConnectOptions或相关库验证令牌及其声明?
  3. 该验证步骤是否必要,还是已由OpenIDConnect/B2C处理?
解决方案

1. 接收id_token的方法

先修复配置不生效的核心问题:你的AccountController中默认使用OpenIdConnectDefaults.AuthenticationScheme,但AddMicrosoftIdentityWebAppAuthentication为B2C注册的是AzureADB2CDefaults.OpenIdScheme,导致配置的OpenIdConnectOptions从未被应用。将Controller中的默认scheme改为AzureADB2CDefaults.OpenIdScheme即可触发配置的事件和选项。

接收id_token的两种常用方式:

  • 通过HttpContext获取:确保options.SaveTokens = true(已在配置中设置),登录后的请求中可通过以下代码获取:
    string idToken = await HttpContext.GetTokenAsync(AzureADB2CDefaults.OpenIdScheme, "id_token");
    
  • 在OpenIDConnect事件中获取:在OnTokenValidated或OnTokenResponseReceived事件中,直接从上下文获取验证后的令牌:
    OnTokenValidated = ctx =>
    {
        // 获取验证后的JWT令牌
        var jwtToken = ctx.SecurityToken as JwtSecurityToken;
        string idToken = jwtToken?.RawData;
        // 直接访问令牌声明
        var userId = ctx.Principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return Task.CompletedTask;
    }
    

2. 验证令牌及其声明

自动验证(默认已处理)

OpenID Connect中间件会自动完成id_token的标准验证,包括:

  • 验证令牌签名(使用B2C元数据端点获取的公钥)
  • 验证令牌有效期(exp声明)
  • 验证受众(aud声明,需匹配应用ID)
  • 验证签发者(iss声明,需匹配B2C租户的签发者URL)

自定义验证

如果需要添加业务相关的自定义验证,可通过以下方式实现:

  • 通过TokenValidationParameters配置:在OpenIdConnectOptions中修改参数,添加额外验证规则:
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateAudience = true,
        ValidAudience = "my-app-id",
        ValidateIssuer = true,
        ValidIssuer = "https://your-b2c-tenant.b2clogin.com/your-tenant-id/v2.0/",
        NameClaimType = "name",
        RoleClaimType = "role",
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true
    };
    
  • 在事件中添加自定义验证:在OnTokenValidated事件中添加业务规则验证,不通过则终止认证流程:
    OnTokenValidated = ctx =>
    {
        var email = ctx.Principal.FindFirst("emails")?.Value;
        if (string.IsNullOrEmpty(email))
        {
            ctx.Fail("Email claim is missing");
        }
        if (!email.EndsWith("@yourdomain.com"))
        {
            ctx.Fail("Invalid email domain");
        }
        return Task.CompletedTask;
    }
    
  • 手动验证令牌:如果需要手动验证原始id_token字符串,可使用System.IdentityModel.Tokens.Jwt库:
    using System.IdentityModel.Tokens.Jwt;
    using Microsoft.IdentityModel.Tokens;
    
    public async Task<bool> ValidateIdToken(string idToken)
    {
        var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            "https://your-b2c-tenant.b2clogin.com/your-tenant-id/policy-name/v2.0/.well-known/openid-configuration",
            new OpenIdConnectConfigurationRetriever());
        var config = await configManager.GetConfigurationAsync();
    
        var validationParameters = new TokenValidationParameters
        {
            ValidAudience = "my-app-id",
            ValidIssuer = config.Issuer,
            IssuerSigningKeys = config.SigningKeys
        };
    
        var handler = new JwtSecurityTokenHandler();
        try
        {
            handler.ValidateToken(idToken, validationParameters, out _);
            return true;
        }
        catch
        {
            return false;
        }
    }
    

3. 验证步骤是否必要?

  • 标准验证无需手动处理:OpenID Connect中间件和Microsoft.Identity.Web已经自动完成了id_token的标准安全验证,这些是身份认证流程的核心部分,必须确保生效。
  • 自定义验证按需添加:如果业务有额外规则(比如特定声明必须存在、用户属于特定角色/租户等),则需要添加自定义验证逻辑,否则无需手动重复执行标准验证。

内容的提问来源于stack exchange,提问作者Zachary Allard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:55:21