Verwenden Sie die Authentifizierung mit mehreren JWT-Trägern

87

Ist es möglich, mehrere JWT-Token-Aussteller in ASP.NET Core 2 zu unterstützen? Ich möchte eine API für externe Dienste bereitstellen und muss zwei Quellen für JWT-Token verwenden - Firebase- und benutzerdefinierte JWT-Token-Aussteller. In ASP.NET Core kann ich die JWT-Authentifizierung für das Bearer-Authentifizierungsschema festlegen, jedoch nur für eine Behörde:

  services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Authority = "https://securetoken.google.com/my-firebase-project"
            options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = "my-firebase-project"
                    ValidateAudience = true,
                    ValidAudience = "my-firebase-project"
                    ValidateLifetime = true
                };
        }

Ich kann mehrere Emittenten und Zielgruppen haben, aber nicht mehrere Behörden festlegen.

Gesund
quelle
1
AFAIK Sie können einem JWT eine beliebige Anzahl von Eigenschaften hinzufügen. Nichts hindert Sie daran, zwei Ausstellernamen in einem JWT aufzuzeichnen. Das Problem besteht darin, dass Ihre Anwendung beide Schlüssel kennen muss, wenn jeder Aussteller einen anderen Schlüssel zum Signieren verwendet.
Tim Biegeleisen

Antworten:

186

Sie können voll und ganz erreichen, was Sie wollen:

services
    .AddAuthentication()
    .AddJwtBearer("Firebase", options =>
    {
        options.Authority = "https://securetoken.google.com/my-firebase-project"
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "my-firebase-project"
            ValidateAudience = true,
            ValidAudience = "my-firebase-project"
            ValidateLifetime = true
        };
    })
    .AddJwtBearer("Custom", options =>
    {
        // Configuration for your custom
        // JWT tokens here
    });

services
    .AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase", "Custom")
            .Build();
    });

Lassen Sie uns die Unterschiede zwischen Ihrem Code und diesem durchgehen.

AddAuthentication hat keinen Parameter

Wenn Sie ein Standardauthentifizierungsschema festlegen, versucht die Authentifizierungs-Middleware bei jeder einzelnen Anforderung, den dem Standardauthentifizierungsschema zugeordneten Authentifizierungshandler auszuführen. Da wir jetzt zwei mögliche Authentifizierungsschemata haben, macht es keinen Sinn, eines davon auszuführen.

Verwenden Sie eine andere Überladung von AddJwtBearer

Jede einzelne AddXXXMethode zum Hinzufügen einer Authentifizierung weist mehrere Überladungen auf:

Da Sie dieselbe Authentifizierungsmethode zweimal verwenden, die Authentifizierungsschemata jedoch eindeutig sein müssen, müssen Sie die zweite Überladung verwenden.

Aktualisieren Sie die Standardrichtlinie

Da die Anforderungen nicht mehr automatisch authentifiziert werden, werden beim Anlegen von [Authorize]Attributen für einige Aktionen die Anforderungen abgelehnt und eine HTTP 401ausgegeben.

Da das ist nicht das, was wir wollen , weil wir die Authentifizierungs - Handler eine Chance zu authentifizieren , die Anforderung geben wollen, müssen wir die Standardrichtlinie des Autorisierungssystems verändern sowohl die durch die Angabe Firebaseund Customsollte Systeme Authentifizierung werden versucht , die Anforderung zu authentifizieren.

Das hindert Sie nicht daran, einige Aktionen restriktiver zu gestalten. Das [Authorize]Attribut verfügt über eine AuthenticationSchemesEigenschaft, mit der Sie überschreiben können, welche Authentifizierungsschemata gültig sind.

Wenn Sie komplexere Szenarien haben, können Sie die richtlinienbasierte Autorisierung verwenden . Ich finde die offizielle Dokumentation großartig.

Stellen wir uns vor, einige Aktionen stehen nur JWT-Token zur Verfügung, die von Firebase ausgestellt wurden, und müssen einen Anspruch mit einem bestimmten Wert haben. Sie könnten es so machen:

// Authentication code omitted for brevity

services
    .AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase", "Custom")
            .Build();

        options.AddPolicy("FirebaseAdministrators", new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase")
            .RequireClaim("role", "admin")
            .Build());
    });

Sie können dann [Authorize(Policy = "FirebaseAdministrators")]einige Aktionen verwenden.

