在 ASP.NET Core 中添加 OpenIdConnect 和刷新令牌 [英] AddOpenIdConnect and Refresh Tokens in ASP.NET Core

查看:37
本文介绍了在 ASP.NET Core 中添加 OpenIdConnect 和刷新令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已将 AddOpenIdConnect 添加到我的 ASP.NET Core 3.1 Razor 应用程序的 ConfigureServices 方法.它运行良好,直到令牌过期,然后我从 IDP 收到 401 响应.

I have added AddOpenIdConnect to the ConfigureServices method of my ASP.NET Core 3.1 Razor application. It works great until the token expires, then I get 401 responses from my IDP.

我看过一个示例 显示了一种手动连接刷新令牌的方法.

I have seen an example that shows a way to wire up refresh tokens manually.

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

But I am hesitant to do that. It seems super unlikely that the folks at Microsoft did not think about refresh tokens.

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

推荐答案

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

Here is what I came up with. Since there are not very many examples that I could find on how to do refresh tokens in ASP.NET Core with cookies, I thought I would post this here. (The one I link to in the question has issues.)

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

This is just my attempt at getting this working. It has not been used in any production setting. This code goes in the ConfigureServices method.

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;
});

这段代码有两部分:

  1. AddOpenIdConnect:这部分代码为应用程序设置 OIDC.这里的关键设置是:
    • SignInScheme:这让 ASP.NET Core 知道您想使用 cookie 来存储您的身份验证信息.
    • *UseTokenLifetime:据我所知,这会设置一个内部的expires_at";cookie 中的值是访问令牌的生命周期.(不是实际的 cookie 过期时间,它停留在会话级别.)
    • *SaveTokens:据我所知,这就是导致令牌保存在 cookie 中的原因.
  1. AddOpenIdConnect: This part of the code sets up OIDC for the application. Key settings here are:
    • SignInScheme: This lets ASP.NET Core know you want to use cookies to store your authentication information.
    • *UseTokenLifetime: As I understand it, this sets an internal "expires_at" value in the cookie to be the lifespan of the access token. (Not the actual cookie expiration, which stays at the session level.)
    • *SaveTokens: As I understand it, this is what causes the tokens to be saved in the cookie.

代码使用这些必须来自您的配置文件的值:

The code uses these values that must come from your configuration file:

  • 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 毫秒,那么它可能会过期,并且在您有机会刷新它之前调用失败.)此设置是您要考虑的访问令牌准备好刷新的过期前的分钟数.
  • clientId: OAuth2 Client ID. Also called Client Key, Consumer Key, etc.
  • clientSecret: OAuth2 Client Secret. Also called Consumer Secret, etc.
  • oidcDiscoveryUrl: Base part of the URL to your IDP's Well Known Configuration document. If your Well Known Configuration document is at https://youridp.domain.com/oauth2/oidcdiscovery/.well-known/openid-configuration then this value would be https://youridp.domain.com/oauth2/oidcdiscovery.
  • tokenUrl: Url to your IDP's token endpoint. For example: https:/youridp.domain.com/oauth2/token
  • refreshThresholdMinutes: If you wait till the access token is very close to expiring, then you run the risk of failing calls that rely on the access token. (If it is 5 miliseconds from expiration then it could expire, and fail a call, before you get a chance to refresh it.) This setting is the number of minutes before expiration you want to consider an access token ready to be refreshed.

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

* I am new to ASP.NET Core. As such I am not 100% sure that those settings do what I think. This is just a bit of code that is working for me and I thought I would share it. It may or may not work for you.

这篇关于在 ASP.NET Core 中添加 OpenIdConnect 和刷新令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