Refreshing access tokens in IdentityServer4 clients

Jonas picture Jonas · Dec 15, 2016 · Viewed 8k times · Source

I wonder how to refresh a access token in a IdentityServer4 client using the hybrid flow and which is built using ASP.NET Core MVC.

If I have understood the whole concept correctly the client first need to have the "offline_access" scope in order to be able to use refresh tokens which is best practice to enable short lived access tokens and ability to revoke refresh tokens preventing any new access tokens to be issued to the client.

I successfully get a access token and a refresh token, but how should I handle the actual update procedure of the access token in the MVC client?

Can the OpenId Connect (OIDC) middleware handle this automatically? Or should I rather check the expire time of the access token everywhere I call WEB Api's by basically check if the access token have expired or will expire very soon (upcoming 30 seconds) then refresh the access token by calling the token endpoint using the refresh token?

Is it recommended to use the IdentityModel2 library TokenClient extension method RequestRefreshTokenAsync in my Controller action methods for calling the token endpoint?

I have seen code that in the OIDC middleware events request access token and using the response store a claim containing a expire datetime. The problem is that my OIDC in somehow already request a access token automatically so it doesn't feel good to request a new access token directly after recieving the first one.

Example of a Controller action method without access token refresh logic:

public async Task<IActionResult> GetInvoices()
    {
        var token = await HttpContext.Authentication.GetTokenAsync("access_token");

        var client = new HttpClient();
        client.SetBearerToken(token);

        var response = await client.GetStringAsync("http://localhost:5001/api/getInvoices");
        ViewBag.Json = JArray.Parse(response).ToString();

        return View();
    }

Answer

Micka&#235;l Derriey picture Mickaël Derriey · Jan 9, 2017

The OIDC middleware will not take care of this for you. It's being executed when it detects a HTTP 401 response, it then redirects the user to IdentityServer login page. After the redirection to your MVC application, it will turn claims into a ClaimsIdentity and pass this on to the Cookies middleware which will materialise that into a session cookie.

Every other request will not involve the OIDC middleware as long as the cookie is still valid.

So you have to take care of this yourself. Another thing you want to consider is that whenever you're going to refresh the access token, you'll have to update the existing one so you don't lose it. If you don't do this, the session cookie will always contain the same token - the original one - and you'll refresh it every time.

A solution I found is to hook that into the Cookies middleware. Here's the general flow:

  • On every request, use the Cookies middleware events to inspect the access token
  • If it's close to its expiration time, request a new one
  • Replace the new access and refresh tokens in the ClaimsIdentity
  • Instruct the Cookies middleware to renew the session cookie so it contains the new tokens

What I like with this approach is that in your MVC code, you're pretty much guaranteed to always have a valid access token, unless refereshing the token keeps failing several times in a row.

What I don't like is that it's very tied to MVC - more specifically the Cookies middleware - so it's not really portable.

You can have a look at this GitHub repo I put together. It indeed uses IdentityModel as this takes care of everything and hides most of the complexity of the HTTP calls you'd have to make to IdentityServer.