IdentityModel 实施:如何在到期日或之后验证(和刷新)access_token?
Posted
技术标签:
【中文标题】IdentityModel 实施:如何在到期日或之后验证(和刷新)access_token?【英文标题】:IdentityModel implementation: How to verify (and refresh) access_token on or after expiry date? 【发布时间】:2021-12-13 20:02:00 【问题描述】:背景故事:我正在尝试将使用 .NET 5 (MVC) 的新客户端应用程序与现有 IdentityServer4 链接起来。 IdentityServer4(简称 IS4)既用于对客户端进行身份验证,也用于提供声明和角色以及 API(单独的 web 应用程序)依赖于在后端系统上请求数据的 access_token。在新客户端上,我使用 IdentityModel 包来处理身份验证和授权。到目前为止,我已经管理了身份验证和授权工作,但我遇到了关于 access_token 到期日期的问题。
我目前已将 IS4 中的客户端配置为具有以下功能。如果该行显示“(默认)”,则表示它是 IdentityModel/IS4 建议或设置的默认值:
IdentityTokenLifetime:300s / 5m(默认) IdentityAccessToken:300s / 5m(缩短以便我测试) AuthorizationCodeLifetime:300s / 5m(默认)流程 第一个例子:
-
用户浏览网页,被重定向到 IS4 登录。
用户填写用户名/密码并成功通过身份验证,并被重定向回网络应用的安全部分。
当用户点击安全网页时,会使用用户的 access_token 向外部 api 发出 api 请求以获取用户数据。
请求返回用户数据,网页返回该数据。
完美运行。
第二个例子:
-
用户浏览网页,被重定向到 IS4 登录。
用户已使用 cookie 通过 IS4 进行身份验证,因此成功通过身份验证,并被重定向回 Web 应用的安全部分。
当用户点击安全网页时,会使用用户的 access_token 向外部 api 发出 api 请求以获取用户数据。
请求返回用户数据,网页返回该数据。
完美运行。
第三个例子:
-
用户在网页上等待 15 分钟,然后刷新页面。
用户已登录网站,因此不会重定向到 IS4。
由于用户刷新,用户点击安全网页,使用用户的 access_token 向外部 api 发出 api 请求以获取用户数据。
请求返回空,因为 access_token 已过期(10 分钟前)
悲伤的笑脸:'(
第四个例子:
-
以下示例三:用户看到错误并重新启动浏览器。
用户浏览网页,被重定向到 IS4 登录。
用户已使用 cookie 通过 IS4 进行身份验证,因此成功通过身份验证,并被重定向回 Web 应用的安全部分。
当用户点击安全网页时,会使用用户的 access_token 向外部 api 发出 api 请求以获取用户数据。
请求返回用户数据,网页返回该数据。 (因为 access_token 是新生成的,由于新的 IS4“登录”而具有未来的到期日期)
完美运行。
示例三是我遇到的问题。我期望发生的是 [Authorization]-check 不允许过期会话(access_tokens)通过,而是将用户重定向到 IS4 以根据用户仍然拥有的有效 cookie 自动重新验证(例如四)。
我试图解决的问题:
延长 IdentityAccessToken 生命周期:不能解决问题,而只是将问题移至新的 expire_date。 在我们现有的 IS4 实现中使用 IdentityModel 客户端“Web5”示例,显示相同的行为。--
应用程序的要求是具有较短的 access_token 生命周期,以允许根据后端更改的声明/角色快速更新用户的权限和访问权限,同时允许“持久”登录以减少用户的时间”花费必须填写他们的帐户详细信息。
完全有可能不是技术问题,而是我的思维过程或对这些事情的理解是错误的。如果是这样,请告诉我流程应该是什么,最好是一个工作示例。
--
客户端中的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();
【问题讨论】:
【参考方案1】:对于此流程,使用后端应用程序的解决方案是使用刷新令牌,可以通过请求 offline_access
范围并确保客户端配置为允许它们来获得。
刷新令牌与访问令牌一起返回,并且可用于在初始令牌过期后获取新的访问令牌(通过反向通道令牌端点调用)。这可以在第一次失败时完成(即来自 API 的 401 响应)或基于访问令牌的到期时间(通过使用 expires_in
令牌端点响应值或访问令牌本身中的 exp
声明.
查看:https://identityserver4.readthedocs.io/en/latest/topics/refresh_tokens.html
还有样本:https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Clients/src/MvcAutomaticTokenManagement
【讨论】:
感谢您的链接和评论。在我的应用程序中,我已经在使用“offline_access”范围。至于例子,使用底部链接,唯一的区别是cookie过期时间;其余配置看起来相同。然而,我觉得我错过了一些东西。不就是“services.AddAccessTokenManagement();”的服务吗?到期时应该自动刷新令牌吗?如果是这样,根据我最初帖子中的配置,这可能是什么原因? (我将编辑我的初始帖子以添加我正在使用的范围) 好问题。当您需要令牌时,您实际上是如何检索令牌的? “HttpContext.GetTokenAsync("access_token")”或“HttpContext.GetUserAccessTokenAsync();”。在第一种情况下,令牌从初始 IS4 登录中填充,但在会话期间似乎保持不变。第二种方法只能刷新一次令牌并返回新令牌(如果需要),但也不更新 HttpContext 中的令牌,因此我只能在该函数调用期间使用该令牌。我希望为整个 HttpContext 更新令牌,这意味着可以在 [Authorize(Roles = "XX")] 检查中使用更新/更改的角色 如果是我,我希望设置一个场景来证明自动更新正在被触发,或者通过监视日志记录或实现一个钩子(覆盖的类、事件处理程序等)我可以设置断点或登录。您确定使用示例中的 HttpClient 管道吗?如果您深入研究代码,所有这些都挂在为 HttpClient 绑定注册的处理程序上:github.com/IdentityModel/IdentityModel.AspNetCore/blob/… 示例中使用的精确版本:github.com/IdentityModel/IdentityModel.AspNetCore/blob/… - 您将看到它在第 58 行的方法中注册客户端并附加处理程序以上是关于IdentityModel 实施:如何在到期日或之后验证(和刷新)access_token?的主要内容,如果未能解决你的问题,请参考以下文章