Ein letzter zu beachtender Punkt: Wenn Sie AuthenticationFailedEreignisse abfangen und etwas anderes als die erste AddJwtBearerRichtlinie verwenden, wird möglicherweise Folgendes angezeigt: IDX10501: Signature validation failed. Unable to match key...Dies wird dadurch verursacht, dass das System jede AddJwtBearernacheinander überprüft, bis eine Übereinstimmung erzielt wird. Der Fehler kann normalerweise ignoriert werden.

Mickaël Derriey
quelle
4
Erfordert dies, dass der Header-Wert von Firebase oder einer benutzerdefinierten Lösung geändert wird? dh. stattdessen ist Authorization : Bearer <token>der Header Authorization : Firebase <token>zum Beispiel? Beim Ausprobieren dieser Lösung wurde folgende Fehlermeldung angezeigt: "Für das Schema 'Bearer' ist kein Authentifizierungshandler registriert."
Rush Frisby
4
Nein, die Header müssen nicht geändert werden. Die Fehlermeldung weist darauf hin, dass Sie sich auf ein nicht vorhandenes Authentifizierungsschema (Bearer) beziehen. In unseren Beispielen sind die beiden registrierten Schemata Firebase und Custom, die ersten Argumente der .AddJwtBearerMethodenaufrufe.
Mickaël Derriey
5
Hallo. War auf der Suche nach genau dieser Lösung. Leider wird die Ausnahme "Es wurde kein Authentifizierungsschema angegeben und es wurde keine DefaultChallengeScheme gefunden" angezeigt. options.DefaultPolicy ist in Ordnung gesetzt. Irgendwelche Ideen?
Terjetyl
11
Dies war eine äußerst hilfreiche Antwort und hat viel von dem, was ich überall gesehen habe, in Stücken zusammengestellt.
Aron W.
2
@ TylerOhlsen das ist nicht richtig; Während es in dem von Ihnen beschriebenen Fall verwendet wird, ist es nicht das einzige. Es wird auch verwendet, wenn Sie keine Berechtigungsanforderung auf Endpunktebene angeben, sondern MVC-Controller und / oder -Aktionen mit einem leeren [Authorize]Attribut dekorieren .
Mickaël Derriey
4

Dies ist eine Erweiterung von Mickaël Derrieys Antwort.

Unsere App hat eine benutzerdefinierte Autorisierungsanforderung, die wir aus einer internen Quelle lösen. Wir haben Auth0 verwendet, wechseln jedoch mit OpenID zur Microsoft-Kontoauthentifizierung. Hier ist der leicht bearbeitete Code aus unserem ASP.Net Core 2.1-Start. Für zukünftige Leser funktioniert dies ab diesem Zeitpunkt für die angegebenen Versionen. Der Aufrufer verwendet das id_token von OpenID für eingehende Anforderungen, die als Bearer-Token übergeben werden. Ich hoffe, es hilft jemand anderem, der versucht, eine Identitätsautoritätskonvertierung durchzuführen, genauso wie mir diese Frage und Antwort geholfen hat.

const string Auth0 = nameof(Auth0);
const string MsaOpenId = nameof(MsaOpenId);

string domain = "https://myAuth0App.auth0.com/";
services.AddAuthentication()
        .AddJwtBearer(Auth0, options =>
            {
                options.Authority = domain;
                options.Audience = "https://myAuth0Audience.com";
            })
        .AddJwtBearer(MsaOpenId, options =>
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateAudience = true,
                    ValidAudience = "00000000-0000-0000-0000-000000000000",

                    ValidateIssuer = true,
                    ValidIssuer = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",

                    ValidateIssuerSigningKey = true,
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    RequireSignedTokens = true,
                    ClockSkew = TimeSpan.FromMinutes(10),
                };
                options.MetadataAddress = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0/.well-known/openid-configuration";
            }
        );

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes( Auth0, MsaOpenId )
        .Build();

    var approvedPolicyBuilder =  new AuthorizationPolicyBuilder()
           .RequireAuthenticatedUser()
           .AddAuthenticationSchemes(Auth0, MsaOpenId)
           ;

    approvedPolicyBuilder.Requirements.Add(new HasApprovedRequirement(domain));

    options.AddPolicy("approved", approvedPolicyBuilder.Build());
});
Keine Rückerstattung Keine Rückgabe
quelle