C# 使用owin中间件和IdentityServer v3刷新令牌
我最近安装了IdentityServer v3,它的运行就像做梦一样,但是我在OWIN中间件方面遇到了问题 我想使用混合流,这样我就可以在后端刷新令牌,而无需用户每5分钟重定向回IdentityServer以获取一个新的访问令牌(这也很奇怪,因为它在服务器上的生命周期设置为1小时) 我在startup中使用了下面的配置,我可以很好地获得令牌,但它似乎从来没有尝试在访问令牌过期后刷新它。我是否需要一些自定义逻辑来刷新我的令牌C# 使用owin中间件和IdentityServer v3刷新令牌,c#,oauth-2.0,openid-connect,identityserver3,C#,Oauth 2.0,Openid Connect,Identityserver3,我最近安装了IdentityServer v3,它的运行就像做梦一样,但是我在OWIN中间件方面遇到了问题 我想使用混合流,这样我就可以在后端刷新令牌,而无需用户每5分钟重定向回IdentityServer以获取一个新的访问令牌(这也很奇怪,因为它在服务器上的生命周期设置为1小时) 我在startup中使用了下面的配置,我可以很好地获得令牌,但它似乎从来没有尝试在访问令牌过期后刷新它。我是否需要一些自定义逻辑来刷新我的令牌 app.UseOpenIdConnectAuthenti
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret, //Not sure what this does?
Authority = "https://auth.example.com",
RedirectUri = "http://website.example.com",
PostLogoutRedirectUri = "http://website.example.com",
ResponseType = "code id_token token",
Scope = "openid profile email write read offline_access",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// filter "protocol" claims
var claims = new List<Claim>(from c in n.AuthenticationTicket.Identity.Claims
where c.Type != "iss" &&
c.Type != "aud" &&
c.Type != "nbf" &&
c.Type != "exp" &&
c.Type != "iat" &&
c.Type != "nonce" &&
c.Type != "c_hash" &&
c.Type != "at_hash"
select c);
// get userinfo data
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),
n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => claims.Add(new Claim(ui.Item1, ui.Item2)));
// get access and refresh token
var tokenClient = new OAuth2Client(
new Uri(n.Options.Authority + "/connect/token"),
clientId,
clientSecret);
var response = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
claims.Add(new Claim("access_token", response.AccessToken));
claims.Add(new Claim("expires_at", DateTime.UtcNow.AddSeconds(response.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
claims.Add(new Claim("refresh_token", response.RefreshToken));
claims.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
//Does this help?
n.AuthenticationTicket.Properties.AllowRefresh = true;
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(
claims.Distinct(new ClaimComparer()),
n.AuthenticationTicket.Identity.AuthenticationType),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = async n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var id = n.OwinContext.Authentication.User.FindFirst("id_token");
if (id != null)
{
var idTokenHint = id.Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
}
}
}
});
我能够获取刷新令牌,然后使用它获取新的访问令牌: 我按照和你一样的逻辑得到了一个代币。 我创建了以下方法,每次需要令牌时都会调用该方法:
private static async Task CheckAndPossiblyRefreshToken(ClaimsIdentity id)
{
var clientName = "Myhybridclient";
// check if the access token hasn't expired.
if (DateTime.Now.ToLocalTime() >=
(DateTime.Parse(id.FindFirst("expires_at").Value)))
{
// expired. Get a new one.
var tokenEndpointClient = new OAuth2Client(
new Uri(Constants.TokenEndpoint),
clientName,
"secret");
var tokenEndpointResponse =
await tokenEndpointClient
.RequestRefreshTokenAsync(id.FindFirst("refresh_token").Value);
if (!tokenEndpointResponse.IsError)
{
// replace the claims with the new values - this means creating a
// new identity!
var result = from claim in id.Claims
where claim.Type != "access_token" && claim.Type != "refresh_token" &&
claim.Type != "expires_at"
select claim;
var claims = result.ToList();
claims.Add(new Claim("access_token", tokenEndpointResponse.AccessToken));
claims.Add(new Claim("expires_at",
DateTime.Now.AddSeconds(tokenEndpointResponse.ExpiresIn)
.ToLocalTime().ToString()));
claims.Add(new Claim("refresh_token", tokenEndpointResponse.RefreshToken));
var newIdentity = new ClaimsIdentity(claims, "Cookies");
var wrapper = new HttpRequestWrapper(HttpContext.Current.Request);
wrapper.GetOwinContext().Authentication.SignIn(newIdentity);
}
else
{
// log, ...
throw new Exception("An error has occurred");
}
}
}
在我看来,等待本地时间大于或等于访问令牌到期时间不是最好的主意。设想一种情况,当这种情况将考虑Access在到期前只有几毫秒的有效性。如果您将此类AccessToken发送到其他服务,那么它可能会被拒绝,因为它在此期间已过期。当您添加声明“expires\u at”时,您还可以添加类似“refresh\u at”的声明,作为从现在到“expires\u at”值之间的一半时间间隔。这样的解决方案可以确保访问令牌不会在您能够使用它之前过期。我通常允许通过配置,用1分钟来检查令牌是否过期:例如,如果令牌在1分钟内过期,我将继续刷新它。
private static async Task CheckAndPossiblyRefreshToken(ClaimsIdentity id)
{
var clientName = "Myhybridclient";
// check if the access token hasn't expired.
if (DateTime.Now.ToLocalTime() >=
(DateTime.Parse(id.FindFirst("expires_at").Value)))
{
// expired. Get a new one.
var tokenEndpointClient = new OAuth2Client(
new Uri(Constants.TokenEndpoint),
clientName,
"secret");
var tokenEndpointResponse =
await tokenEndpointClient
.RequestRefreshTokenAsync(id.FindFirst("refresh_token").Value);
if (!tokenEndpointResponse.IsError)
{
// replace the claims with the new values - this means creating a
// new identity!
var result = from claim in id.Claims
where claim.Type != "access_token" && claim.Type != "refresh_token" &&
claim.Type != "expires_at"
select claim;
var claims = result.ToList();
claims.Add(new Claim("access_token", tokenEndpointResponse.AccessToken));
claims.Add(new Claim("expires_at",
DateTime.Now.AddSeconds(tokenEndpointResponse.ExpiresIn)
.ToLocalTime().ToString()));
claims.Add(new Claim("refresh_token", tokenEndpointResponse.RefreshToken));
var newIdentity = new ClaimsIdentity(claims, "Cookies");
var wrapper = new HttpRequestWrapper(HttpContext.Current.Request);
wrapper.GetOwinContext().Authentication.SignIn(newIdentity);
}
else
{
// log, ...
throw new Exception("An error has occurred");
}
}
}