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:
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?
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.
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.
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With