Getting "error": "unsupported_grant_type" when trying to get a JWT by calling an OWIN OAuth secured Web Api via Postman

159,933

Solution 1

The response is a bit late - but in case anyone has the issue in the future...

From the screenshot above - it seems that you are adding the url data (username, password, grant_type) to the header and not to the body element.

Clicking on the body tab, and then select "x-www-form-urlencoded" radio button, there should be a key-value list below that where you can enter the request data

Solution 2

With Postman, select Body tab and choose the raw option and type the following:

grant_type=password&username=yourusername&password=yourpassword

Solution 3

  1. Note the URL: localhost:55828/token (not localhost:55828/API/token)
  2. Note the request data. Its not in json format, its just plain data without double quotes. [email protected]&password=Test123$&grant_type=password
  3. Note the content type. Content-Type: 'application/x-www-form-urlencoded' (not Content-Type: 'application/json')
  4. When you use JavaScript to make post request, you may use following:

    $http.post("localhost:55828/token", 
      "userName=" + encodeURIComponent(email) +
        "&password=" + encodeURIComponent(password) +
        "&grant_type=password",
      {headers: { 'Content-Type': 'application/x-www-form-urlencoded' }}
    ).success(function (data) {//...
    

See screenshots below from Postman:

Postman Request

Postman Request Header

Solution 4

If you are using AngularJS you need to pass the body params as string:

    factory.getToken = function(person_username) {
    console.log('Getting DI Token');
    var url = diUrl + "/token";

    return $http({
        method: 'POST',
        url: url,
        data: 'grant_type=password&[email protected]&password=mypass',
        responseType:'json',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    });
};

Solution 5

try to add this in your payload

grant_type=password&username=pippo&password=pluto
Share:
159,933

Related videos on Youtube

user20358
Author by

user20358

Updated on July 05, 2022

Comments

  • user20358
    user20358 almost 2 years

    I have followed this article to implement an OAuth Authorization server. However when I use post man to get a token, I get an error in the response:

    "error": "unsupported_grant_type"

    I read somewhere that the data in Postman needs to be posted using Content-type:application/x-www-form-urlencoded. I have prepped the required settings in Postman:

    enter image description here

    and yet my headers are like this:

    enter image description here

    Here is my code

    public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }
    
        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            if (context.OwinContext.Request.Method == "OPTIONS" && context.IsTokenEndpoint)
            {
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST" });
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "accept", "authorization", "content-type" });
                context.OwinContext.Response.StatusCode = 200;
                context.RequestCompleted();
                return Task.FromResult<object>(null);
            }
            return base.MatchEndpoint(context);       
        }
    
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            string allowedOrigin = "*";
    
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Content-Type" });
    
            Models.TheUser user = new Models.TheUser();
            user.UserName = context.UserName;
            user.FirstName = "Sample first name";
            user.LastName = "Dummy Last name";
    
            ClaimsIdentity identity = new ClaimsIdentity("JWT");
    
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            foreach (string claim in user.Claims)
            {
                identity.AddClaim(new Claim("Claim", claim));    
            }
    
            var ticket = new AuthenticationTicket(identity, null);
            context.Validated(ticket);
        }
    }
    
    public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly string _issuer = string.Empty;
    
        public CustomJwtFormat(string issuer)
        {
            _issuer = issuer;
        }
    
        public string Protect(AuthenticationTicket data)
        {
            string audienceId = ConfigurationManager.AppSettings["AudienceId"];
            string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["AudienceSecret"];
            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
            var signingKey = new HmacSigningCredentials(keyByteArray);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.WriteToken(token);
            return jwt;
        }
    
        public AuthenticationTicket Unprotect(string protectedText)
        {
            throw new NotImplementedException();
        }
    }
    

    In the CustomJWTFormat class above only the breakpoint in the constructor gets hit. In the CustomOauth class, the breakpoint in the GrantResourceOwnerCredentials method never gets hit. The others do.

    The Startup class:

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    
            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
    
            ConfigureOAuthTokenGeneration(app);
            ConfigureOAuthTokenConsumption(app);
    
            app.UseWebApi(config);
        }
    
        private void ConfigureOAuthTokenGeneration(IAppBuilder app)
        {
            var OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                //For Dev enviroment only (on production should be AllowInsecureHttp = false)
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/oauth/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["Issuer"])
            };
    
            // OAuth 2.0 Bearer Access Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
        }
    
        private void ConfigureOAuthTokenConsumption(IAppBuilder app)
        {
            string issuer = ConfigurationManager.AppSettings["Issuer"]; 
            string audienceId = ConfigurationManager.AppSettings["AudienceId"];
            byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
    
            // Api controllers with an [Authorize] attribute will be validated with JWT
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { audienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                    }
                });
        }
    }
    

    Do I need to set up Content-type:application/x-www-form-urlencoded somewhere else in the web api code? What could be wrong? Please help.

    • Neo
      Neo almost 7 years
      I do not want to pass username password rather , I want to verify using exernal providers like twitter consumer key and consumer secret , how can I do that?
    • The Senator
      The Senator almost 7 years
      Thanks for this, although your question and ultimate answer weren't actually what I was looking for, an inline snippet seems to have solved it for me. I was struggling with the OPTIONS auth token point being protected by the client id/secret. You saved me!
    • Paramvir Singh Karwal
      Paramvir Singh Karwal about 5 years
      Make sure that your authorization code has not expired. I was using postman for testing my code. The request contained old authorization code.
  • Parshwa Shah
    Parshwa Shah over 7 years
    How can i make same the request from angular service call?
  • sadhana
    sadhana almost 7 years
    I am sending the same request as described above, still getting invalid_grant. Please suggest a solution.
  • greg
    greg over 6 years
    for body's that include special characters like p@ssword would you need to replace the "@" with a "%40"?
  • OverMars
    OverMars over 6 years
    Great answer! Steps helped.
  • Peter
    Peter about 6 years
    @GregDegruy Looks like only password must be url-encoded. username can contain '@' but I had to replace an & in my password with %26
  • Jonathan Applebaum
    Jonathan Applebaum over 5 years
    also relevant to angular 2, i have tried to pass the data variable as an object and i got an error, passing the data as a string solved it.
  • Alielson Piffer
    Alielson Piffer over 5 years
    I did the same mistake. By selecting "x-www-form-urlencoded" in current version of Postman, the key "Content-Type" will be automatically added in Headers tab. Others parameters should be added in Body tab.
  • Bambam Deo
    Bambam Deo about 5 years
    Why it isn't working with form-data of body in place of x-www-form-urlencoded?
  • user230910
    user230910 almost 5 years
    Please explain how this answers the question as posted?
  • Rishabh Rusia
    Rishabh Rusia over 2 years
    Postman automatically selects header for Content type based on body selected. We cannot change it to application/x-www-form-urlencoded when entering text content