IdentityServer4客户端 - 刷新访问令牌上CookieAuthenticationEvents [英] IdentityServer4 client - Refreshing access tokens on CookieAuthenticationEvents
问题描述
我正在尝试在访问令牌到期时使用刷新令牌.类似这样的问题时回答 href="https://stackoverflow.com/a/41557598/3501052">.还有续订令牌的示例代码动作
I am trying to use refresh token when the access token expires. A similar so question is answered here. And a sample code to renew token by an action
和i相在startup.cs以下代码结束
And i end up with the following code in the 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.和日志说:
After successfully login, i am getting a 401 for every other request. And the log says
[身份服务器] 2017-07-04 10:15:58.819 +01:00 [调试] TjpIkvHQi ../cfivu6Nql5ADJJlZRuoJV1QI =" 在数据库中找到:真
[Identity Server]2017-07-04 10:15:58.819 +01:00 [Debug] "TjpIkvHQi../cfivu6Nql5ADJJlZRuoJV1QI=" found in database: True
[身份服务器] 2017年7月4日10:15:58.820 01:00 [调试] reference_token"授与价值: " ..9e64c1235c6675fcef617914911846fecd72f7b372" 在商店找到,但有 过期.
[Identity Server]2017-07-04 10:15:58.820 +01:00 [Debug] "reference_token" grant with value: "..9e64c1235c6675fcef617914911846fecd72f7b372" found in store, but has expired.
[身份服务器] 2017年7月4日10:15:58.821 01:00 [错误]无效
参考标记. "{\" ValidateLifetime \:是,
\"AccessTokenType \":\参考\",\"TokenHandle \":
\ .. 9e64c1235c6675fcef617914911846fecd72f7b372 \"}
[Identity Server]2017-07-04 10:15:58.821 +01:00 [Error] Invalid
reference token. "{ \"ValidateLifetime\": true,
\"AccessTokenType\": \"Reference\", \"TokenHandle\":
\"..9e64c1235c6675fcef617914911846fecd72f7b372\" }"
[身份服务器] 2017年7月4日10:15:58.822 01:00 [调试]令牌是 无效.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Token is invalid.
[身份服务器] 2017年7月4日10:15:58.822 01:00 [调试]创建 无效令牌的自省响应.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Creating introspection response for inactive token.
[身份服务器] 2017年7月4日10:15:58.822 01:00 [信息]成功 令牌反省.令牌状态: 无效",对于API名: API1"
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Information] Success token introspection. Token status: "inactive", for API name: "api1"
任何帮助将通过高度赞赏
Any help would by highly appreciated
<强>更新强>:
基本上,当令牌过期我得到一个
Basically, when the token expires i get a System.StackOverflowException
on the following line
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
更新2 : 的不使用HttpContext.Authentication检索任何的.检查我的回答下面找到工作implementaion
UPDATE 2: Do not use HttpContext.Authentication to retrieve anything. Check my answer below to find the working implementaion
推荐答案
我在做这个的最后两天,无法使这项工作.有趣地,在这里张贴的问题后,2小时内我做它的工作:)
I was working on this for last two days and could not make this work. Funnily, after posting the question here, within 2 hours I make it working :)
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
.
Basically httpContextAuthentication.GetTokenAsync
make this recursive, for that reason StackOverflowException
occured.
请让我知道,如果这实现了任何问题
Please let me know if this implementation has any issue
这篇关于IdentityServer4客户端 - 刷新访问令牌上CookieAuthenticationEvents的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!