Adding Refresh Tokens to a Web API v2 Authorization Server

In the last post I showed how to add a simple username/password (aka resource owner password credentials flow) authorization server to Web API v2. This has several advantages:

  • The client does not need to hold on to the user credentials after the token has been requested (e.g. for re-submitting them on every request)
  • The user does not need to re-enter his credentials for the lifetime of the token
  • The back end does not need to validate the password on each request (passwords become harder and harder too secure – and validation becomes increasingly expensive)
  • Credential validation is factored out into the authorization server, and does not “pollute” the business services

This is already a big improvement. But there are also some security/architecture considerations:

  • Once the token has been issued, there is no “built-in” way to revoke it. Or in other words you’d need to write your own mechanism for that which often involves database checks on each request. Doable – but often defeats the purpose.
  • Related to that – when a claim that’s inside the token changes in your back end (e.g. role membership) you cannot easily “update” the token. You need to wait until the old token expires for the new claims to propagate. A good example of that is Windows authentication – when an admin adds or removes you from a Windows group, you need to logoff/logon for your Windows token to get updated.

For these reasons you need to find a balance between usability (how often must the user re-authenticate) and security/enforcing business requirements.

Refresh tokens can potentially improve the situation but also increase complexity. A refresh token is a long lived token that allows requesting new access tokens without having to present the user credentials again. This means that the access token itself could be short lived and whenever the refresh token is used to request a new access token, the contents of that access token can be updated. But with that power, you’ll have more responsibilities. Let’s have a look.

Refresh tokens must be bound to a client – you typically don’t want that a refresh token from your desktop client can be used from the web client and so on (this is also important for being able to revoke them). That means you need to introduce client authentication (or at least identification). This also means that your client needs an embedded credential (or must use dynamic client registration – but that is out of scope for this post). Depending on the client type this might not be a real secret and shouldn’t be used to base further security decisions on.

This changes how the ValidateClientAuthentication method looks like. We need to validate client credentials, and need to make the client ID available in the pipeline for later processing (unfortunately we need to use the OWIN context for that because of some shortcoming in the current middleware API).

public override async Task ValidateClientAuthentication
  (
OAuthValidateClientAuthenticationContext
context)

{

    // validate client credentials (demo)

    // should be stored securely (salted, hashed, iterated)

    string id, secret;

    if (context.TryGetBasicCredentials(out id, out secret))

    {

        if (secret == “secret”)

        {

            // need to make the client_id available for later security checks

            context.OwinContext.Set<string>(“as:client_id”, id);

            context.Validated();

        }

    }

}

We also need slight modifications to the GrantResourceOwnerCredentials method. In there we need to add some metadata to the resulting authentication ticket about the client. This will be used later when it comes to creating and persisting the refresh token.

public override async Task GrantResourceOwnerCredentials
  (
OAuthGrantResourceOwnerCredentialsContext
context)

{

    // validate user credentials (demo mode)

    // should be stored securely (salted, hashed, iterated)       

    if (context.UserName != context.Password)

    {

        context.Rejected();

        return;

    }

 

    // create identity

    var id = new ClaimsIdentity(“Embedded”);

    id.AddClaim(new Claim(“sub”, context.UserName));

    id.AddClaim(new Claim(“role”, “user”));

 

    // create metadata to pass on to refresh token provider

    var props = new AuthenticationProperties(new Dictionary<string, string>

        {

            { “as:client_id”, context.ClientId }

        });

 

    var ticket = new AuthenticationTicket(id, props);

    context.Validated(ticket);

}

At this point, the refresh token handler alongside the authentication ticket need to be persisted in some data store – – I’ll skip that for now and come back to it later.

There is one method you need to add to the authorization server provider. This method is called when the actual refresh token request comes in. In there you need to verify that the current client is actually the client that requested the token in the first place (the client binding). Here’s also your chance to modify the outgoing access token when you want to update claims etc..

public override async Task GrantRefreshToken(
 
OAuthGrantRefreshTokenContext
context)

