Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with long bearer tokens from webapi by providing a surrogate token

I am building a web api using ASP.NET WebApi 2 using claims authentication, and my users can have very large number of claims. With a large number of claims the bearer token grows very large quickly, so I am attempting to find a way of returning a much shorter bearer token.

SO far I have discovered that I can provide a IAuthenticationTokenProvider to the OAuth options OAuthAuthorizationServerOptions.AccessTokenProvider property:

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromHours(12),
    AccessTokenProvider = new GuidProvider() // <-- here
};

And this gives me a chance to intercept the AuthenticationTicket and stash it away, replacing it with something simpler - in my example below a hashed guid. (Note: At the moment this class simply holds a ConcurrentDictionary<string,AuthenticationTicket> with my sessions - in a real-world example I intend to store the sessions in some persistent storage)

public class GuidProvider : IAuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> tokens 
        = new ConcurrentDictionary<string, AuthenticationTicket>();

    public void Create(AuthenticationTokenCreateContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();

        var ticket = Crypto.Hash(guid);

        tokens.TryAdd(ticket, context.Ticket);

        context.SetToken(ticket);
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        AuthenticationTicket ticket;

        if (tokens.TryGetValue(context.Token, out ticket))
        {
            if (ticket.Properties.ExpiresUtc.Value < DateTime.UtcNow)
            {
                tokens.TryRemove(context.Token, out ticket);
            }
            context.SetTicket(ticket);
        }
    }
}

So my questions:

  • Is this an appropriate (and secure!) way of providing a surrogate key in place of my long claims-generated token?
  • Is there perhaps a better/easier place where I should be doing this within the webapi/OAuth stack?

Another thing to note is that I intend to support refresh tokens, and in fact the example above was pulled from examples which use this sort of mechanism for the Refresh token - except with a refresh token they appear to be single-use, so the ReceiveAsync method would usually always remove the refresh token supplied from the ConcurrentDictionary, I'm not entirely sure I understand why?

like image 346
Jamiec Avatar asked Dec 08 '14 15:12

Jamiec


People also ask

How do I pass a bearer token in API?

Bearer tokens enable requests to authenticate using an access key, such as a JSON Web Token (JWT). The token is a text string, included in the request header. In the request Authorization tab, select Bearer Token from the Type dropdown list. In the Token field, enter your API key value.

How do you handle API tokens?

Here are some basic considerations to keep in mind when using tokens: Keep it secret. Keep it safe: The signing key should be treated like any other credential and revealed only to services that need it. Do not add sensitive data to the payload: Tokens are signed to protect against manipulation and are easily decoded.

How long are bearer tokens good for?

A valid bearer token (with active access_token or refresh_token properties) keeps the user's authentication alive without requiring him or her to re-enter their credentials frequently. The access_token can be used for as long as it's active, which is up to one hour after login or renewal.


1 Answers

I do not recommend to do this because you are eventually going to store the authentication tickets into the database or Redis server, the draw back here that with each request containing a bearer token, you are going to check this permanent store in order to resolve the Guid and get the ticket again to construct it.

I suggest that you use JSON Web Token JWT instead of the default bearer access tokens format, to do this you need implement your custom access token format CustomOAuthProvider in property Provider in OAuthAuthorizationServerOptions as the code below:

 OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth2/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat("http://jwtauthzsrv.azurewebsites.net")
        };

I've noticed that adding more claims to the JWT token won't increase its size dramatically as the case of default access token format.

Below a sample of 2 JWTs with different claims inside each one, the second one is larger than the first by only 50 chars. I recommend you to check the encoded content of each one using jwt.io First JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzMyNywibmJmIjoxNDE4NjQ1NTI3fQ.vH9XPtjtAv2-6SwlyX4fKNJfm5ZTVHd_9a3bRgkA_LI

Second JWT (More claims):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciIsIlN1cGVydmlzb3IxIiwiU3VwZXJ2aXNvcjIiLCJTdXBlcnZpc29yMyJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzQ1NiwibmJmIjoxNDE4NjQ1NjU2fQ.TFEGDtz1RN8VmCQu7JH4Iug0B8UlWDLVrIlvc-7IK3E

The JWT format is becoming the standard way to issue OAuth 2.0 bearer tokens, as well it will work with refresh token grant. But keep in mind that JWT is only signed tokens and not encrypted as the case in default access token format, so do not store confidential data in.

I've written detailed blog post on bitoftech.net on how to use JWT tokens in ASP.NET Web API along with a live demo API and source code on GIthub, feel free to check it and let me know if you need more help.

Good luck!

like image 85
Taiseer Joudeh Avatar answered Sep 30 '22 13:09

Taiseer Joudeh