locked
OAuth 2 refresh token invalid_grant RRS feed

  • 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 Li

    Tuesday, 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 Li

    Wednesday, 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): POST

    Any 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 ticket

    Friday, 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