2017-01-19 33 views
5

Śledziłem Quickstart na stronie dokumentacji i posiadam działającą konfigurację trzech usług (IdentityServer, jedna usługa Api, jedna aplikacja ASPV MVC) przy użyciu IdentityServer do uwierzytelniania.IdentityServer4 - Używanie odświeżania tokenów po wykonaniu Quickstart for Hybrid MVC

Wszystko działa idealnie (logowanie, logowanie, autoryzacja itp.) Aż do 1 godziny po wygaśnięciu access_token. W tym momencie aplikacja MVC rozpoczyna odbiór (poprawnie) 401 z usługi API (ponieważ token wygasł). W tym momencie wiem, że powinienem użyć parametru refresh_token, aby uzyskać nowy access_token.

Szukałem mechanizmu, który automatycznie odświeżył access_token i natknął się na to: https://github.com/mderriey/TokenRenewal/blob/master/src/MvcClient/Startup.cs (od this answer). Próbowałem go użyć, ale nie zadziałało (TokenEndpointResponse był pusty, mimo że uwierzytelnienie się powiodło).

Rozumiem, jak używać refresh_token, aby uzyskać nowy access_token, ale po tym, jak go mam, jak mam go wstawić z powrotem do cookie, aby przyszłe żądanie miało dostęp do nowych tokenów?

Odpowiedz

8

Przykład McvHybrid jest dobrym przykładem na to, że nowy access_token i refresh_token wraca do zleceniodawcy. Oto link do pliku github z kodem, który znajduje się w RenewTokens(), jak pokazano poniżej.

public async Task<IActionResult> RenewTokens() 
    { 
     var disco = await DiscoveryClient.GetAsync(Constants.Authority); 
     if (disco.IsError) throw new Exception(disco.Error); 

     var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc.hybrid", "secret"); 
     var rt = await  HttpContext.Authentication.GetTokenAsync("refresh_token"); 
     var tokenResult = await tokenClient.RequestRefreshTokenAsync(rt); 

     if (!tokenResult.IsError) 
     { 
      var old_id_token = await HttpContext.Authentication.GetTokenAsync("id_token"); 
      var new_access_token = tokenResult.AccessToken; 
      var new_refresh_token = tokenResult.RefreshToken; 

      var tokens = new List<AuthenticationToken>(); 
      tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.IdToken, Value = old_id_token }); 
      tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.AccessToken, Value = new_access_token }); 
      tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.RefreshToken, Value = new_refresh_token }); 

      var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn); 
      tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) }); 

      var info = await HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies"); 
      info.Properties.StoreTokens(tokens); 
      await HttpContext.Authentication.SignInAsync("Cookies", info.Principal, info.Properties); 

      return Redirect("~/Home/Secure"); 
     } 

     ViewData["Error"] = tokenResult.Error; 
     return View("Error"); 
    } 
+1

w próbce RenewTokens McvHybrid są wywoływane tylko ręcznie (poprzez kliknięcie linku). Czy znasz przykład, kiedy odświeżanie tokena jest wywoływane, gdy dojdzie do wygaśnięcia tokena dostępu? –

+0

Możesz łatwo rzucić powyższy kod do jakiegoś oprogramowania pośredniego, które jest następnie rejestrowane w twoim potoku przed MVC. – LugTread

+1

@LugTread Nie przypuszczam, że miałbyś jakiś przykład tego? Jestem nowicjuszem dla MVC w .NET Core i nigdy nie napisałem middleware, szukam teraz tego, ale jeśli zrobiłeś coś podobnego i masz przykład kodu, który byłby świetny. Dziękuję Ci. – bgs264

1

Jako opcja do sposobu RenewTokens z MVC Client example, zrobiłem jeden filtr, który sprawia, że ​​praca się automatycznie, gdy znacznik jest około 10 minut lub mniej wygaśnie.

public class TokenFilterAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var expat = filterContext.HttpContext.Authentication.GetTokenAsync("expires_at").Result; 

     var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind); 

     if ((dataExp - DateTime.Now).TotalMinutes < 10) 
     { 
      var disco = DiscoveryClient.GetAsync("http://localhost:5000/").Result; 
      if (disco.IsError) throw new Exception(disco.Error); 

      var tokenClient = new TokenClient(disco.TokenEndpoint, "clientId", 
       "clientSecret"); 

      var rt = filterContext.HttpContext.Authentication.GetTokenAsync("refresh_token").Result; 
      var tokenResult = tokenClient.RequestRefreshTokenAsync(rt).Result; 

      if (!tokenResult.IsError) 
      { 
       var oldIdToken = filterContext.HttpContext.Authentication.GetTokenAsync("id_token").Result; 
       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.Now + TimeSpan.FromSeconds(tokenResult.ExpiresIn); 
       tokens.Add(new AuthenticationToken 
       { 
        Name = "expires_at", 
        Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) 
       }); 

       var info = filterContext.HttpContext.Authentication.GetAuthenticateInfoAsync("Cookies").Result; 
       info.Properties.StoreTokens(tokens); 
       filterContext.HttpContext.Authentication.SignInAsync("Cookies", info.Principal, info.Properties); 
      } 
     } 
    } 
} 

Zastosowanie:

[Authorize] 
[TokenFilter] 
public class HomeController : Controller 
{}