【发布时间】:2019-02-10 00:27:28
【问题描述】:
解决此问题的代码见下文
我正在尝试寻找最佳和最有效的方法来处理在 ASP.NET Core 2.1 中已过期的刷新令牌。
让我再解释一下。
我正在使用 OAUTH2 和 OIDC 请求授权码授权流(或带有 OIDC 的混合流)。这种流/授权类型使我可以访问 AccessToken 和 RefreshToken(授权码也是如此,但这不适用于这个问题)。
访问令牌和刷新令牌由 ASP.NET Core 存储,可以分别使用HttpContext.GetTokenAsync("access_token"); 和HttpContext.GetTokenAsync("refresh_token"); 检索。
我可以毫无问题地刷新access_token。当refresh_token 过期、撤销或以某种方式无效时,问题就会出现。
正确的流程是让用户登录并再次返回整个身份验证流程。然后应用程序会返回一组新的令牌。
我的问题是如何以最好和最正确的方法实现这一目标。我决定编写一个自定义中间件,如果 access_token 已过期,它会尝试更新它。然后中间件将新令牌设置到 HttpContext 的 AuthenticationProperties 中,以便以后的任何调用都可以使用它。
如果由于某种原因刷新令牌失败,我需要再次调用 ChallengeAsync。我正在从中间件调用 ChallengeAsync。
这是我遇到一些有趣行为的地方。但是,大多数情况下,这可行,但有时我会收到 500 个错误,而没有关于失败原因的有用信息。似乎中间件在尝试从中间件调用 ChallengeAsync 时遇到问题,并且可能另一个中间件也在尝试访问上下文。
我不太确定发生了什么。我不太确定这是否是放置此逻辑的正确位置。也许我不应该在中间件中使用它,也许在其他地方。也许 HttpClient 的 Polly 是最好的地方。
我愿意接受任何想法。
感谢您提供的任何帮助。
适合我的代码解决方案
感谢Mickaël Derriey 的帮助和指导(请务必查看他的答案以了解有关此解决方案的更多信息)。这是我想出的解决方案,它对我有用:
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = context =>
{
//check to see if user is authenticated first
if (context.Principal.Identity.IsAuthenticated)
{
//get the user's tokens
var tokens = context.Properties.GetTokens();
var refreshToken = tokens.FirstOrDefault(t => t.Name == "refresh_token");
var accessToken = tokens.FirstOrDefault(t => t.Name == "access_token");
var exp = tokens.FirstOrDefault(t => t.Name == "expires_at");
var expires = DateTime.Parse(exp.Value);
//check to see if the token has expired
if (expires < DateTime.Now)
{
//token is expired, let's attempt to renew
var tokenEndpoint = "https://token.endpoint.server";
var tokenClient = new TokenClient(tokenEndpoint, clientId, clientSecret);
var tokenResponse = tokenClient.RequestRefreshTokenAsync(refreshToken.Value).Result;
//check for error while renewing - any error will trigger a new login.
if (tokenResponse.IsError)
{
//reject Principal
context.RejectPrincipal();
return Task.CompletedTask;
}
//set new token values
refreshToken.Value = tokenResponse.RefreshToken;
accessToken.Value = tokenResponse.AccessToken;
//set new expiration date
var newExpires = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
exp.Value = newExpires.ToString("o", CultureInfo.InvariantCulture);
//set tokens in auth properties
context.Properties.StoreTokens(tokens);
//trigger context to renew cookie with new token values
context.ShouldRenew = true;
return Task.CompletedTask;
}
}
return Task.CompletedTask;
}
};
【问题讨论】:
-
我一直在关注您的代码,因为我有类似的要求,我希望 ID 令牌能够访问我的 api。但是当我尝试使用上面的代码创建新的 id 令牌时,它会创建令牌。但是当我使用 id_token 授权我的 API 时,它返回“未授权”。有什么想法吗?
-
Id_tokens 不会转到您的 API。 access_tokens 转到您的 API。我建议您先测试并让您的 API 与 access_tokens 一起工作,然后再让刷新令牌工作。刷新令牌只会发出一个新的 access_token
标签: asp.net-core oauth asp.net-core-2.1 refresh-token openid-connect