Owin Security - How to Implement Oauth2 Refresh Tokens

OWIN Security - How to Implement OAuth2 Refresh Tokens

Just implemented my OWIN Service with Bearer (called access_token in the following) and Refresh Tokens. My insight into this is that you can use different flows. So it depends on the flow you want to use how you set your access_token and refresh_token expiration times.

I will describe two flows A and B in the follwing (I suggest what you want to have is flow B):

A) expiration time of access_token and refresh_token are the same as it is per default 1200 seconds or 20 minutes. This flow needs your client first to send client_id and client_secret with login data to get an access_token, refresh_token and expiration_time. With the refresh_token it is now possible to get a new access_token for 20 minutes (or whatever you set the AccessTokenExpireTimeSpan in the OAuthAuthorizationServerOptions to). For the reason that the expiration time of access_token and refresh_token are the same, your client is responsible to get a new access_token before the expiration time! E.g. your client could send a refresh POST call to your token endpoint with the body (remark: you should use https in production)

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

to get a new token after e.g. 19 minutes to prevent the tokens from expiration.

B) in this flow you want to have a short term expiration for your access_token and a long term expiration for your refresh_token. Lets assume for test purpose you set the access_token to expire in 10 seconds (AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10)) and the refresh_token to 5 Minutes. Now it comes to the interesting part setting the expiration time of refresh_token: You do this in your createAsync function in SimpleRefreshTokenProvider class like this:

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 is able to send a POST call with a refresh_token to your token endpoint when the access_token is expired. The body part of the call may look like this: grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xx

One important thing is that you may want to use this code not only in your CreateAsync function but also in your Create function. So you should consider to use your own function (e.g. called CreateTokenInternal) for the above code.
Here you can find implementations of different flows including refresh_token flow(but without setting the expiration time of the refresh_token)

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

I am sorry that I can't help out with further materials than the OAuth Specs and the Microsoft API Documentation. I would post the links here but my reputation doesn't let me post more than 2 links....

I hope this may help some others to spare time when trying to implement OAuth2.0 with refresh_token expiration time different to access_token expiration time. I couldn't find an example implementation on the web (except the one of thinktecture linked above) and it took me some hours of investigation until it worked for me.

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

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

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

OWIN Security - OAuth2 Refresh Token - How to include Refresh Token's expiration

I created a DTO class for the refresh token to be serialized and returned in place of the refreshTokenId variable.

public class RefreshTokenDTO
{
[JsonProperty("token")]
public string Token { get; set; }

[JsonProperty("issued")]
public DateTime Issued { get; set; }

[JsonProperty("expires")]
public DateTime Expires { get; set; }
}

and changed the SimpleRefreshTokenProvider.CreateAsync method

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
...

using (AuthRepository repo = new AuthRepository())
{
...

if (result)
{
//context.SetToken(refreshTokenId);

var rToken = new RefreshTokenDTO
{
Token = refreshTokenId,
Issued = token.IssuedUtc,
Expires = token.ExpiresUtc
};

var json = JsonConvert.SerializeObject(rToken);

context.SetToken(json);
}
}
}

The new response is

{
"access_token": "dRJiWTT03KlapjqENhDeIa-f35rE4eRDn6DL60laVeKysUQkOHE2Zu6ySYYFmq53jN5KQL3A7Aj6obM3oe5iYtHbfeueODcMitzGEBXMph1-791v86VWgdvW4EtvIbhQnLq8Acr6K_Nt5qDQWTCD5DETjr6h0OenbZtDIQak3ycUUPEU5m1Ws3b2qZbw62-DSUzaOZ2TYhvdkRdFg_zWhLIDd9vIWiGXcWxTr415P5a7d1s_K-8vmq5q-I5nEUCmJshmCWIU_4oPKz7sQLHhy79JE9Z00BfdidzFbYaA9yo",
"token_type": "bearer",
"expires_in": 1799,
"refresh_token": "{\"Token\":\"f94f5ba1494644e388f0ec4862a81909\",\"Issued\":\"2017-02-22T17:48:03.771661Z\",\"Expires\":\"2017-03-04T17:48:03.771661Z\"}",
"as:client_id": "epAndroid",
"as:issued": "",
"userName": "pm00115905",
".issued": "Wed, 22 Feb 2017 17:48:02 GMT",
".expires": "Wed, 22 Feb 2017 18:18:02 GMT"
}

