IdentityServer4 - 在遵循Hybrid MVC的快速入门之后使用刷新令牌

Car*_* G. 12 .net identityserver4

我已经按照文档页面中的Quickstart进行操作,并使用IdentityServer进行身份验证,从而实现三种服务(IdentityServer,一种Api服务,一种ASPNET MVC应用程序)的工作配置.

一切正常(登录,登录,授权等),直到access_token到期后1小时.此时,MVC应用程序开始(正确地)从API服务接收401(因为令牌已过期).那时,我知道我应该使用refresh_token来获取新的access_token.

我正在寻找一种自动刷新access_token的机制,并偶然发现:https://github.com/mderriey/TokenRenewal/blob/master/src/MvcClient/Startup.cs(来自这个答案).我尝试使用它,但它不起作用(TokenEndpointResponse即使身份验证成功,也为null).

我理解如何使用a refresh_token来获取新的access_token,但是在我拥有它之后,我将如何将其插回到cookie中以便将来的请求可以访问新的令牌?

小智 12

McvHybrid样本有一个很好的例子来获得新的access_tokenrefresh_token回到校长.这是带有代码的github文件的链接,RenewTokens()如下所示.

    public async Task<IActionResult> RenewTokens()
    {
        var disco = await DiscoveryClient.GetAsync(Constants.Authority);
        if (disco.IsError) throw new Exception(disco.Error);

        var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc.hybrid", "secret");
        var rt = await     HttpContext.Authentication.GetTokenAsync("refresh_token");
        var tokenResult = await tokenClient.RequestRefreshTokenAsync(rt);

        if (!tokenResult.IsError)
        {
            var old_id_token = await HttpContext.Authentication.GetTokenAsync("id_token");
            var new_access_token = tokenResult.AccessToken;
            var new_refresh_token = tokenResult.RefreshToken;

            var tokens = new List<AuthenticationToken>();
            tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.IdToken, Value = old_id_token });
            tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.AccessToken, Value = new_access_token });
            tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.RefreshToken, Value = new_refresh_token });

            var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
            tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });

            var info = await HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies");
            info.Properties.StoreTokens(tokens);
            await HttpContext.Authentication.SignInAsync("Cookies", info.Principal, info.Properties);

            return Redirect("~/Home/Secure");
        }

        ViewData["Error"] = tokenResult.Error;
        return View("Error");
    }
Run Code Online (Sandbox Code Playgroud)

  • 在McvHybrid示例中,只能手动调用RenewToken(通过单击链接)。您是否知道在访问令牌即将到期时调用刷新令牌的示例? (3认同)

Raf*_*ack 6

作为来自MVC客户端示例的 RenewTokens方法的一个选项,我做了一个过滤器,当令牌大约10分钟或更短时间到期时自动生成作业.

