OWIN Security - How to Implement OAuth2 Update Tokens

I am using the Web Api 2 template that comes with Visual Studio 2013, has some OWIN middleware for user authentication, etc.

In OAuthAuthorizationServerOptions I noticed that the OAuth2 server is configured to distribute tokens that expire after 14 days

  OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/api/token"), Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) , AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), AllowInsecureHttp = true }; 

This is not suitable for my last project. I would like to pass short-lived bearer_tokens that can be updated using refresh_token

I did a lot of searches and did not find anything useful.

So, how far I managed to get. Now I have reached the point "WTF do i now".

I wrote a RefreshTokenProvider that implements IAuthenticationTokenProvider according to the RefreshTokenProvider property in the OAuthAuthorizationServerOptions class:

  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(); _refreshTokens.TryAdd(guid, context.Ticket); // hash?? context.SetToken(guid); } public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { AuthenticationTicket ticket; if (_refreshTokens.TryRemove(context.Token, out ticket)) { context.SetTicket(ticket); } } public void Create(AuthenticationTokenCreateContext context) { throw new NotImplementedException(); } public void Receive(AuthenticationTokenReceiveContext context) { throw new NotImplementedException(); } } // Now in my Startup.Auth.cs OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/api/token"), Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) , AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(2), AllowInsecureHttp = true, RefreshTokenProvider = new RefreshTokenProvider() // This is my test }; 

So now, when someone asks for a bearer_token , I send a refresh_token , which is great.

So, how can I use this refresh_token to get a new bearer_token , apparently I need to send a request to the marker endpoint with specific HTTP headers?

Just thinking out loud when I type ... Should I handle expresh_token expiration in my SimpleRefreshTokenProvider ? How will the client receive a new refresh_token ?

I really could work with some reading materials / documentation because I don't want to be wrong and would like to follow the standard.

+54
c # asp.net-web-api asp.net-identity owin
Dec 17 '13 at 15:10
source share
4 answers

It just implements my OWIN service with media (called access_token in the following) and updates the tokens. My understanding of this is that you can use different threads. So it depends on the thread you want to use when you set the access_token and refresh_token expiration times.

I will describe two streams A and B in the text (I suggest that you want to have stream B):

A) the expiration times of access_token and refresh_token are the same as the default of 1200 seconds or 20 minutes. This thread requires that your client first send client_id and client_secret with login information in order to get access_token, refresh_token and expiration_time. With refresh_token you can now get a new access_token within 20 minutes (or whatever you set AccessTokenExpireTimeSpan to OAuthAuthorizationServerOptions). Due to the fact that the expiration times for access_token and refresh_token are the same, your client is responsible for receiving a new access_token before the expiration date! For example. your client can send an updated POST request to the endpoint of your token with the body (note: you must use https in the production process)

 grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxx 

to get a new token after, for example, 19 minutes, to prevent token skipping.

B) in this thread, you want to have a short-term expiration for your access_token and a long-term expiration for your refresh_token. Assume for testing purposes, you set the access_token parameter to expire after 10 seconds ( AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10) ) and refresh_token for up to 5 minutes. Now we are talking about the interesting part, setting the expiration time of refresh_token: you do this in your createAsync function in the SimpleRefreshTokenProvider class as follows:

 var guid = Guid.NewGuid().ToString(); //copy properties and set the desired lifetime of refresh token var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary) { IssuedUtc = context.Ticket.Properties.IssuedUtc, ExpiresUtc = DateTime.UtcNow.AddMinutes(5) //SET DATETIME to 5 Minutes //ExpiresUtc = DateTime.UtcNow.AddMonths(3) }; /*CREATE A NEW TICKET WITH EXPIRATION TIME OF 5 MINUTES *INCLUDING THE VALUES OF THE CONTEXT TICKET: SO ALL WE *DO HERE IS TO ADD THE PROPERTIES IssuedUtc and *ExpiredUtc to the TICKET*/ var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties); //saving the new refreshTokenTicket to a local var of Type ConcurrentDictionary<string,AuthenticationTicket> // consider storing only the hash of the handle RefreshTokens.TryAdd(guid, refreshTokenTicket); context.SetToken(guid); 

