web-dev-qa-db-fra.com

Obtention de "error": "unsupported_grant_type" lors de la tentative d'obtention d'un JWT en appelant une API Web sécurisée OWIN OAuth via Postman.

J'ai suivi cet article pour implémenter un serveur d'autorisation OAuth. Cependant, lorsque j'utilise post man pour obtenir un jeton, je reçois une erreur dans la réponse:

"error": "unsupported_grant_type"

J'ai lu quelque part que les données dans Postman doivent être publiées avec Content-type:application/x-www-form-urlencoded. J'ai préparé les paramètres requis dans Postman:

enter image description here

et pourtant mes en-têtes sont comme ceci:

enter image description here

Voici mon 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();
    }
}

Dans la classe CustomJWTFormat ci-dessus, seul le point d'arrêt du constructeur est touché. Dans la classe CustomOauth, le point d'arrêt de la méthode GrantResourceOwnerCredentials n'est jamais touché. Les autres font. 

La classe de démarrage:

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)
                }
            });
    }
}

Dois-je configurer Content-type:application/x-www-form-urlencoded ailleurs dans le code de l'API Web? Quel pourrait être le problème? S'il vous plaît aider.

57
user20358

La réponse est un peu tardive - mais au cas où quelqu'un aurait le problème à l'avenir ...

Dans la capture d'écran ci-dessus, il semble que vous ajoutiez les données de l'URL (nom d'utilisateur, mot de passe, grant_type) à l'en-tête et non à l'élément body.

En cliquant sur l'onglet du corps, puis en sélectionnant le bouton radio "x-www-form-urlencoded", il devrait y avoir une liste de valeurs-clés sous laquelle vous pouvez saisir les données de la demande.

103
MarkP

Avec Postman, sélectionnez l'onglet Corps, choisissez l'option brute et entrez les informations suivantes:

grant_type=password&username=yourusername&password=yourpassword
46
fojeeck
  1. Notez l'URL: localhost:55828/token (pas localhost:55828/API/token)
  2. Notez les données de la demande. Ce n'est pas au format JSON, c'est juste des données brutes sans guillemets .[email protected]&password=Test123$&grant_type=password
  3. Notez le type de contenu. Content-Type: 'application/x-www-formulaire-urlencoded' (pas Content-Type: 'application/json')
  4. Lorsque vous utilisez JavaScript pour faire une demande de publication, vous pouvez utiliser les éléments suivants:

    $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) {//...
    

Voir les captures d'écran ci-dessous de Postman:

 Postman Request

 Postman Request Header

16
Chirag

Si vous utilisez AngularJS, vous devez transmettre les paramètres du corps sous forme de chaîne:

    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' }
    });
};
5
Gregory

essayez d'ajouter ceci dans votre charge utile

grant_type=password&username=pippo&password=pluto
2
Mauro Sala

Ancienne question, mais pour angular 6, vous devez le faire lorsque vous utilisez HttpClient J'expose publiquement les données de jetons ici, mais ce serait bien si vous y accédez via des propriétés en lecture seule.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { delay, tap } from 'rxjs/operators';
import { Router } from '@angular/router';


@Injectable()
export class AuthService {
    isLoggedIn: boolean = false;
    url = "token";

    tokenData = {};
    username = "";
    AccessToken = "";

    constructor(private http: HttpClient, private router: Router) { }

    login(username: string, password: string): Observable<object> {
        let model = "username=" + username + "&password=" + password + "&grant_type=" + "password";

        return this.http.post(this.url, model).pipe(
            tap(
                data => {
                    console.log('Log In succesful')
                    //console.log(response);
                    this.isLoggedIn = true;
                    this.tokenData = data;
                    this.username = data["username"];
                    this.AccessToken = data["access_token"];
                    console.log(this.tokenData);
                    return true;

                },
                error => {
                    console.log(error);
                    return false;

                }
            )
        );
    }
}
1
Abdul Rehman Sayed

Je recevais aussi cette erreur et la raison en était un mauvais appel url. Je laisse cette réponse ici, si quelqu'un d'autre mélange les URL et obtient cette erreur. Il m'a fallu des heures pour comprendre que j'avais une mauvaise URL.

Erreur que j'ai eu (code HTTP 400):

{
    "error": "unsupported_grant_type",
    "error_description": "grant type not supported"
}

J'appelais:

https://MY_INSTANCE.lightning.force.com

Alors que l'URL correcte aurait été:

https://MY_INSTANCE.cs110.my.salesforce.com

0
Firze