{

    var originalClient = context.Ticket.Properties.Dictionary[“as:client_id”];

    var currentClient = context.OwinContext.Get<string>(“as:client_id”);

 

    // enforce client binding of refresh token

    if (originalClient != currentClient)

    {

        context.Rejected();

        return;

    }

 

    // chance to change authentication ticket for refresh token requests

    var newId = new ClaimsIdentity(context.Ticket.Identity);

    newId.AddClaim(new Claim(“newClaim”, “refreshToken”));

 

    var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties);

    context.Validated(newTicket);

}

The last missing piece is persistence – this is encapsulated in an IAuthenticationTokenProvider derived class. Here you need to create a secure handle for the refresh token and associate the authentication ticket with it to store it in some data store. Refresh token handles should be treated as secrets and should be stored hashed (I leave that as an exercise). Another security decision to make is if you want the refresh handle to be one-time use only, or if you re-use the same handle for that client/application combination. Most implementations I have seen so far seem to re-use the handle, my sample uses the one-time approach:

// sample persistence of refresh tokens

// this is not production ready!

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider

{

    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string,AuthenticationTicket>();

 

    public async Task CreateAsync(AuthenticationTokenCreateContext context)

    {

        var guid = Guid.NewGuid().ToString();

           

        // maybe only create a handle the first time, then re-use

        _refreshTokens.TryAdd(guid, context.Ticket);

 

        // consider storing only the hash of the handle

        context.SetToken(guid);

    }

 

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)

    {

        AuthenticationTicket ticket;

        if (_refreshTokens.TryRemove(context.Token, out ticket))

        {

            context.SetTicket(ticket);

        }

    }

}

You then register the token provider in startup (note the shorter access token lifetime):

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions

    {

        // for demo purposes

        AllowInsecureHttp = true,

 

        TokenEndpointPath = new PathString(“/token”),

        AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),

 

        Provider = new SimpleAuthorizationServerProvider(),

        RefreshTokenProvider = new SimpleRefreshTokenProvider()

    });

 

The Client
On the client side you now have to distinguish between three phases – requesting the initial access token (+ refresh token), using that token until it expires and requesting a new access token using the refresh token. You also need to pass in the client id and secret to all requests to the authorization server. Using the Thinktecture.IdentityModel.Client OAuth2Client this looks like this:

private static TokenResponse GetToken()

