IdentityServer4客户端 - 刷新CookieAuthenticationEvents上的访问令牌

时间:2017-07-04 09:24:45

标签: authentication cookies identityserver4 asp.net-core-middleware

我正在尝试在访问令牌过期时使用刷新令牌。类似的问题得到回答here。并a sample code to renew token采取行动

我最终在startup.cs

中使用了以下代码
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "Cookies",
    //ExpireTimeSpan = TimeSpan.FromSeconds(100),
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    Events = new CookieAuthenticationEvents()
    {
        OnValidatePrincipal = async x =>
        {
            if (x.Properties?.Items[".Token.expires_at"] == null) return;

            var logger = loggerFactory.CreateLogger(this.GetType());

            var now = DateTimeOffset.UtcNow;
            var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
            var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
            var timeRemaining = tokenExpireTime.Subtract(now.DateTime);

            if (timeElapsed > timeRemaining)
            {
                var httpContextAuthentication = x.HttpContext.Authentication;//Donot use the HttpContext.Authentication to retrieve anything, this cause recursive call to this event
                var oldAccessToken = await httpContextAuthentication.GetTokenAsync("access_token");
                var oldRefreshToken = await httpContextAuthentication.GetTokenAsync("refresh_token");
                logger.LogInformation($"Refresh token :{oldRefreshToken}, old access token:{oldAccessToken}");


                var disco = await DiscoveryClient.GetAsync(AuthorityServer);
                if (disco.IsError) throw new Exception(disco.Error);

                var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
                var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
                logger.LogInformation("Refresh token requested. " + tokenResult.ErrorDescription);


                if (!tokenResult.IsError)
                {

                    var oldIdToken = await httpContextAuthentication.GetTokenAsync("id_token");
                    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.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
                    tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });

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

                }
                x.ShouldRenew = true;
            }
            else
            {
                logger.LogInformation("Not expired");
            }
        }

    }
});

客户端设置如下

AllowAccessTokensViaBrowser = true,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding,
AbsoluteRefreshTokenLifetime = 86400,    
AccessTokenLifetime = 10,
AllowOfflineAccess = true,
AccessTokenType = AccessTokenType.Reference

成功登录后,我收到了其他所有请求的401。日志说

  

[Identity Server] 2017-07-04 10:15:58.819 +01:00 [Debug]   &#34; TjpIkvHQi ../ cfivu6Nql5ADJJlZRuoJV1QI =&#34;在数据库中找到:True

     

[Identity Server] 2017-07-04 10:15:58.820 +01:00 [Debug]   &#34; reference_token&#34;有价值的补助:   &#34; .. 9e64c1235c6675fcef617914911846fecd72f7b372&#34;在商店发现,但有   过期。

     

[Identity Server] 2017-07-04 10:15:58.821 +01:00 [错误]无效   参考令牌。 &#34; {\&#34; ValidateLifetime \&#34;:true,
  \&#34; AccessTokenType \&#34;:\&#34;参考\&#34;,\&#34; TokenHandle \&#34;:   \&#34; .. 9e64c1235c6675fcef617914911846fecd72f7b372 \&#34; }&#34;

     

[Identity Server] 2017-07-04 10:15:58.822 +01:00 [调试]令牌是   无效。

     

[Identity Server] 2017-07-04 10:15:58.822 +01:00 [Debug]创建   非活动令牌的内省响应。

     

[Identity Server] 2017-07-04 10:15:58.822 +01:00 [信息]成功   象征内省。令牌状态:&#34;无效&#34;,用于API名称:&#34; api1&#34;

任何帮助都会受到高度赞赏

更新

基本上,当令牌过期时,我会在下一行获得System.StackOverflowException

var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();

更新2 不要使用HttpContext.Authentication来检索任何内容。检查下面的答案,找到工作实施

1 个答案:

答案 0 :(得分:4)

过去两天我一直在研究这个问题,但是无法完成这项工作。有趣的是,在这里发布问题后,我在2小时内完成了工作:)

Events = new CookieAuthenticationEvents()
{
    OnValidatePrincipal = async x =>
    {
        if (x.Properties?.Items[".Token.expires_at"] == null) return;
        var now = DateTimeOffset.UtcNow;

        var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
        var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
        var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
        WriteMessage($"{timeRemaining} and elapsed at {timeElapsed}");
        if (timeElapsed > timeRemaining)
        {
            var oldAccessToken = x.Properties.Items[".Token.access_token"];

            var oldRefreshToken = x.Properties.Items[".Token.refresh_token"];
            WriteMessage($"Refresh token :{oldRefreshToken}, old access token {oldAccessToken}");

            var disco = await DiscoveryClient.GetAsync(AuthorityServer);
            if (disco.IsError) throw new Exception(disco.Error);

            var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
            var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);

            if (!tokenResult.IsError)
            {
                var oldIdToken = x.Properties.Items[".Token.id_token"];//tokenResult.IdentityToken

                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.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
                tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });

                x.Properties.StoreTokens(tokens);

                WriteMessage($"oldAccessToken: {oldAccessToken}{Environment.NewLine} and new access token {newAccessToken}");

            }
            x.ShouldRenew = true;
        }
    }
}

基本上httpContextAuthentication.GetTokenAsync使这个递归,因此出现了StackOverflowException

如果此实施有任何问题,请告诉我