public class TokenFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var expat = filterContext.HttpContext.Authentication.GetTokenAsync("expires_at").Result;

        var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);

        if ((dataExp - DateTime.Now).TotalMinutes < 10)
        {
            var disco = DiscoveryClient.GetAsync("http://localhost:5000/").Result;
            if (disco.IsError) throw new Exception(disco.Error);

            var tokenClient = new TokenClient(disco.TokenEndpoint, "clientId",
                "clientSecret");

            var rt = filterContext.HttpContext.Authentication.GetTokenAsync("refresh_token").Result;
            var tokenResult = tokenClient.RequestRefreshTokenAsync(rt).Result;

            if (!tokenResult.IsError)
            {
                var oldIdToken = filterContext.HttpContext.Authentication.GetTokenAsync("id_token").Result;
                var newAccessToken = tokenResult.AccessToken;
                var newRefreshToken = tokenResult.RefreshToken;

                var tokens = new List<AuthenticationToken>
                {
                    new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},
                    new AuthenticationToken
                    {
                        Name = OpenIdConnectParameterNames.AccessToken,
                        Value = newAccessToken
                    },
                    new AuthenticationToken
                    {
                        Name = OpenIdConnectParameterNames.RefreshToken,
                        Value = newRefreshToken
                    }
                };

                var expiresAt = DateTime.Now + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
                tokens.Add(new AuthenticationToken
                {
                    Name = "expires_at",
                    Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
                });

                var info = filterContext.HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies").Result;
                info.Properties.StoreTokens(tokens);
                filterContext.HttpContext.Authentication.SignInAsync("Cookies", info.Principal, info.Properties);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

[Authorize]
[TokenFilter]
public class HomeController : Controller
{}
Run Code Online (Sandbox Code Playgroud)


小智 5

首先,请务必使用 IdentityModel 库 (nuget it)。\n其次,由于 Auth 2.0 已发布,因此出现了一些重大更改,并且 Rafaels 解决方案中使用的 HttpContext.Authentication 现已过时。以下是要使其再次作为过滤器启动并运行所需进行的更改

\n\n
var expat = filterContext.HttpContext.Authentication.GetTokenAsync("expires_at").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该变成:

\n\n
var expat = filterContext.HttpContext.GetTokenAsync("expires_at").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n
var rt = filterContext.HttpContext.Authentication.GetTokenAsync("refresh_token").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该变成:

\n\n
var rt = filterContext.HttpContext.GetTokenAsync("refresh_token").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n
var oldIdToken = filterContext.HttpContext.Authentication.GetTokenAsync("id_token").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该成为

\n\n
var oldIdToken = filterContext.HttpContext.GetTokenAsync("id_token").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n
var info = filterContext.HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该成为

\n\n
var info = filterContext.HttpContext.AuthenticateAsync("Cookies").Result;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n
filterContext.HttpContext.Authentication.SignInAsync("Cookies", info.Principal, info.Properties);\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该成为

\n\n
filterContext.HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是整个代码:

\n\n
public class TokenFilterAttribute : ActionFilterAttribute\n{\n    public override void OnActionExecuting(ActionExecutingContext filterContext)\n    {\n        var expat = filterContext.HttpContext.GetTokenAsync("expires_at").Result;\n\n        var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);\n\n        if ((dataExp - DateTime.Now).TotalMinutes < 10)\n        {\n            var disco = DiscoveryClient.GetAsync("http://localhost:5000/").Result;\n            if (disco.IsError) throw new Exception(disco.Error);\n\n            var tokenClient = new TokenClient(disco.TokenEndpoint, "clientId",\n            "clientSecret");\n\n            var rt = filterContext.HttpContext.GetTokenAsync("refresh_token").Result;\n            var tokenResult = tokenClient.RequestRefreshTokenAsync(rt).Result;\n\n            if (!tokenResult.IsError)\n            {\n                var oldIdToken = filterContext.HttpContext.GetTokenAsync("id_token").Result;\n                var newAccessToken = tokenResult.AccessToken;\n                var newRefreshToken = tokenResult.RefreshToken;\n\n                var tokens = new List<AuthenticationToken>\n                {\n                    new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},\n                    new AuthenticationToken\n                    {\n                        Name = OpenIdConnectParameterNames.AccessToken,\n                        Value = newAccessToken\n                    },\n                    new AuthenticationToken\n                    { \n                        Name = OpenIdConnectParameterNames.RefreshToken,\n                        Value = newRefreshToken\n                    }\n                };\n\n                var expiresAt = DateTime.Now + TimeSpan.FromSeconds(tokenResult.ExpiresIn);\n                tokens.Add(new AuthenticationToken\n                {\n                    Name = "expires_at",\n                    Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)\n                });\n\n                var info = filterContext.HttpContext.AuthenticateAsync("Cookies").Result;\n                info.Properties.StoreTokens(tokens);  \n                filterContext.HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Us\xd0\xb0ge 与 Rafael 显示的相同。

\n