处理ASP.NET Core中的过期刷新标记

问题描述 投票:8回答:1

请参阅下面的代码以解决此问题

我正在尝试找到处理ASP.NET Core 2.1中已过期的刷新令牌的最佳和最有效的方法。

让我解释一下。

我正在使用OAUTH2和OIDC来请求授权代码授权流程(或使用OIDC的混合流程)。此流/授权类型使我可以访问AccessToken和RefreshToken(授权代码,但这不适用于此问题)。

访问令牌和刷新令牌由ASP.NET核心存储,并且可以分别使用HttpContext.GetTokenAsync("access_token");HttpContext.GetTokenAsync("refresh_token");进行检索。

我可以毫无问题地刷新access_token。当refresh_token以某种方式过期,撤销或无效时,问题就会发挥作用。

正确的流程是让用户再次登录并再次返回整个身份验证流程。然后应用程序获得一组新的令牌返回。

我的问题是如何以最好和最正确的方法实现这一目标。我决定编写一个自定义中间件,试图更新access_token,如果它已经过期。然后,中间件将新令牌设置为HttpContext的AuthenticationProperties,以便以后管道中的任何调用都可以使用它。

如果由于任何原因刷新令牌失败,我需要再次调用ChallengeAsync。我从中间件调用ChallengeAsync。

这是我遇到一些有趣的行为的地方。然而,大部分时间这都有效,有时候我会得到500个错误而没有关于什么是失败的有用信息。几乎看起来中间件试图从中间件调用ChallengeAsync有问题,也许另一个中间件也试图访问上下文。

我不太清楚发生了什么事。我不太确定这是否是适合这种逻辑的地方。也许我不应该在中间件中使用它,也许在其他地方。也许Polly for HttpClient是最好的地方。

我愿意接受任何想法。

感谢您的任何帮助,您可以提供。

代码解决方案对我有用


感谢Mickaël Derriey的帮助和指导(请务必在此解决方案中查看他的答案以获取更多信息)这是我提出的解决方案,它适用于我:

options.Events = new CookieAuthenticationEvents
{
    OnValidatePrincipal = context =>
    {
        //check to see if user is authenticated first
        if (context.Principal.Identity.IsAuthenticated)
        {
            //get the users 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;
    }
};
asp.net-core oauth asp.net-core-2.1 refresh-token oidc
1个回答
9
投票

访问令牌和刷新令牌由ASP.NET核心存储

我认为值得注意的是,令牌存储在cookie中,用于标识应用程序的用户。

现在这是我的看法,但我不认为自定义中间件是刷新令牌的正确位置。这样做的原因是,如果您成功刷新令牌,则需要替换现有令牌并将其以新cookie的形式发送回浏览器,该cookie将替换现有令牌。

这就是为什么我认为最相关的地方是ASP.NET Core正在读取cookie。每个认证机制都会暴露几个事件;对于cookie,有一个名为ValidatePrincipal的cookie,在读取cookie并且已经成功地对其进行反序列化后,每个请求都会调用它。

public void ConfigureServices(ServiceCollection services)
{
    services
        .AddAuthentication()
        .AddCookies(new CookieAuthenticationOptions
        {
            Events = new CookieAuthenticationEvents
            {
                OnValidatePrincipal = context =>
                {
                    // context.Principal gives you access to the logged-in user
                    // context.Properties.GetTokens() gives you access to all the tokens

                    return Task.CompletedTask;
                }
            }
        });
}

这种方法的好处是,如果你设法更新令牌并将其存储在AuthenticationProperties中,context类型的CookieValidatePrincipalContext变量有一个名为ShouldRenew的属性。将该属性设置为true会指示中间件发出新cookie。

如果您无法续订令牌,或者您发现刷新令牌已过期并且您希望阻止用户继续前进,则该同一类具有RejectPrincipal方法,该方法指示cookie中间件将请求视为匿名。

关于这一点的好处是,如果您的MVC应用程序仅允许经过身份验证的用户访问它,MVC将负责发出HTTP 401响应,认证系统将捕获该响应并转变为挑战,并且用户将被重定向回到身份提供商。

我有一些代码可以显示这将如何在GitHub上的mderriey/TokenRenewal存储库中运行。虽然意图不同,但它显示了如何使用这些事件的机制。

© www.soinside.com 2019 - 2024. All rights reserved.