How to create Refresh Token with External Login Provider? How to create Refresh Token with External Login Provider? asp.net asp.net

How to create Refresh Token with External Login Provider?


I spent a lot of time to find the answer to this question. So, i'm happy to help you.

1) Change your ExternalLogin method.It usually looks like:

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, actually, it is necessary to add refresh_token. 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 to use basic context.SerializeTicket in SimpleRefreshTokenProvider CreateAsync method. Message from Bit Of Technology

Seems in the ReceiveAsync method, the context.DeserializeTicket is not returning an Authentication Ticket at all in the external login case. When I look at the context.Ticket property after that call it’s null. Comparing that to the local login flow, the DeserializeTicket method sets the context.Ticket property to an AuthenticationTicket. So the mystery now is how come the DeserializeTicket behaves differently in the two flows. The protected ticket string in the database is created in the same CreateAsync method, differing only in that I call that method manually in the GenerateLocalAccessTokenResponse, vs. the Owin middlware calling it normally… And neither SerializeTicket or DeserializeTicket throw an error…

So, you need to use Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer to searizize and deserialize ticket. It will be 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 of:

token.ProtectedTicket = context.SerializeTicket();

And for 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 of:

context.DeserializeTicket(refreshToken.ProtectedTicket);

3) Now you need to add refresh_token to ExternalLogin method response.Override AuthorizationEndpointResponse in your OAuthAuthorizationServerProvider. Something like this:

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.. thats all! Now, after calling ExternalLogin method, you get 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)


@giraffe and others offcourse

A few remarks. There's no need to use the custom tickerserializer.

The following line:

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

As tokenformat: Startup.OAuthOptions.AccessTokenFormat is used. Since we want to provide a refeshtoken this needs te be changed to: Startup.OAuthOptions.RefreshTokenFormat

Otherwise if you want to get a new accesstoken and refresh the refreshtoken ( grant_type=refresh_token&refresh_token=...... ) the deserializer/unprotector will fail. Since it uses the wrong purposes keywords at the decrypt stage.


Finally found the solution for my problem.First of all, if you EVER encounter any problems with OWIN and you cannot figure out what is going wrong, I advise you to simply enable symbol-debugging and debug it. A great explanation can be found here:http://www.symbolsource.org/Public/Home/VisualStudio

My mistake simply was, that I was calculating a wrong ExiresUtc when using external login providers. So my refreshtoken basically was always expired right away....

If you are implementing refresh tokens, then look at this gread blog article:http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

And to make it work with refresh tokens for external providers, you have to set the two requried parameters ("as:clientAllowedOrigin" and "as:clientRefreshTokenLifeTime") on the contextso 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 the 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);    }