services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Events = new CookieAuthenticationEvents
{
// After the auth cookie has been validated, this event is called.
// In it we see if the access token is close to expiring. If it is
// then we use the refresh token to get a new access token and save them.
// If the refresh token does not work for some reason then we redirect to
// the login screen.
OnValidatePrincipal = async cookieCtx =>
{
var now = DateTimeOffset.UtcNow;
var expiresAt = cookieCtx.Properties.GetTokenValue("expires_at");
var accessTokenExpiration = DateTimeOffset.Parse(expiresAt);
var timeRemaining = accessTokenExpiration.Subtract(now);
// TODO: Get this from configuration with a fall back value.
var refreshThresholdMinutes = 5;
var refreshThreshold = TimeSpan.FromMinutes(refreshThresholdMinutes);
if (timeRemaining < refreshThreshold)
{
var refreshToken = cookieCtx.Properties.GetTokenValue("refresh_token");
// TODO: Get this HttpClient from a factory
var response = await new HttpClient().RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = tokenUrl,
ClientId = clientId,
ClientSecret = clientSecret,
RefreshToken = refreshToken
});
if (!response.IsError)
{
var expiresInSeconds = response.ExpiresIn;
var updatedExpiresAt = DateTimeOffset.UtcNow.AddSeconds(expiresInSeconds);
cookieCtx.Properties.UpdateTokenValue("expires_at", updatedExpiresAt.ToString());
cookieCtx.Properties.UpdateTokenValue("access_token", response.AccessToken);
cookieCtx.Properties.UpdateTokenValue("refresh_token", response.RefreshToken);
// Indicate to the cookie middleware that the cookie should be remade (since we have updated it)
cookieCtx.ShouldRenew = true;
}
else
{
cookieCtx.RejectPrincipal();
await cookieCtx.HttpContext.SignOutAsync();
}
}
}
};
})
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = oidcDiscoveryUrl;
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.RequireHttpsMetadata = true;
options.ResponseType = OidcConstants.ResponseTypes.Code;
options.UsePkce = true;
// This scope allows us to get roles in the service.
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("offline_access");
// This aligns the life of the cookie with the life of the token.
// Note this is not the actual expiration of the cookie as seen by the browser.
// It is an internal value stored in "expires_at".
options.UseTokenLifetime = false;
options.SaveTokens = true;
});
4条答案
按热度按时间o2gm4chl1#
这是我想到的。因为没有太多的例子,我可以找到如何做刷新令牌在ASP.NET核心与cookie,我想我会张贴在这里。(一个我链接到的问题有问题。)
这只是我尝试让它工作。它还没有在任何生产设置中使用。此代码位于
ConfigureServices
方法中。此代码包含两个部分:
AddOpenIdConnect
:这部分代码为应用程序设置OIDC。关键设置如下:SignInScheme
:这会让ASP.NET核心知道您要使用cookie来存储身份验证信息。UseTokenLifetime
:据我所知,这将cookie中的一个内部“expires_at”值设置为访问令牌的生命周期(不是实际的cookie过期时间,它停留在会话级别)。SaveTokens
:据我所知,这就是令牌保存在cookie中的原因。OnValidatePrincipal
:在验证Cookie时调用此部分。在此部分中,我们将检查访问令牌是否即将过期或过期。如果是,则将刷新令牌,并将更新的值存储在Cookie中。如果无法刷新令牌,则将用户重定向到登录屏幕。代码使用的这些值必须来自配置文件:
clientId
:OAuth2客户端ID。也称为客户端密钥、使用者密钥等。clientSecret
:OAuth2客户端机密。也称为使用者机密等。oidcDiscoveryUrl
:IDP的熟知配置文档的URL的基本部分。如果熟知配置文档位于https://youridp.domain.com/oauth2/oidcdiscovery/.well-known/openid-configuration
,则此值为https://youridp.domain.com/oauth2/oidcdiscovery
。tokenUrl
:IDP的令牌端点的URL。例如:https:/youridp.domain.com/oauth2/token
refreshThresholdMinutes
:如果一直等到访问令牌非常接近过期,则依赖该访问令牌的呼叫可能会失败。(如果距离过期还有5毫秒,则在您有机会刷新它之前,它可能会过期并导致呼叫失败。)此设置是您希望将访问令牌视为已准备好进行刷新的分钟数。n9vozmp42#
据我所知,ASP.NET Core 3.1没有内置自动刷新访问令牌的功能,但我从IdentityServer4作者那里找到了这个方便的library,它将访问令牌和刷新令牌存储在内存中(这可以被覆盖),并在您从库中请求时自动刷新访问令牌。
如何使用图书馆:https://identitymodel.readthedocs.io/en/latest/aspnetcore/web.html.
NuGet软件包:https://www.nuget.org/packages/IdentityModel.AspNetCore/.
源代码:https://github.com/IdentityModel/IdentityModel.AspNetCore.
relj7zay3#
我最近在一个.NET 7.0示例中实现了令牌刷新,在许多MS OIDC堆栈(包括旧堆栈)中,总是有一个刷新令牌和重写cookie的选项:Owin,.NET Core等。不过文档似乎很差,我不得不在aspnet源代码中挖掘,以找出cookie重写步骤。所以我想我应该添加到这个线程中,以防对未来的读者有用。
刷新令牌授予
首先发送基于标准的
refresh token grant
请求:重写Cookie
然后重写cookie,这是通过使用一组新的令牌“登录”来完成的。更好的方法名称可能是类似“更新身份验证状态”的名称。如果您查看HTTP响应,您将看到一个更新的
set-cookie
标头,其中包含新的令牌。请注意,在刷新令牌授予响应中,您可能会收到新的刷新令牌和新的ID令牌,也可能不会收到。如果没有收到,请提供现有值。
摘要
当你收到来自API的401响应时,能够刷新访问令牌是任何Web应用的基本功能。使用短期访问令牌,然后编写类似于上面的代码,以良好的可用性更新它们。
请注意,依赖到期时间并不完全可靠。在某些情况下,API令牌验证可能会由于基础架构事件而失败。API随后会为未到期的访问令牌返回401。Web应用应通过刷新处理此问题,然后重试API请求。
jucafojl4#
AddOpenIdConnect
用于配置执行OpenID Connect协议以从身份提供商获取令牌的处理程序。但它不知道您要将令牌保存在何处。它可能是以下任一项:您可以将令牌存储在cookie中,然后检查令牌的过期时间,并通过拦截cookie的验证事件来刷新令牌(如示例所示)。
但是
AddOpenIdConnect
没有逻辑来控制用户想要存储令牌的位置和自动实现令牌刷新。您还可以尝试将中间件 Package 为ADAL .NET/MSAL .NET以提供缓存特性,然后您可以静默地获取/刷新令牌。