How to create an update token using an external provider?

I searched through the Internet and could not find a solution to my problem. I am using OAuth in my application. I am using ASP.NET Web API 2 and Owin. The scenario is that after a user requests a token endpoint, he or she will receive an access token along with an update token to create a new access token. I have a class that helps me generate an update token. There he is:

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider { private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>(); public async Task CreateAsync(AuthenticationTokenCreateContext context) { var refreshTokenId = Guid.NewGuid().ToString("n"); using (AuthRepository _repo = new AuthRepository()) { var refreshTokenLifeTime = context.OwinContext.Get<string> ("as:clientRefreshTokenLifeTime"); var token = new RefreshToken() { Id = Helper.GetHash(refreshTokenId), ClientId = clientid, Subject = context.Ticket.Identity.Name, IssuedUtc = DateTime.UtcNow, ExpiresUtc = DateTime.UtcNow.AddMinutes(15) }; context.Ticket.Properties.IssuedUtc = token.IssuedUtc; context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc; token.ProtectedTicket = context.SerializeTicket(); var result = await _repo.AddRefreshToken(token); if (result) { context.SetToken(refreshTokenId); } } } // this method will be used to generate Access Token using the Refresh Token public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { string hashedTokenId = Helper.GetHash(context.Token); using (AuthRepository _repo = new AuthRepository()) { var refreshToken = await _repo.FindRefreshToken(hashedTokenId); if (refreshToken != null ) { //Get protectedTicket from refreshToken class context.DeserializeTicket(refreshToken.ProtectedTicket); // one refresh token per user and client var result = await _repo.RemoveRefreshToken(hashedTokenId); } } } public void Create(AuthenticationTokenCreateContext context) { throw new NotImplementedException(); } public void Receive(AuthenticationTokenReceiveContext context) { throw new NotImplementedException(); } } 

I now allow my users to register via facebook. As soon as the user logs into facebook, I create an access token and pass it to him. Should I generate an update token? Something comes to my mind is to create a long access token, like in one day, and then this user must again log in with facebook. But if I do not want to do this, I can provide the client with an update token, and he can use it to update the created access token and get a new one. How to create an update token and attach it to the answer when someone signs up or logs in using facebook or from the outside?

Here is my external registration API

  public class AccountController : ApiController { [AllowAnonymous] [Route("RegisterExternal")] public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var accessTokenResponse = GenerateLocalAccessTokenResponse(model.UserName); return Ok(accessTokenResponse); } } 

// Private method for creating an access token

 private JObject GenerateLocalAccessTokenResponse(string userName) { var tokenExpiration = TimeSpan.FromDays(1); ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType); identity.AddClaim(new Claim(ClaimTypes.Name, userName)); identity.AddClaim(new Claim("role", "user")); var props = new AuthenticationProperties() { IssuedUtc = DateTime.UtcNow, ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration), }; var ticket = new AuthenticationTicket(identity, props); var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); JObject tokenResponse = new JObject( new JProperty("userName", userName), new JProperty("access_token", accessToken), // Here is what I need new JProperty("resfresh_token", GetRefreshToken()), new JProperty("token_type", "bearer"), new JProperty("refresh_token",refreshToken), new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()), new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()), new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString()) ); return tokenResponse; } 
+16
oauth
Sep 09 '14 at 8:15
source share
3 answers

I spent a lot of time to find the answer to this question. Therefore, I am happy to help you.

1) Change the ExternalLogin method. Usually it looks like this:

 if (hasRegistered) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager, OAuthDefaults.AuthenticationType); ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager, CookieAuthenticationDefaults.AuthenticationType); AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName); Authentication.SignIn(properties, oAuthIdentity, cookieIdentity); } 

Now, in fact, you need to add refresh_token. The method will look like this:

 if (hasRegistered) { Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager, OAuthDefaults.AuthenticationType); ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager, CookieAuthenticationDefaults.AuthenticationType); AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName); // ADD THIS PART var ticket = new AuthenticationTicket(oAuthIdentity, properties); var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket); Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext( Request.GetOwinContext(), Startup.OAuthOptions.AccessTokenFormat, ticket); await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context); properties.Dictionary.Add("refresh_token", context.Token); Authentication.SignIn(properties, oAuthIdentity, cookieIdentity); } 

Now the refrehs token will be generated.

