ASP.NET Core中如何复用OIDC认证处理器配置与逻辑刷新令牌?
关于OIDC令牌刷新后是否需重新执行认证流程的问题
背景配置
当前使用的ASP.NET Core认证配置如下,框架会自动处理令牌验证、发现端点读取、公钥获取、声明映射等操作:
builder.Services .AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { // ... }) .AddOpenIdConnect(options => { // OIDC configurations });
自定义令牌刷新中间件
为实现令牌即将过期时自动刷新,编写了如下中间件,但获取新令牌后不确定是否需要复用OIDC登录时的处理逻辑:
app.Use(async (ctx, next) => { if (ctx.User.Identity!.IsAuthenticated) { var epoch = DateTimeOffset.MinValue.Ticks; long.TryParse(ctx.User.FindFirstValue(JwtRegisteredClaimNames.Exp), out epoch); var expireTime = DateTimeOffset.FromUnixTimeSeconds(epoch); if (expireTime < DateTimeOffset.UtcNow.AddMinutes(2)) { var tokenManagementService = ctx.RequestServices.GetRequiredService<TokenManagementService>(); var refreshResult = await tokenManagementService.RefreshTokenAsync(ctx); if (refreshResult is RefreshTokenFailure { LogoutNeeded: true }) { await ctx.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); ctx.User = new ClaimsPrincipal(new ClaimsIdentity()); } else if (refreshResult is RefreshTokenSuccess newTokens) { // I've got the new tokens // Everything that the OIDC authentication handler did // needs to be done again (right?) } } } await next(ctx); });
问题解答
是否需要重新执行OIDC认证处理器的所有操作?
是,你需要重新执行核心的验证和身份映射操作,不需要重复执行读取发现端点、获取公钥这类一次性操作,但必须对新获取的id_token做合法性验证,执行声明映射,最终更新用户的ClaimsPrincipal和认证Cookie。
原因很直接:新令牌可能包含更新后的用户声明、权限信息,只有经过和登录时一致的验证逻辑,才能确保身份合法;同时必须把新的声明映射到用户身份对象中,让后续请求能使用最新的身份信息。
具体实现方式
推荐复用OIDC认证处理器的现有逻辑,避免重复造轮子,具体代码示例如下:
else if (refreshResult is RefreshTokenSuccess newTokens) { // 获取OpenIdConnect认证处理器实例 var handlerProvider = ctx.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); var oidcHandler = await handlerProvider.GetHandlerAsync(ctx, OpenIdConnectDefaults.AuthenticationScheme) as OpenIdConnectHandler; if (oidcHandler == null) { // 处理处理器获取失败的情况,比如强制登出 await ctx.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); ctx.User = new ClaimsPrincipal(new ClaimsIdentity()); return; } // 1. 验证新id_token的合法性(复用OIDC配置的验证参数) var validationParams = oidcHandler.Options.TokenValidationParameters.Clone(); var validationResult = await new JwtSecurityTokenHandler().ValidateTokenAsync( newTokens.IdToken, validationParams ); // 2. 执行声明映射(复用OIDC配置的ClaimActions) var claimsIdentity = new ClaimsIdentity( validationResult.Claims, OpenIdConnectDefaults.AuthenticationScheme ); oidcHandler.Options.ClaimActions.Run(validationResult.Claims, claimsIdentity); // 3. 创建新的ClaimsPrincipal var newPrincipal = new ClaimsPrincipal(claimsIdentity); // 4. 更新认证Cookie,持久化新的身份信息 var authProps = new AuthenticationProperties { IsPersistent = ctx.User.Identity.IsAuthenticated, ExpiresUtc = DateTimeOffset.UtcNow.Add(oidcHandler.Options.RemoteAuthenticationOptions.CookieAuthenticationOptions.ExpireTimeSpan) }; // 把新的access_token和refresh_token存入认证属性,方便后续使用 authProps.Items["access_token"] = newTokens.AccessToken; authProps.Items["refresh_token"] = newTokens.RefreshToken; await ctx.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, newPrincipal, authProps); // 更新当前请求的User对象,让后续中间件能直接使用新身份 ctx.User = newPrincipal; }
关键注意事项
- 必须验证令牌合法性:绝对不能直接信任刷新接口返回的令牌,必须验证签名、有效期、受众、颁发者等核心字段,避免伪造令牌的安全风险
- 同步更新令牌存储:确保新的access_token和refresh_token被存储到正确的位置(比如Cookie、分布式缓存),保证后续请求能获取到有效令牌
- 复用原有配置:所有验证规则、声明映射逻辑都要复用
AddOpenIdConnect中配置的内容,避免出现和登录流程不一致的情况
内容的提问来源于stack exchange,提问作者Parsa99
相关产品推荐
相关产品推荐

