Search code examples
c#identityserver4refresh-token

IdentityServer4 Refresh Token: How to determine expiration time?


I am working with the Identity Server 4 sample code. In particular, for the client I am using the sample MVC Client with the Hybrid flow: https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Clients/src/MvcHybrid

And for the server I am using Identity Server with in-memory clients (no Entity Framework, and no ASP.Net Identity): https://github.com/IdentityServer/IdentityServer4/tree/master/samples/Quickstarts

Both client and server have pretty much vanilla, out-of-the-box configuration.

I am trying to understand how refresh tokens expire and how a native app can pro-actively determine the expiration time (before it gets rejected by an API). My understanding is that the default expiration for refresh tokens is long:

http://docs.identityserver.io/en/latest/topics/refresh_tokens.html:

Maximum lifetime of a refresh token in seconds. Defaults to 2592000 seconds / 30 days

However, when the sample code requests a refresh token, I do not get the expected expiration time. Here is the sample code:

var disco = await _discoveryCache.GetAsync();
if (disco.IsError) throw new Exception(disco.Error);

var rt = await HttpContext.GetTokenAsync("refresh_token");
var tokenClient = _httpClientFactory.CreateClient();

var tokenResult = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
{
    Address = disco.TokenEndpoint,

    ClientId = "mvc.hybrid",
    ClientSecret = "secret",
    RefreshToken = rt
});

tokenResult.ExpiresIn is 3600 seconds, which is actually the expiration of an access token. I was expecting that to be 2592000 seconds. So question #1 is: Why is this the case?

But more importantly, I know that the expiration for the refresh token is in fact the default 30 days when I use SQL Server as the data store. There is a table PersistedGrants that contains the refresh tokens, and the expiration is clearly 30 days from the issue date. So question #2 is: How can an app programmatically determine the expiration date of the refresh token it received?

I've tried to parse the RefreshToken itself, but it is not really a full JWT, so this throws an error:

var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessTokenResponse.RefreshToken);
var diff = jwt.ValidTo - jwt.ValidFrom;

I've also searched through the IdentityServer4 unit / integration tests and cannot find an example of introspecting a refresh token.

Presumably that information either needs to be somewhere in the initial token response, or there needs to be an endpoint built into Identity Server. But I can't find either of these things.


Solution

  • Ok, so the answer is that there is no data in the access_token response that indicates the expiration time of the refresh_token. Additionally, there is no endpoint that can be used to check the expiration.

    The OAuth spec does not say anything about this, so I did not want to alter the access_token response. I wound up making my own endpoint that returns the expiration time if needed. Here is my controller action, if anyone needs a starting point:

    private readonly IRefreshTokenStore _refreshTokenStore; // inject this into your controller
    
    ...
    
    [Route("[controller]/GetRefreshTokenExpiration")]
    [Authorize(...YOUR SCOPE...)]
    public async Task<IActionResult> GetRefreshTokenExpiration(string refreshTokenKey)
    {
        var refreshToken = await this._refreshTokenStore.GetRefreshTokenAsync(refreshTokenKey);
        if (refreshToken == null)
        {
            return NotFound(new { message = "Refresh token not found" });
        }
        return Ok(new {
            message = "Refresh token found",
            lifetime_seconds = refreshToken.Lifetime
        });
    }