Now your client can send a POST call with refresh_token to the endpoint of your token when the access_token time is access_token . The main part of the call may look like this: grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xx

The important thing is that you can use this code not only in your CreateAsync function, but also in your Create function. Therefore, you should consider using your own function (e.g. CreateTokenInternal) for the above code. Here you can find implementations of different streams, including the refresh_token stream (but without setting the refresh_token update expiration time)

Below is one sample implementation of IAuthenticationTokenProvider on github (with setting refresh_token expiration time)

I'm sorry that I cannot help with additional materials than the OAuth specifications and Microsoft API documentation. I would post links here, but my reputation does not allow me to post more than two links ....

I hope this can help some others save time when trying to implement OAuth2.0 with refresh_token expiration time different from access_token expiration time. I could not find an implementation example on the Internet (other than the thinktecture link above), and it took me several hours to investigate until it worked for me.

New information: In my case, I have two different possibilities for receiving tokens. One of them is to get a valid access_token. There I have to send a POST call with the body of String in the format application / x-www-form-urlencoded with the following data

 client_id=YOURCLIENTID&grant_type=password&username=YOURUSERNAME&password=YOURPASSWORD 

Secondly, if the access_token is invalid, we can try refresh_token by sending a POST call with the body String in the format application/x-www-form-urlencoded with the following data grant_type=refresh_token&client_id=YOURCLIENTID&refresh_token=YOURREFRESHTOKENGUID

+62
Mar 08
source share

You need to implement RefreshTokenProvider . First create a class for RefreshTokenProvider, i.e.

 public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider { public override void Create(AuthenticationTokenCreateContext context) { // Expiration time in seconds int expire = 5*60; context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire)); context.SetToken(context.SerializeTicket()); } public override void Receive(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); } } 

Then add the instance to OAuthOptions .

 OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/authenticate"), Provider = new ApplicationOAuthProvider(), AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(expire), RefreshTokenProvider = new ApplicationRefreshTokenProvider() }; 
+30
May 17 '14 at 10:39
source share

I do not think you should use an array to store tokens. You do not need a pointer as a token.

You can easily use context.SerializeTicket ().

See my code below.

 public class RefreshTokenProvider : IAuthenticationTokenProvider { public async Task CreateAsync(AuthenticationTokenCreateContext context) { Create(context); } public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { Receive(context); } public void Create(AuthenticationTokenCreateContext context) { object inputs; context.OwinContext.Environment.TryGetValue("Microsoft.Owin.Form#collection", out inputs); var grantType = ((FormCollection)inputs)?.GetValues("grant_type"); var grant = grantType.FirstOrDefault(); if (grant == null || grant.Equals("refresh_token")) return; context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays); context.SetToken(context.SerializeTicket()); } public void Receive(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); if (context.Ticket == null) { context.Response.StatusCode = 400; context.Response.ContentType = "application/json"; context.Response.ReasonPhrase = "invalid token"; return; } if (context.Ticket.Properties.ExpiresUtc <= DateTime.UtcNow) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; context.Response.ReasonPhrase = "unauthorized"; return; } context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays); context.SetTicket(context.Ticket); } } 
+6
Apr 19 '16 at 4:56 on
source share

Freddy's answer helped me do this a lot. For completeness, here you can implement token hashing:

 private string ComputeHash(Guid input) { byte[] source = input.ToByteArray(); var encoder = new SHA256Managed(); byte[] encoded = encoder.ComputeHash(source); return Convert.ToBase64String(encoded); } 

In CreateAsync :

 var guid = Guid.NewGuid(); ... _refreshTokens.TryAdd(ComputeHash(guid), refreshTokenTicket); context.SetToken(guid.ToString()); 

ReceiveAsync :

 public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { Guid token; if (Guid.TryParse(context.Token, out token)) { AuthenticationTicket ticket; if (_refreshTokens.TryRemove(ComputeHash(token), out ticket)) { context.SetTicket(ticket); } } } 
+1
Jan 06 '16 at 9:35
source share



All Articles