2015-07-28 16 views
6

Niedawno skonfigurowałem IdentityServer v3 i jego działanie było jak sen, jednak mam problemy z oprogramowaniem pośredniczącym OWIN.Odświeżanie tokenów przy użyciu owin middleware i IdentityServer v3

Chciałbym użyć przepływu hybrydowego, aby odświeżać tokeny w backendach, bez konieczności przekierowania użytkownika z powrotem do serwera tożsamości, aby uzyskać nowy token dostępu co 5 minut (co również jest nieparzyste, ponieważ jego zestaw ma mieć czas życia 1 godziny na serwerze).

Używam następującej konfiguracji podczas uruchamiania i otrzymuję tokeny dobrze, ale nigdy nie wydaje się, aby spróbować odświeżyć token dostępu, gdy wygasł. Czy potrzebuję jakiejś niestandardowej logiki, aby odświeżyć moje tokeny?

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

Jestem również za pomocą następujących w moim ApiClient (RestSharp), który mówi do mojego API zasobów

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

Odpowiedz

1

udało mi się uzyskać odświeżanie żeton, a następnie użyć go, aby uzyskać dostęp do nowego token: Podążyłem za podobną logiką jak twoja, aby uzyskać token. I stworzył następującą metodę którą nazwałem każdym razem, gdy potrzebne token:

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"); 
      } 
     } 
    } 
+1

Moim zdaniem czeka do czasu lokalnego jest większy lub równy dostęp czas ważności tokenu nie jest najlepszym pomysłem. Wyobraźmy sobie sytuację, w której warunek ten będzie uważał za ważny tylko kilka milisekund przed wygaśnięciem. Jeśli wyślesz taki AccessToken do innej usługi, może on zostać odrzucony, ponieważ wygasł w międzyczasie. Podczas dodawania roszczenia "expires_at" możesz również dodać roszczenie typu "refresh_at" jako połowę okresu między teraz a wartością "expires_at". Takie rozwiązanie pozwoli upewnić się, że token dostępu nie wygaśnie, zanim będzie można z niego korzystać. – Gilmor

+0

Zwykle zezwalam, poprzez konfigurację, na minutę sprawdzam, czy wygasł token: na przykład, jeśli token wygaśnie za mniej niż 1 minutę, przejdę dalej i odświeżę go. – jahansha

Powiązane problemy