Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IdentityServer 3 refresh user with refresh token

We are trying to set up Identity Server 3 in the right way. We got authentication working fine and we manage to retrieve the refresh token.

The client application is using Angular.

Now when the acces_token expires any calls to the rest api fails (we managed to get it to return 401) but we are wondering how to re-authenticate the user.

In our tests, any api call made from Javascript is failing (401) but as soon as the page is refreshed the whole mechanism is kicking in. We do see that we are redirected to the identity server but it does not show up the login page, we are sent back to the client application with new tokens apparently.

What I would like to do is to refresh the access token without having to force the user to refresh the page.

What I'm not sure though is whose responsibility is it? Is that the client application (website) or the angular application? In other word, should the application handle this transparently for Angular or should angular do something when it receives a 401, in which case, I'm not too sure how the information will flow back to the web app.

Any clue?

Additional Information: We are using OpenId Connect

like image 751
Georges Legros Avatar asked Dec 10 '15 13:12

Georges Legros


2 Answers

I got it working!

As I said in the comments I used this article. The writer is referencing a very nice lib that I am using as well.

Facts:

  1. Identity Server 3 is requesting the client secret upon access token refresh
  2. One should not store the refresh_token or the client_secret on the javascript application as they are considered unsafe (see the article)

So I chose to send the refresh_token as en encrypted cookie sith this class (found of ST BTW, just can't find the link anymore, sorry...)

public static class StringEncryptor
{
    public static string Encrypt(string plaintextValue)
    {
        var plaintextBytes = plaintextValue.Select(c => (byte) c).ToArray();
        var encryptedBytes = MachineKey.Protect(plaintextBytes);
        return Convert.ToBase64String(encryptedBytes);
    }

    public static string Decrypt(string encryptedValue)
    {
        try
        {
            var encryptedBytes = Convert.FromBase64String(encryptedValue);
            var decryptedBytes = MachineKey.Unprotect(encryptedBytes);
            return new string(decryptedBytes.Select(b => (char)b).ToArray());
        }
        catch
        {
            return null;
        }
    }
}

The javascript application is getting the value from the cookie. It then deletes the cookie to avoid that thing to be sent over and over again, it is pointless.

When the access_token becomes invalid, I send an http request to the application server with the encrypted refresh_token. That is an anonymous call.

The server contacts the identity server and gets a new access_token that is sent back to Javascript. The awesome library queued all other requests so when I'm back with my new token, I can tell it to continue with authService.loginConfirmed();.

The refresh is actually pretty easy as all you have to do is to use the TokenClient from IdentityServer3. Full method code:

    [HttpPost]
    [AllowAnonymous]
    public async Task<JsonResult> RefreshToken(string refreshToken)
    {
        var tokenClient = new TokenClient(IdentityServerConstants.IdentityServerUrl + "/connect/token", "my-application-id", "my-application-secret");
        var response = await tokenClient.RequestRefreshTokenAsync(StringEncryptor.Decrypt(refreshToken));

        return Json(new {response.AccessToken});
    }

Comments are welcome, this is probably the best way to do that.

like image 188
Georges Legros Avatar answered Oct 23 '22 14:10

Georges Legros


For future reference - using refresh tokens in an angular (or other JS) application is not the correct way as a refresh token is too sensitive to store in the browser. You should use silent renew based on the identityserver cookie to get a new access token. Also see the oidc-client-js javascript library, as this can manage silent renew for you.

like image 34
stombeur Avatar answered Oct 23 '22 14:10

stombeur