在 ASP.NET Core 中添加 OpenIdConnect 和刷新令牌

Vac*_*ano 11 oauth-2.0 openid-connect asp.net-core refresh-token asp.net-core-3.1

我已经添加AddOpenIdConnectConfigureServices我的 ASP.NET Core 3.1 Razor 应用程序的方法中。在令牌过期之前,它运行良好,然后我从 IDP 收到 401 响应。

我看过一个例子,展示了一种手动连接刷新令牌的方法。

但我很犹豫要不要这样做。微软的人似乎不太可能没有考虑刷新令牌。

ASP.NET Core 3.1 是否有办法让刷新令牌自动更新访问令牌?

Vac*_*ano 18

这是我想出的。由于我找不到很多关于如何在 ASP.NET Core 中使用 cookie 刷新令牌的示例,我想我会在这里发布。(我在问题中链接到的那个有问题。)

这只是我尝试让这个工作。它尚未用于任何生产环境。此代码在ConfigureServices方法中。

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;
});
Run Code Online (Sandbox Code Playgroud)

这段代码有两部分:

  1. AddOpenIdConnect:这部分代码为应用程序设置OIDC。这里的关键设置是:
    • SignInScheme:这让 ASP.NET Core 知道您想使用 cookie 来存储您的身份验证信息。
    • * UseTokenLifetime:据我所知,这将 cookie 中的内部“expires_at”值设置为访问令牌的生命周期。(不是实际的 cookie 过期,它停留在会话级别。)
    • * SaveTokens:据我所知,这就是导致令牌保存在 cookie 中的原因。
  2. 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 毫秒,那么在您有机会刷新它之前,它可能会过期并导致调用失败。)此设置是您希望将访问令牌视为准备好刷新的过期前的分钟数。

* 我是 ASP.NET Core 的新手。因此,我不能 100% 确定这些设置是否符合我的想法。这只是一些对我有用的代码,我想我会分享它。它可能适合您,也可能不适合您。

  • 有人请给这个人一枚奖章 (2认同)