Search code examples
.netidentityserver4

IdentityServer4 - Using Refresh Tokens after following the Quickstart for Hybrid MVC


I've followed the Quickstart in the documentation page and have a working configuration of three services (IdentityServer, one Api service, one ASPNET MVC application) using IdentityServer for authentication.

Everything works perfectly (login, login, authorization, etc.) until after 1 hour when the access_token expires. At this point, the MVC application starts to receive (correctly) a 401 from the API service (since the token is expired). At that point, I know I should use the refresh_token to get a new access_token.

I was looking for a mechanism that automatically refreshed the access_token and stumbled upon this: https://github.com/mderriey/TokenRenewal/blob/master/src/MvcClient/Startup.cs (from this answer). I tried to use that but it didn't work (the TokenEndpointResponse was null even though the authentication was successful).

I understand how to use a refresh_token to get a new access_token, but after I have it, how would I go inserting it back into the cookie so that future request have access to the new tokens?


Solution

  • The McvHybrid sample has a good example for getting the new access_token and refresh_token back into the principal. Here's a link to the github file with the code, which is located in RenewTokens() as shown below.

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