在ASP.NET Core中将Azure AD ID令牌兑换为自签名访问令牌
你当前通过Azure AD完成身份验证并获取到ID令牌及用户信息,但在OnUserInformationReceived事件中生成自定义令牌的时机并不合适。以下是正确的实现方案:
最佳时机:使用OnTokenValidated事件
OnTokenValidated是OpenIdConnect认证流程中ID令牌验证通过后触发的事件,此时用户身份已被确认,是生成并存储自定义令牌的最优节点。
实现步骤与代码示例
修改OpenIdConnect的事件配置,替换原有的OnUserInformationReceived逻辑:
authenticationBuilder.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { // 保留原有配置 options.Authority = $"https://login.microsoftonline.com/{oidcTenantId}/v2.0/"; options.ClientId = oidcClientId; options.ClientSecret = oidcClientSecret; options.ResponseType = OpenIdConnectResponseType.CodeIdToken; options.CallbackPath = "/signin-oidc"; options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcInstance).Validate; options.GetClaimsFromUserInfoEndpoint = true; // 替换为OnTokenValidated事件处理逻辑 options.Events.OnTokenValidated = async context => { // 从已验证的身份主体中提取preferred_username声明 var userIdClaim = context.Principal.Claims.FirstOrDefault(c => c.Type == "preferred_username"); if (userIdClaim == null) { throw new InvalidOperationException("Azure AD返回的ID令牌中缺少必需的preferred_username声明"); } // 生成自定义自签名访问令牌(将preferred_username映射为sub声明) int lifetimeInMinutes = 5; string jwt = MyTokenRules.GenerateJwt( userId: userIdClaim.Value, // 此处将preferred_username映射为访问令牌的sub声明 issuer: jwtSettings.Issuer, audience: jwtSettings.Audience, key: Environment.GetEnvironmentVariable(jwtSettings.SecretKeyName) ?? string.Empty, duration: TimeSpan.FromMinutes(lifetimeInMinutes)); // 方式1:将自定义令牌作为Claim存入身份Cookie,随用户身份一同存储 var identity = context.Principal.Identity as ClaimsIdentity; identity?.AddClaim(new Claim("custom_access_token", jwt)); // 方式2:用独立Cookie存储自定义令牌(适合需要单独管控令牌生命周期的场景) context.Response.Cookies.Append( "CustomAccessToken", jwt, new CookieOptions { HttpOnly = true, // 防范XSS攻击 Secure = true, // 仅HTTPS环境传输 SameSite = SameSiteMode.Lax, Expires = DateTimeOffset.UtcNow.AddMinutes(lifetimeInMinutes) }); await Task.CompletedTask; }; });
为什么不推荐OnUserInformationReceived?
该事件触发于用户信息端点返回数据后,但此时认证流程尚未完成,身份主体(Principal)还未被Cookie认证中间件序列化存储。在此处生成的令牌无法确保被正确持久化到Cookie中,时序上存在风险。
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

