Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 797
Carlos G. Avatar asked Jan 19 '17 12:01

Carlos G.


People also ask

How do you use Refresh Token in identityserver4?

Requesting an access token using a refresh tokenTo get a new access token, you send the refresh token to the token endpoint. This will result in a new token response containing a new access token and its expiration and potentially also a new refresh token depending on the client configuration (see above).

What is sliding refresh token lifetime?

The Refresh token has a sliding window that is valid for 14 days and refresh token's validity is for 90 days.

What is token refreshing?

Refresh token rotation is a technique for getting new access tokens using refresh tokens that goes beyond silent authentication. Refresh token rotation guarantees that every time an application exchanges a refresh token to get a new access token, a new refresh token is also returned.


2 Answers

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");
    }
like image 110
LugTread Avatar answered Dec 09 '22 13:12

LugTread


As an option to RenewTokens method from MVC Client example, I made one filter that makes the job automatically, when the token is about 10 minutes or less to expire.

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

Usage:

[Authorize]
[TokenFilter]
public class HomeController : Controller
{}
like image 32
Rafael Trojack Avatar answered Dec 09 '22 15:12

Rafael Trojack