IdentityModel实现:.NET5 MVC对接IS4如何验证刷新过期access_token
.NET 5 MVC对接IdentityServer4 access_token过期无法自动重定向认证问题
背景
我正尝试将.NET 5(MVC)开发的新客户端应用对接现有IdentityServer4(简称IS4)。IS4同时承担客户端身份认证、提供claims/角色,以及独立Web API访问后端系统所需的access_token的功能。新客户端使用IdentityModel包处理认证授权,目前认证、授权功能均正常,仅遇到access_token过期相关的问题。
IS4客户端配置参数
标注(default)的为IdentityModel/IS4默认设置:
- IdentityTokenLifetime:300秒/5分钟 (default)
- IdentityAccessToken:300秒/5分钟(为便于测试做了缩短)
- AuthorizationCodeLifetime:300秒/5分钟 (default)
运行场景
场景一(正常)
- 用户访问网页,被重定向到IS4登录页
- 用户输入账号密码完成认证,被重定向回Web应用的安全区域
- 用户访问安全页面时,携带当前用户的access_token请求外部API拉取用户数据
- 请求返回用户数据,页面正常加载展示
- 运行完全正常
场景二(正常)
- 用户访问网页,被重定向到IS4登录页
- 用户已通过Cookie完成IS4认证,直接认证通过,被重定向回Web应用的安全区域
- 用户访问安全页面时,携带当前用户的access_token请求外部API拉取用户数据
- 请求返回用户数据,页面正常加载展示
- 运行完全正常
场景三(问题场景)
- 用户在网页停留15分钟后刷新页面
- 用户仍处于网站登录状态,因此没有被重定向到IS4
- 刷新后用户访问安全页面,携带当前用户的access_token请求外部API拉取用户数据
- 请求返回为空,因为access_token早在10分钟前就已过期
- 运行失败
场景四(正常)
- 场景三发生后,用户看到错误重启浏览器
- 用户访问网页,被重定向到IS4登录页
- 用户已通过Cookie完成IS4认证,直接认证通过,被重定向回Web应用的安全区域
- 用户访问安全页面时,携带当前用户的access_token请求外部API拉取用户数据
- 请求返回用户数据,页面正常加载展示(原因是本次IS4"登录"生成了新的有效期内的access_token)
- 运行完全正常
问题描述与期望效果
问题为场景三的异常情况。期望效果是:[Authorization]校验不会放行过期会话(过期access_token),而是基于用户仍然有效的Cookie自动重定向到IS4重新认证,与场景四逻辑一致。
已尝试的解决方案
- 延长IdentityAccessToken有效期:没有解决根本问题,只是将问题发生的时间延后到新的expire_date
- 在现有IS4实现中使用IdentityModel客户端"Web5"示例:复现了相同的问题
业务约束
应用要求access_token有效期较短,以便后端claims/roles变更时可以快速更新用户权限,同时需要支持"持久化"登录,减少用户输入账号信息的频率。
如果存在思路或者对相关机制的理解错误,可告知正确流程,最好附带可运行的示例。
现有客户端IdentityModel配置
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services .AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(options => { options.Events.OnSigningOut = async e => { // revoke refresh token on sign-out await e.HttpContext.RevokeUserRefreshTokenAsync(); }; }) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.GetClaimsFromUserInfoEndpoint = true; options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = Configuration.GetValue<string>("IdentityServer:Authority"); options.ClientId = Configuration.GetValue<string>("IdentityServer:ClientId"); options.ClientSecret = Configuration.GetValue<string>("IdentityServer:ClientSecret"); options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer:RequireHttpsMetadata"); options.UsePkce = true; options.ResponseType = OidcConstants.ResponseTypes.CodeIdToken; options.SaveTokens = true; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = JwtClaimTypes.Name, RoleClaimType = JwtClaimTypes.Role }; // Scopes options.Scope.Add("openid"); options.Scope.Add("offline_access"); }) .AddOpenIdConnect("persistent", options => { options.CallbackPath = "/signin-persistent"; options.Events = new OpenIdConnectEvents { OnRedirectToIdentityProvider = context => { context.ProtocolMessage.Prompt = OidcConstants.PromptModes.None; return Task.FromResult<object>(null); }, OnMessageReceived = context => { if (string.Equals(context.ProtocolMessage.Error, "login_required", StringComparison.Ordinal)) { context.HandleResponse(); context.Response.Redirect("/"); } return Task.FromResult<object>(null); } }; ... // Rest of 'persistent' is similar as the non-persistent one ... }); // Examples of IdentityModel suggest that calling this function make the boilerplate tasks of refreshing tokens and alike automatically work services.AddAccessTokenManagement();
内容的提问来源于stack exchange,提问作者Jordy
相关产品推荐
相关产品推荐