{

    var client = new OAuth2Client(

        new Uri(http://localhost:2727/token),

        “client1”,

        “secret”);

 

    var response = client.RequestResourceOwnerPasswordAsync(
     
“bob”, “bob”
).Result;

    return response;

}

 

private static TokenResponse RefreshToken(string refreshToken)

{

    var client = new OAuth2Client(

        new Uri(http://localhost:2727/token),

        “client1”,

        “secret”);

 

    var response = client.RequestRefreshTokenAsync(refreshToken).Result;

    return response;

}

 

Summary
This post turned out to be longer than I planned  – and that reminds me that there is indeed some complexity here, e.g.:

  • secure storage of user and client credentials as well as token handles
  • doing proper client binding for token handles
  • thinking about retention policies for handles (one-time vs. re-use)
  • persistence layer

And we haven’t even touched topics like refresh token revocation and the added complexity of scopes and supporting multiple OAuth2 flows.

As you’ve seen above this is all doable using Web API v2 built-in tech – that’s nice. Another option of course would be to use AuthorizationServer which has the above already built-in and much much more.

Full sample here.

This entry was posted in AuthorizationServer, IdentityModel, Katana, OAuth, OWIN, WebAPI. Bookmark the permalink.

38 Responses to Adding Refresh Tokens to a Web API v2 Authorization Server

  1. Thank you for clarifying some questions I had about the tokens using webApi2.
    I just read the 3 articles. They were great, thanks!
    Please continue to write about it!

    Could implement some code using the example of the template webapi, however using MongoDb to persist data.

    https://github.com/cleytonferrari/KarolCamp/tree/codigoRefatoradoNivel6-WebAPI2-MVC5/KarolCamp/KarolCamp.API

    I still have a lot to learn before you can pass this knowledge on to my students in college, so thank you for writing about it!

  2. Hi and thank you for the post
    You said :
    Once the token has been issued, there is no “built-in” way to revoke it. Or in other words you’d need to write your own mechanism for that which often involves database checks on each request.

    In web api 2 there is IUserSecurityStampStore.GetSecurityStampAsync method. Do this a good way to revoke the token (invalidate it ) ?

    • I think the purpose of this interface is a little different (i think it deals with password changes and revoking cookies). But basically you need to keep a list of revoked tokens and check that list periodically on incoming requests.

  3. Pingback: The Web API v2 OAuth2 Authorization Server Middleware–Is it worth it? | leastprivilege.com

  4. Hi, may I ask how the Client ID should be generated? I am having an phone app that users can log in by username and password to get access and refresh token.
    I saw that refresh token should have a client ID, but I am not sure if the client ID should be generated by the authentication server or the phone, or if it should just be the same for the same app.

    • Either it is the same for every app instance, or the app registers itself dynamically on the first start. Most people use the same id/secret for all instances.

      • Alistair says:

        When the access token is sent from the server to the client I am thinking of adding an additional header along the lines of:

        refresh-token-params: unique-client-id, unique-password

        The unique-client-id and unique-password are then tied to the refresh token on the server.

        To use the refresh token the client must send back the unique-client-id, unique-password and refresh token id to the server.

        Should the user log in from another machine they will get a new unique-client-id and unique-password. This way no refresh tokens are shared.

        In our app we are planning on using AngularJS so we don’t have any way of securing the clientid and password so generating a unique clientid and password gets round this problem (I think)!

        Is this a good idea?

      • Sorry – I would need to think more about it – and I don’t have the time right now. Feel free to post more thoughts on it.

  5. Tom says:

    Thanks for the excellent post.

    Is context.TryGetBasicCredentials using basic authentication to identity the client? Can you expand on why you decided to do that?

    Tom

    • It’s a sample ;) You have two options – either basic authentication or via form post values. Up to you.

      • tstojecki says:

        How would that work in a SPA app though, where the client secret cannot be securely stored and passed around?

      • A SPA would typically use implicit flow instead.

      • tstojecki says:

        I see, but that doesn’t support refresh tokens, correct? In that flow, the only way of persisting the login for longer is to increase the expiration of the access token, which isn’t considered a good practice either, right?

      • Well – a refresh token (in combination with either no or locally stored client creds) is in essence the same as a very long lived access token, right?

        The lifetime of the access token basically controls your security vs productivity experience.

        Google e.g. lets the use re-authenticate every 14 days.

  6. tstojecki says:

    Good point on the refresh token not being an ideal option either with no or locally stored client creds.
    So what I am hearing you say is that when it comes to SPAs none of the two (implicit grant or resource owner password grant) are ideal. Implicit grant won’t give you a refresh token. Resource owner will, but since you can’t identity the client reliably it won’t have the security benefits it usually provides.
    If that is the case, I would agree that you may as well go for implicit grant as it is less complex.
    Thanks for your input Dominick!

  7. Pingback: Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin - Bit of Technology

  8. Thanks Dominick for this post, I’ve blogged my own implementation including persisting refresh tokens and clients into database http://wp.me/p4nPYq-9u
    Will be great if you were able to review it.

    • Hi,

      I saw it – thanks for the mention.

      I don’t have the time right now to do a proper review – but looks good. Have you pointed out the issues of resource owner flow with refresh tokens and untrusted clients?

      • Thanks Dominick for your reply,
        Regarding the untrusted JS clients, I’m sending the client id for identification only, I tried to solve this issue by storing the “Allowed Origin” for this JS client in database, so when I validate the client information I will read the stored Allowed Origin for this client from database and use this value to set “Access-Control-Allow-Origin” header, so if the request is coming from different Origin then 405 response will be returned. I believe by doing this it is hard for another app to impersonate my app identity and fake the requests.
        Let me know if you have something in your mind regarding this.

      • As long as the client uses a browser ….

        You are essentially issuing a token with a *very* long lifetime to a client that does not authenticate and stores it in the context of a browser. As long as you are aware of that – that’s fine.

        Do you reuse the refresh token handle – or issue a new one every time the access token gets refreshed?

      • Hi Dominick,
        No I issue new refresh token identifier and delete the old one once a new access token gets refreshed. That is better than keeping using the same refresh token identifier, right?

      • BWR says:

        Do I have it right that the issue with refresh tokens in an untrusted client is that if the refresh token is compromised an attacker will have very long lived access?

        So the difference between storing an access token vs refresh token in an untrusted client is that if compromised, the access token has an expiration, while a refresh token would need to be revoked?

        If that’s correct, it’s the same problem with any sliding expiration scheme, correct? Doesn’t sliding cookie authentication have the same problem? Is the difference that cookies are less vulnerable to XSS?

    • A token compromise is always a bad thing. Depends on the implementation how bad it is. There can be long lived access token, long lived refresh tokens, sliding vs absolute etc.

      Refresh tokens were originally not designed to be used in client side applications – you can make them work but you need extra precautions.

  9. _pisees_ says:

    How can I do this with AngularJS calling Web API?

  10. Jason says:

    Dominick,

    Right now, my head is *swimming* with all of the security-related information I have tried to process throughout the last couple of weeks. This article, along with Taiseer’s (it looks like this article was the basis for his) has been the best information I have found to date. That said, thank you for your efforts.

    Here are some questions that I have after reading this article. (1) My understanding is that the token will be generated using the machine key in some fashion if hosted in IIS. Where can I override this? Generally speaking, what are the options for this? I am not saying that there is a need for this but rather I just like to know these types of things. (2) In a snippet of code in the article, it looks like a client id and secret is passed in order to obtain a token. Where does the secret originate from? How is it different than the id?

    Other than that, what other resources would you recommend to continue the learning process. I noticed that there is some content on Pluralsight that you have authored (which I have started looking at). The important thing (something which you appear to address) is that I want to keep things *simple* for now.

    Again, thank you for all of your guidance.

    • Check the Web API security and OAuth2 course – that will clarify things.

      Microsoft uses the Katana IDataProtector to create tokens by default – you could replace that in the OWIN environment.
      Client ID and secret are generated by the server when the client gets registered – kind of a bootstrapping.

  11. _pisees_ says:

    I see thanks Dominick. What OAuth flows make sense for an angular client? I am also investigating resource

  12. Rob Orchiston says:

    I have a client side call

    var response = client.RequestRefreshTokenAsync(persistedRefreshToken).Result;

    which calls into this Assembly Thinktecture.IdentityModel.Client.dll, v1.0.0.0
    and this method:
    public Task RequestRefreshTokenAsync(string refreshToken, Dictionary additionalValues = null);

    What I would expect to receive back in the .expiry field is the refreshToken expiry time which in my system is set to 2 weeks. This way my client can optimistically know whether the refresh token it has is likely still valid (unless it was revoked on the server side store). Then I can display to the client whether they are “logged in” or not based on the expiry time of the refresh token. However in the .expires field of the above call, I only get back the access token expiry time in seconds. Therefore the client can never know if the Refresh token it has persisted is likely to be valid or not. How can I get the refresh tokens expiry time on the client when I receive a new refresh token from the server??

    Thanks
    Rob

  13. smakus says:

    Dominick, I have found that in this example, the refresh token will have the same expiration time as the access_token (bearer token) in this case. Is that correct? I have instead added AuthenticationProperties to the refresh token CreateAsync method to extend the expiration, but I’m not sure if this is good form. Can you advise? Thanks…

    • You are right – and IIRC I updated the sample. Follow the link at the end.

      • derekgreer says:

        It would be helpful to update the example in the article too. When I first implemented this, I guess I had a longer token expiration time and was testing the code by forcing retrieval of the refresh token. All seemed well, but I came back to the code a few weeks later and was testing with an expire time of 30 seconds to see the app update the token, but it kept getting invalid_grant with no error message. I thought to myself, “what in the world is going on here. It was working the last time I messed with this.” I eventually figured out the refresh was keeping the original expiresUtc value.

  14. Hi Dominick,

    How to use refresh token in MVC Client ?

    Thank you.

  15. gs says:

    Hi Domnick,
    How to store Access Token issued by the Authorization Server. I see from the above post how to save Refresh Token.
    Thank You.

Leave a comment