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

问题描述:

我试图在访问令牌到期时使用刷新令牌。类似的问题的答案为here。而a sample code to renew token一个动作IdentityServer4客户端 - 在CookieAuthenticationEvents上刷新访问令牌

和我结束了在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年7月4日10:15:58.819 +01:00 [调试] “TjpIkvHQi ../ cfivu6Nql5ADJJlZRuoJV1QI =” 在数据库中找到:真

[ [服务器] 2017-07-04 10:15:58.820 +01:00 [调试] “reference_token”授予值: “.9e64c1235c6675fcef617914911846fecd72f7b372”在商店中发现,但已经过期 。

[Identity Server] 2017-07-04 10:15:58.821 +01:00 [错误]无效的 引用令牌。 “{\” ValidateLifetime \ “:真的,
\” AccessTokenType \ “:\” 参考\ “\ ”TokenHandle \“: \” .. 9e64c1235c6675fcef617914911846fecd72f7b372 \ “}”

[标识服务器] 2017- 07-04 10:15:58.822 +01:00 [调试]令牌为 无效。

[Identity Server] 2017-07-04 10:15:58.822 +01:00 [调试]为不活动令牌创建自检响应。

[Identity Server] 2017-07-04 10:15:58.822 +01:00 [信息]成功 令牌自省。令牌状态: “禁用”,API名称: “API1”

任何帮助将通过高度赞赏

UPDATE

基本上,当令牌过期我就得到一个System.StackOverflowException以下行

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

UPDATE 2不要使用HttpContext.Authentication来检索任何东西。检查我的答案下面找到工作实现

我在这工作了最后两天,无法使这项工作。有趣地,在这里张贴的问题后,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发生。

请让我知道,如果这实现了

+0

为什么创造AuthenticationTokens的列表时使用oldIdToken代替tokenResult.IdentityToken价值的任何问题? –

+0

@JonasAxelsson是的,'tokenResult.IdentityToken'也可以用来刷新IdentityToken,但在这里我试图弄清楚如何刷新令牌在IdS4上工作 – MJK

+1

似乎oldIdToken值不同于tokenResult.IdentityToken,所以它很可能最好使用新的tokenResult.AccessToken和tokenResult.RefreshToken –