【问题标题】:Refresh tokens using owin middleware and IdentityServer v3使用 owin 中间件和 IdentityServer v3 刷新令牌
【发布时间】:2015-10-18 13:29:16
【问题描述】:

我最近设置了 IdentityServer v3,它的运行就像做梦一样,但是我在使用 OWIN 中间件时遇到了问题。

我想使用混合流,这样我就可以在后端刷新令牌,而用户不必每 5 分钟重定向回 IdentityServer 以获取一个新的访问令牌(这也很奇怪,因为它设置为具有生命周期服务器上 1 小时)。

我在启动时使用了以下配置并且我得到了很好的令牌,但是一旦它过期,它似乎永远不会尝试刷新访问令牌。我是否需要一些自定义逻辑来刷新我的令牌?

        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;
                        }
                    }
                }
            }
        });

我还在与我的资源 api 对话的 ApiClient (RestSharp) 中使用以下内容

public class MyTokenAuthenticator : IAuthenticator
{
    public void Authenticate(IRestClient client, IRestRequest request)
    {
        var tokenClaim = ClaimsPrincipal.Current.Claims.FirstOrDefault(c => c.Type.Equals("access_token"));

        if (tokenClaim != null && !String.IsNullOrWhiteSpace(tokenClaim.Value))
            request.AddHeader("Authorization", String.Format("Bearer {0}", tokenClaim.Value));
    }
}

【问题讨论】:

    标签: c# oauth-2.0 openid-connect identityserver3


    【解决方案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");
                }
            }
        } 
    

    【讨论】:

    • 在我看来,等到本地时间更大或相等的访问令牌过期时间不是最好的主意。想象一下这样一种情况,这种情况将认为 AccessToken 在到期前几毫秒有效。如果您将此类 AccessToken 发送到其他服务,那么它可能会被拒绝,因为它同时已过期。当您添加声明“expires_at”时,您还可以添加类似“refresh_at”声明作为现在和“expires_at”值之间的一半时间跨度。这样的解决方案将允许确保访问令牌在您能够使用之前不会过期。
    • 我通常允许,通过配置,我分钟检查令牌是否过期:例如,如果令牌在不到 1 分钟内过期,我会继续刷新它。
    猜你喜欢
    • 2014-09-18
    • 2016-10-07
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 2020-02-14
    • 2018-03-27
    • 1970-01-01
    • 2020-11-11
    相关资源
    最近更新 更多