and on the client side I created a JsonConverter

public class RefreshTokenJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject<RefreshToken>(reader.Value.ToString());
}

public override bool CanConvert(Type objectType)
{
return true;
}
}

Asp.net OWIN Identity refresh tokens and token expiration

Sending the refresh token back in the token response seems un-safe? If
a man in the middle were to intercept, they have everything they need
to request new tokens.

SSL

Granting refresh tokens is commonly tied to validating a Client ID and
Client Secret. As far as I know, it is not possible to safely store
these in a JS SPA.

You are absolutely correct here. ClientSecret cannot be kept on the client. So you could simply have a server side endpoint that will return you a new access token from the current user session. The idea here is to keep the refresh token on the server and when the client needs a new access token it would request this endpoint. Obviously the client will need to be authenticated which could be achieved by common ASP.NET authentication.

How to implement OAuth 2.0 Authorization Code Grant from Single-Page Application?

I ended up using a HTML form and sending the current access token in the form body. I created a hidden input in the form for the access token and only set its value when the form was being submitted (to reduce the chance of it being seen by the user or a malicious script/extension).

On the server side, our OAuth code was previously only looking for the access token in the Authorization header; I changed it to also look in the request form (IOwinRequest.ReadFormAsync) for authorization code requests.

The OAuth code now sends a redirect response to a successful authorization code request instead of sending a JSON response with the redirect URI.

How to update Owin access tokens with refresh tokens without creating new refresh token?

Since no one has answered yet i'm going to provide what i did and which is doing what i was looking for. Therefore I'm going to accept this answer for now.

public class Startup
{
public static void Configuration(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(
new OAuthBearerAuthenticationOptions());

app.UseOAuthAuthorizationServer(
new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new OAuthAuthorizationServerProvider()
{
OnValidateClientAuthentication = async c =>
{
c.Validated();
},
OnGrantResourceOwnerCredentials = async c =>
{
//Add a string with the current date
string dateNow = DateTime.UtcNow.ToString();

if (c.UserName == "alice" && c.Password == "supersecret")
{
Claim claim1 = new Claim(ClaimTypes.Name, c.UserName);
Claim[] claims = new Claim[] { claim1 };
ClaimsIdentity claimsIdentity =
new ClaimsIdentity(
claims, OAuthDefaults.AuthenticationType);

//Add a claim with the creationdate of the token
claimsIdentity.AddClaim(new Claim("creationDate", dateNow));

c.Validated(claimsIdentity);
}
}
},
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(40),
AllowInsecureHttp = true,
RefreshTokenProvider = new ApplicationRefreshTokenProvider()
});
}
}

And in the ApplicationRefreshTokenProvider i made theese changes

public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider
{
public override void Create(AuthenticationTokenCreateContext context)
{
//Get the claim which holds creation date
DateTime creationDate = Convert.ToDateTime(clientid.Claims.Where(c => c.Type == "creationDate").Single().Value);
//Create a variable holding current time minus 30 seconds(This is how long time you can create new refresh tokens by providing your original refresh token)
DateTime now = DateTime.UtcNow.AddSeconds(-30);

//If the time has passed more than 30 seconds from the time you got your original access and refresh token by providing credentials
//you may not create and return new refresh tokens(Obviously the 30 seconds could be changed to something less or more aswell)
if(now < ceationDate)
{
// Expiration time in seconds
int expire = 2 * 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);
}
}


Related Topics



Leave a reply



Submit