Asked by:
OAuth 2 refresh token invalid_grant

Question
-
User749047830 posted
Hello,
I am attempting to implement refresh tokens but my refresh request always ends up as HTTP 400 invalid_grant. This is a webapi project using OWIN and OAUTH2.
Any ideas what might be the cause?
/// <summary> /// Creates a refresh token /// </summary> /// <param name="context"> /// The authentication context /// </param> /// <returns> /// The new refresh token /// </returns> /// <exception cref="OverflowException"> /// The dictionary already contains a refresh token ticket. /// </exception> public async Task CreateAsync(AuthenticationTokenCreateContext context) { using (var ctx = new MpsDbContext()) { var refreshToken = new RefreshToken { RefreshTokenId = Guid.NewGuid(), Expires = DateTime.UtcNow.AddMinutes(5), Issued = DateTime.UtcNow, ProtectedTicket = context.SerializeTicket(), Subject = context.Ticket.Identity.Name }; context.Ticket.Properties.IssuedUtc = refreshToken.Issued; context.Ticket.Properties.ExpiresUtc = refreshToken.Expires; ctx.RefreshTokens.Add(refreshToken); await ctx.SaveChangesAsync(); context.SetToken(refreshToken.RefreshTokenId.ToString()); } } /// <summary> /// Validates a refresh token /// </summary> /// <param name="context"> /// The authentication context /// </param> /// <returns> /// Nothing is returned /// </returns> public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { using (var ctx = new MpsDbContext()) { var clientRefreshToken = Guid.Parse(context.Token); var result = from n in ctx.RefreshTokens where n.RefreshTokenId.Equals(clientRefreshToken) select n; var refreshToken = result.First(); if (refreshToken != null) { context.DeserializeTicket(refreshToken.ProtectedTicket); ctx.RefreshTokens.Remove(refreshToken); await ctx.SaveChangesAsync(); } } } /// <summary> /// Create a refresh token /// </summary> /// <param name="context"> /// The request context. /// </param> /// <exception cref="NotImplementedException"> /// Synchronous operation not allowed /// </exception> public void Create(AuthenticationTokenCreateContext context) { throw new NotImplementedException(); } /// <summary> /// Validate a refresh token /// </summary> /// <param name="context"> /// The request context. /// </param> /// <exception cref="NotImplementedException"> /// Synchronous operation not allowed /// </exception> public void Receive(AuthenticationTokenReceiveContext context) { throw new NotImplementedException(); }
public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context) { var newId = new ClaimsIdentity(context.Ticket.Identity); var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties); context.Validated(newTicket); }
refreshToken = function () { var postData = { "grant_type": "refresh_token", "refresh_token": sessionStorage.getItem("MpsRefreshToken") }; $.ajax( { url: "token", type: "POST", crossDomain: false, data: postData, headers: { "Authorization": sessionStorage.getItem("MpsAuthToken") }, success: function (data, textStatus, jqXHR) { sessionStorage.clear(); sessionStorage.setItem("MpsAuthToken", "Bearer " + data.access_token); sessionStorage.setItem("MpsRefreshToken", data.refresh_token); sessionStorage.setItem("MpsUserName", data.userName); sessionStorage.setItem("MpsUserId", data.userId); sessionStorage.setItem("MpsUserEmail", data.userEmail); sessionStorage.setItem("TokenExpires", data[".expires"]); sessionStorage.setItem("TokenLifeSpan", data.expires_in); tokenLifeSpan = parseInt(sessionStorage.getItem("TokenLifeSpan")); timer = window.setInterval(checkTokenExpired, 30000); }, error: function (jqXHR, textStatus, errorThrown) { window.alert("error"); } }); }
POST http://localhost/mps/token HTTP/1.1 Accept: */* Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Authorization: Bearer hNlwBHqUbKYPRz41rriwGKG9x6G5MD-uCWX5ftYTM8P-8MeQ7qXB7xtn0KeIVKBP3jUDMQVenB4LgBMOkrq1Cn9bkt_jIqQv-rUEr8eyJ67kxxQ5kHKMbzcsjl4jgeyIEJxQe9efhFf2LoZ9pPIdbfkKfKG4BUUMpvRmHHbzju-X20Ok9CzEDSE-KPKv2i9vpsuI9391uprHFkluzP-58jL8GNH3al7KkgYRCb9syANsCq6MY1s5Hhj0UrYwIOPP5tNs2MO0wNVOg86uH1qxBl1zof24aF1TQzJ6KCRHKyxFmyUj3aEd0HTAsyvHAUx-KAi7miwMyzfzkSc7nodlYwSeXPLd2FMQffQMQwUOdGZo_ol03cib294--yg5WOWCGEqWCIHcrl6WauJ9_e4MGudD3VzGqmV1VeuPJV9lHGZ-KiyepnVVbd0IemMCTQBg5dbNgaUQa6Hab3IWev_J9O9gs01UrhMuqTfyuNZ6wv7S1_477MMC_OfkKLqykS6ZMRhqoOzyb2afMwWbxM7pYCpxHO20-8ys9sKCvhVQLFW4LnPdmYvuaMgJMLWxNUS2oNggh-Ro3jLAYfxiElrjNN0fvDK7ouOO16wv-bxgSzoVwCwPdK0XW6Ejj9pjc-QoJb0HBnsXHyURyYxEgK0KTPesCCs X-Requested-With: XMLHttpRequest Referer: http://localhost/mps/default.htm Accept-Language: en-GB,en;q=0.5 Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko Connection: Keep-Alive Content-Length: 75 DNT: 1 Host: localhost Pragma: no-cache grant_type=refresh_token&refresh_token=03f325d3-3635-e511-8266-4c8d79e0337d
Tuesday, July 28, 2015 10:45 AM
All replies
-
User-84896714 posted
Hi Lambros,
Thank you for your post. How did you config your OAuthOptions. Did you enable InsecureHttp?
OAuthOptions = new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, };
Best Regards,
Wang LiTuesday, July 28, 2015 11:17 PM -
User749047830 posted
Hi yes allowinsecurehttp is set to true for now. Does katana expect SSL encrypted for this?
Wednesday, July 29, 2015 2:07 AM -
User-84896714 posted
Hi Lambros,
The default value of allowinsecurehttp is false, it prevent http request.
Best Regards,
Wang LiWednesday, July 29, 2015 2:24 AM -
User749047830 posted
Just gave it a go with HTTPS configured but I still get HTTP400: BAD REQUEST - The request could not be processed by the server due to invalid syntax.
(XHR): POSTAny ideas about how this request should look like?
Wednesday, July 29, 2015 2:40 AM -
User24670 posted
Hi,
HTTP 400 is a very common error code. It' doesn't tell much information how this error happened. You also need to check the sub-status code and closely look into the problem. You can refer to https://support.microsoft.com/en-us/kb/943891
IIS 7.0, IIS 7.5, and IIS 8.0 define the following HTTP status codes that indicate a more specific cause of a 400 error:
- 400.1 - Invalid Destination Header.
- 400.2 - Invalid Depth Header.
- 400.3 - Invalid If Header.
- 400.4 - Invalid Overwrite Header.
- 400.5 - Invalid Translate Header.
- 400.6 - Invalid Request Body.
- 400.7 - Invalid Content Length.
- 400.8 - Invalid Timeout.
- 400.9 - Invalid Lock Token.
To troubleshooting 400 bad request, you can refer to this link : http://blogs.msdn.com/b/webtopics/archive/2009/01/29/how-to-troubleshoot-http-400-errors.aspx
Thursday, July 30, 2015 9:31 AM -
User749047830 posted
That's the least helpful reply ever
Friday, August 7, 2015 2:41 PM -
User-987317448 posted
As is usually the case Microsoft forums, worthless. Would be even better if MS would actually provide complete working examples of how to use this particular OAuth library, they have one but it doesn't even demonstrate how the refresh token flow is supposed to work.
Thursday, January 7, 2016 10:01 PM -
User1574186686 posted
Check the refresh token expiry date you are getting from the repo in the RecieveAsync call is in the future.
You'll get a 400 invalid_grant error if you try and use an expired ticketFriday, January 22, 2016 9:35 AM -
User-281203229 posted
The problem is that you are doing the
context.SerializeTicket()
before setting the
context.Ticket.Properties.IssuedUtc = refreshToken.Issued; context.Ticket.Properties.ExpiresUtc = refreshToken.Expires;
Only do the context.SerializeTicket() after setting the ticket parameters.
Friday, February 19, 2016 1:42 AM -
User749047830 posted
Thanks for your reply, however, this did not help me I am still facing the HTTP 400
Saturday, April 2, 2016 12:51 AM -
User749047830 posted
I now have token refresh working, however, the server still throws me out on the next request. This is the most frustrating framework ever, since the middleware is a complete black box with poor to no documentation. Is there an example somewhere where I can use plain old cookies? I think I will go back to ASP.NET Forms Authenticaiton, at least that worked:P
Also please don't suggest I use ASP.NET Identity 3.0 or EF 7 I am weeks from go live and I don't want to use beta software and EF7 doesn't support base classes on models.
Tuesday, April 5, 2016 11:24 AM