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等方法略 }
核心问题
- 如何在代码中接收id_token?
- 如何使用OpenIdConnectOptions或相关库验证令牌及其声明?
- 该验证步骤是否必要,还是已由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
相关产品推荐
相关产品推荐