2) There is a problem of using the underlying context. Serialize Ticket in SimpleRefreshTokenProvider CreateAsync. Message from Bit Technology

It seems that in the ReceiveAsync method the .DeserializeTicket context is not returning an authentication ticket at all in the external login case. When I look at the context.Ticket property, then we call its null value. Comparing this to a local login, the DeserializeTicket method sets the context.Ticket property to AuthenticationTicket. So the secret is now how it happens DeserializeTicket behaves differently in two streams. A protected ticket string has been created in the database in the same CreateAsync method, which differs only in that I call this method manually in GenerateLocalAccessTokenResponse, against Owin middlware, calling it normal ... And neither SerializeTicket nor DeserializeTicket gives an error ...

So, you need to use Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer to search and deserialize the ticket. It will look like this:

 Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer = new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer(); token.ProtectedTicket = System.Text.Encoding.Default.GetString(serializer.Serialize(context.Ticket)); 

instead:

 token.ProtectedTicket = context.SerializeTicket(); 

And for the ReceiveAsync method:

 Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer = new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer(); context.SetTicket(serializer.Deserialize(System.Text.Encoding.Default.GetBytes(refreshToken.ProtectedTicket))); 

instead:

 context.DeserializeTicket(refreshToken.ProtectedTicket); 

3) Now you need to add refresh_token to the response of the ExternalLogin method. Override AuthorizationEndpointResponse in OAuthAuthorizationServerProvider. Something like that:

 public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context) { var refreshToken = context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["refresh_token"]; if (!string.IsNullOrEmpty(refreshToken)) { context.AdditionalResponseParameters.Add("refresh_token", refreshToken); } return base.AuthorizationEndpointResponse(context); } 

So ... that’s it! Now, by calling the ExternalLogin method, you get the URL: https: // localhost: 44301 / Account / ExternalLoginCallback? Access_token = ACCESS_TOKEN & token_type = bearer & expires_in = 300 & state = STATE & refresh_token = TICKET & returnUrl = URL

I hope this helps)

+20
Jul 09 '15 at 12:53 on
source share

@giraffe and others offcourse

A few comments. There is no need to use a custom tickerserializer.

Next line:

 Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext( Request.GetOwinContext(), Startup.OAuthOptions.AccessTokenFormat, ticket); 

How token format is used: Startup.OAuthOptions.AccessTokenFormat . Since we want to provide a refeshtoken, this needs to be changed as follows: Startup.OAuthOptions.RefreshTokenFormat

Otherwise, if you want to get a new accesstoken and update refreshtoken (grant_type = refresh_token & refresh_token = ......), the deserializer / unprotector will fail. Since it uses incorrect whole keywords at the decryption stage.

+5
Nov 30 '15 at
source share

Finally found a solution to my problem. First of all, if you ALWAYS encounter any problems with OWIN, and you cannot understand what is going wrong, I advise you to simply enable character rendering and debug it. A great explanation can be found here: http://www.symbolsource.org/Public/Home/VisualStudio

My mistake was simply that I calculated the wrong ExiresUtc when using external input providers. So my refreshtoken basically always expired right away.

If you use update tokens, look at the article on this blog: http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2- owin /

To make it work with update tokens for external providers, you need to set two parameters ("like: clientAllowedOrigin" and "as: clientRefreshTokenLifeTime") in context so instead of

  var ticket = new AuthenticationTicket (oAuthIdentity, properties);
 var context = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext (
                     Request.GetOwinContext (), 
                     Startup.OAuthOptions.AccessTokenFormat, ticket);

  await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync (context);
  properties.Dictionary.Add ("refresh_token", context.Token);

you need to get the client first and set context parameters

     // retrieve client from database
     var client = authRepository.FindClient (client_id);
     // only generate refresh token if client is registered
     if (client! = null)
     {
         var ticket = new AuthenticationTicket (oAuthIdentity, properties);
         var context = new AuthenticationTokenCreateContext (Request.GetOwinContext (), AuthConfig.OAuthOptions.RefreshTokenFormat, ticket);
         // Set this two context parameters or it won't work !!
         context.OwinContext.Set ("as: clientAllowedOrigin", client.AllowedOrigin);
         context.OwinContext.Set ("as: clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString ());

         await AuthConfig.OAuthOptions.RefreshTokenProvider.CreateAsync (context);
         properties.Dictionary.Add ("refresh_token", context.Token);
     }

+4
Dec 30 '15 at 13:21
source share



All Articles