"Fehler" erhalten: "unsupported_grant_type", wenn versucht wird, eine JWT durch Aufrufen einer OWIN OAuth-gesicherten Web-API über Postman abzurufen

72

Ich habe diesen Artikel befolgt , um einen OAuth-Autorisierungsserver zu implementieren. Wenn ich jedoch Postman verwende, um ein Token zu erhalten, wird in der Antwort ein Fehler angezeigt:

"error": "unsupported_grant_type"

Ich habe irgendwo gelesen, dass die Daten in Postman mit gebucht werden müssen Content-type:application/x-www-form-urlencoded. Ich habe die erforderlichen Einstellungen in Postman vorbereitet:

Geben Sie hier die Bildbeschreibung ein

und doch sind meine Überschriften so:

Geben Sie hier die Bildbeschreibung ein

Hier ist mein 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 der obigen CustomJWTFormat-Klasse wird nur der Haltepunkt im Konstruktor getroffen. In der CustomOauth-Klasse wird der Haltepunkt in der GrantResourceOwnerCredentials-Methode niemals getroffen. Die anderen tun es.

Die Startup-Klasse:

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

Muss ich Content-type:application/x-www-form-urlencodedirgendwo anders im Web-API-Code einrichten ? Was könnte falsch sein? Bitte helfen Sie.

user20358
quelle
Ich möchte kein Benutzername-Passwort übergeben, sondern mit externen Anbietern wie Twitter-Verbraucherschlüssel und Verbrauchergeheimnis überprüfen. Wie kann ich das tun?
Neo
Vielen Dank dafür, obwohl Ihre Frage und die endgültige Antwort nicht das waren, wonach ich gesucht habe, scheint ein Inline-Snippet es für mich gelöst zu haben. Ich hatte Probleme damit, dass der OPTIONS-Authentifizierungstokenpunkt durch die Client-ID / das Client-Geheimnis geschützt wird. Du hast mich gerettet!
Der Senator
Stellen Sie sicher, dass Ihr Autorisierungscode nicht abgelaufen ist. Ich habe Postman zum Testen meines Codes verwendet. Die Anfrage enthielt alten Autorisierungscode.
Paramvir Singh Karwal

Antworten:

149

Die Antwort ist etwas spät - aber falls jemand das Problem in der Zukunft hat ...

Aus dem obigen Screenshot geht hervor, dass Sie die URL-Daten (Benutzername, Kennwort, Grant-Typ) zum Header und nicht zum Body-Element hinzufügen.

Wenn Sie auf die Registerkarte "Körper" klicken und dann das Optionsfeld "x-www-form-urlencoded" auswählen, sollte sich darunter eine Liste mit Schlüsselwerten befinden, in die Sie die Anforderungsdaten eingeben können

MarkP
quelle
3
Wie kann ich gleich die Anfrage von Angular Service Call stellen?
Parshwa Shah
Ich habe den gleichen Fehler gemacht. Durch Auswahl von "x-www-form-urlencoded" in der aktuellen Version von Postman wird der Schlüssel "Content-Type" automatisch auf der Registerkarte "Header" hinzugefügt. Andere Parameter sollten auf der Registerkarte Body hinzugefügt werden.
Alielson Piffer
Warum funktioniert es nicht mit Formulardaten des Körpers anstelle von x-www-form-urlencoded?
Bambam Deo
60

Wählen Sie bei Postman die Registerkarte Body aus, wählen Sie die Rohoption aus und geben Sie Folgendes ein:

grant_type=password&username=yourusername&password=yourpassword
Fojeeck
quelle
Für Körper, die Sonderzeichen enthalten, wie p@sswordmüssten Sie das "@" durch ein "% 40" ersetzen?
Greg
1
@ GregDegruy Es sieht so aus, als ob nur das Passwort url-codiert sein muss. Benutzername kann '@' enthalten, aber ich musste ein & in meinem Passwort durch% 26 ersetzen
Peter
21
  1. Beachten Sie die URL: localhost:55828/token(nicht localhost:55828/API/token)
  2. Notieren Sie die Anforderungsdaten. Es ist nicht im JSON-Format, es sind nur einfache Daten ohne doppelte Anführungszeichen. [email protected]&password=Test123$&grant_type=password
  3. Notieren Sie den Inhaltstyp. Inhaltstyp: 'application / x-www-form-urlencoded' (nicht Inhaltstyp: 'application / json')
  4. Wenn Sie JavaScript verwenden, um eine Post-Anfrage zu stellen, können Sie Folgendes verwenden:

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

Siehe Screenshots unten von Postman:

Postbotenanfrage

Postman Request Header

Chirag
quelle
3
Ich sende dieselbe Anfrage wie oben beschrieben und erhalte immer noch invalid_grant. Bitte schlagen Sie eine Lösung vor.
Sadhana
Gute Antwort! Schritte halfen.
OverMars
5

Wenn Sie AngularJS verwenden, müssen Sie die Body-Parameter als Zeichenfolge übergeben:

    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' }
    });
};
Gregory
quelle
2
Auch für Winkel 2 relevant, habe ich versucht, die Datenvariable als Objekt zu übergeben, und ich habe einen Fehler erhalten, die Daten als Zeichenfolge zu übergeben, löste es.
Jonathana
4

Versuchen Sie, dies in Ihre Nutzlast aufzunehmen

grant_type=password&username=pippo&password=pluto
Mauro Sala
quelle
1

Alte Frage, aber angular 6dafür muss dies getan werden, wenn Sie verwenden. HttpClient Ich mache Token-Daten hier öffentlich verfügbar, aber es wäre gut, wenn Sie über schreibgeschützte Eigenschaften darauf zugreifen würden.

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;

                }
            )
        );
    }
}
Abdul Rehman sagte
quelle
1

Ich habe auch diesen Fehler erhalten und der Grund war eine falsche Anruf-URL. Ich lasse diese Antwort hier, wenn jemand anderes die URLs mischt und diesen Fehler erhält. Ich habe Stunden gebraucht, um festzustellen, dass ich eine falsche URL hatte.

Fehler, den ich bekommen habe (HTTP-Code 400):

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

Ich habe angerufen:

https://MY_INSTANCE.lightning.force.com

Während die richtige URL gewesen wäre:

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

Firze
quelle
-4

Verwenden Sie grant_type = {Ihr Passwort} Geben Sie hier die Bildbeschreibung ein

Debendra Dash
quelle
Bitte erläutern Sie, wie dies die gestellte Frage beantwortet.
user230910