Utilizza più JWT Bearer Authentication


86

È possibile supportare più emittenti di token JWT in ASP.NET Core 2? Voglio fornire un'API per un servizio esterno e devo utilizzare due origini di token JWT: Firebase e gli emittenti di token JWT personalizzati. In ASP.NET core posso impostare l'autenticazione JWT per lo schema di autorizzazione del portatore, ma solo per un'autorità:

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

Posso avere più emittenti e pubblici, ma non posso impostare più autorità.


1
AFAIK puoi aggiungere un numero qualsiasi di proprietà a un JWT. Quindi, non c'è nulla che ti impedisca di registrare due nomi di emittenti in un JWT. Il problema sta nel fatto che la tua applicazione dovrebbe conoscere entrambe le chiavi, se ogni emittente usasse una chiave diversa per firmare.
Tim Biegeleisen

Risposte:


186

Puoi ottenere totalmente ciò che desideri:

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

Esaminiamo le differenze tra il tuo codice e quello.

AddAuthentication non ha parametri

Se si imposta uno schema di autenticazione predefinito, ad ogni singola richiesta il middleware di autenticazione tenterà di eseguire il gestore di autenticazione associato allo schema di autenticazione predefinito. Poiché ora abbiamo due schemi di autenticazione utilizzabili, non ha senso eseguirne uno.

Usa un altro sovraccarico di AddJwtBearer

Ogni singolo AddXXXmetodo per aggiungere un'autenticazione ha diversi overload:

Ora, poiché usi lo stesso metodo di autenticazione due volte ma gli schemi di autenticazione devono essere univoci, devi usare il secondo overload.

Aggiorna il criterio predefinito

Poiché le richieste non verranno più autenticate automaticamente, l'inserimento di [Authorize]attributi in alcune azioni comporterà il rifiuto delle richieste e HTTP 401verrà emesso un messaggio.

Dal momento che non è quello che vogliamo perché vogliamo dare ai gestori la possibilità di autenticazione per autenticare la richiesta, si cambia la politica di default del sistema di autorizzazione, indicando sia le Firebasee Customautenticazione schemi deve essere provato per autenticare la richiesta.

Ciò non ti impedisce di essere più restrittivo su alcune azioni; l' [Authorize]attributo ha una AuthenticationSchemesproprietà che consente di sovrascrivere gli schemi di autenticazione validi.

Se hai scenari più complessi, puoi utilizzare l' autorizzazione basata su criteri . Trovo che la documentazione ufficiale sia ottima.

Immaginiamo che alcune azioni siano disponibili solo per i token JWT emessi da Firebase e debbano avere una dichiarazione con un valore specifico; potresti farlo in questo modo:

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

È quindi possibile utilizzare [Authorize(Policy = "FirebaseAdministrators")]su alcune azioni.

Un ultimo punto da notare: se stai catturando AuthenticationFailedeventi e usando qualcosa tranne il primo AddJwtBearercriterio, potresti vedere IDX10501: Signature validation failed. Unable to match key...Ciò è causato dal sistema che controlla ciascuno AddJwtBearera turno fino a quando non ottiene una corrispondenza. L'errore di solito può essere ignorato.


4
Ciò richiede che il valore dell'intestazione venga modificato da Firebase o soluzione personalizzata? cioè. invece di Authorization : Bearer <token>quello l'intestazione essere Authorization : Firebase <token>per esempio? Quando ho provato questa soluzione ho ricevuto l'errore: "Nessun gestore di autenticazione è registrato per lo schema 'Bearer'."
Rush Frisby

4
No, non è necessario modificare le intestazioni. Il messaggio di errore suggerisce che stai facendo riferimento a uno schema di autenticazione inesistente (Bearer). Nei nostri esempi, i due schemi registrati sono Firebase e Custom, che sono i primi argomenti delle .AddJwtBearerchiamate al metodo.
Mickaël Derriey

5
Ciao. Stavo cercando solo questa soluzione. Sfortunatamente ricevo un'eccezione "Non è stato specificato alcun authenticationScheme e non è stato trovato alcun DefaultChallengeScheme". options.DefaultPolicy è impostato su ok. Qualche idea?
terjetyl

11
Questa è stata una risposta estremamente utile e ha messo insieme molto di ciò che ho visto in pezzi dappertutto.
Aron W.

2
@TylerOhlsen non è corretto; anche se verrà utilizzato nel caso che descrivi, non è l'unico. Verrà utilizzato anche se non si specifica un requisito di autorizzazione a livello di endpoint, ma si decora i controller MVC e / o le azioni con un [Authorize]attributo vuoto .
Mickaël Derriey,

4

Questa è un'estensione della risposta di Mickaël Derriey.

La nostra app ha un requisito di autorizzazione personalizzato che risolviamo da una fonte interna. Stavamo utilizzando Auth0 ma stiamo passando all'autenticazione dell'account Microsoft utilizzando OpenID. Ecco il codice leggermente modificato dal nostro avvio di ASP.Net Core 2.1. Per i futuri lettori, funziona al momento della stesura di questo documento per le versioni specificate. Il chiamante utilizza id_token da OpenID sulle richieste in arrivo passate come token Bearer. Spero che aiuti qualcun altro a provare a convertire l'autorità di identità tanto quanto questa domanda e risposta mi hanno aiutato.

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());
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.