Autorizzare con uno schema specifico in ASP.NET Core

Per un'introduzione agli schemi di autenticazione in ASP.NET Core, vedere Schema di autenticazione.

In alcuni scenari, ad esempio applicazioni a pagina singola, è comune usare più metodi di autenticazione. Ad esempio, l'app può usare cookiel'autenticazione basata su -per accedere e l'autenticazione del bearer JWT per le richieste JavaScript. In alcuni casi, l'app può avere più istanze di un gestore di autenticazione. Ad esempio, due cookie gestori in cui uno contiene un'identità di base e uno viene creato quando è stata attivata un'autenticazione a più fattori (MFA). L'autenticazione a più fattori può essere attivata perché l'utente ha richiesto un'operazione che richiede una maggiore sicurezza. Per altre informazioni sull'applicazione dell'autenticazione a più fattori quando un utente richiede una risorsa che richiede l'autenticazione a più fattori, vedere la sezione GitHub issue Protect with MFA (Proteggere il problema di GitHub con MFA).

Uno schema di autenticazione viene denominato quando il servizio di autenticazione viene configurato durante l'autenticazione. Ad esempio:

using Microsoft.AspNetCore.Authentication;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication()
        .AddCookie(options =>
        {
            options.LoginPath = "/Account/Unauthorized/";
            options.AccessDeniedPath = "/Account/Forbidden/";
        })
        .AddJwtBearer(options =>
        {
            options.Audience = "http://localhost:5001/";
            options.Authority = "http://localhost:5000/";
        });

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

Nel codice precedente sono stati aggiunti due gestori di autenticazione: uno per cookies e uno per il bearer.

Nota

Se si specifica lo schema predefinito, la HttpContext.User proprietà viene impostata su tale identità. Se tale comportamento non è desiderato, disabilitarlo richiamando la forma senza parametri di AddAuthentication.

Selezione dello schema con l'attributo Authorize

Al momento dell'autorizzazione, l'app indica il gestore da usare. Selezionare il gestore con cui l'app autorizza passando un elenco delimitato da virgole di schemi di autenticazione a [Authorize]. L'attributo [Authorize] specifica lo schema di autenticazione o gli schemi da utilizzare indipendentemente dal fatto che sia configurato un valore predefinito. Ad esempio:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;

namespace AuthScheme.Controllers;

[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
{
    private const string AuthSchemes =
        CookieAuthenticationDefaults.AuthenticationScheme + "," +
        JwtBearerDefaults.AuthenticationScheme;
    public ContentResult Index() => Content(MyWidgets.GetMyContent());

}

Nell'esempio precedente i cookie gestori di connessione e vengono eseguiti e hanno la possibilità di creare e aggiungere un'identità per l'utente corrente. Specificando un solo schema, viene eseguito il gestore corrispondente:

[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]
public class Mixed2Controller : Controller
{
    public ContentResult Index() => Content(MyWidgets.GetMyContent());
}

Nel codice precedente viene eseguito solo il gestore con lo schema "Bearer". Tutte cookiele identità basate su vengono ignorate.

Selezione dello schema con i criteri

Se si preferisce specificare gli schemi desiderati nei criteri, è possibile impostare la AuthenticationSchemes raccolta quando si aggiungono criteri:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
    {
        policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new MinimumAgeRequirement(18));
    });
});

builder.Services.AddAuthentication()
                .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

Nell'esempio precedente il criterio "Over18" viene eseguito solo sull'identità creata dal gestore "Bearer". Usare il criterio impostando la [Authorize] proprietà dell'attributo Policy :

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace AuthScheme.Controllers;
[Authorize(Policy = "Over18")]
public class RegistrationController : Controller
{
    // Do Registration

Usare più schemi di autenticazione

Alcune app potrebbero dover supportare più tipi di autenticazione. Ad esempio, l'app potrebbe autenticare gli utenti da Azure Active Directory e da un database utenti. Un altro esempio è un'app che autentica gli utenti sia da Active Directory Federation Services che da Azure Active Directory B2C. In questo caso, l'app deve accettare un token di connessione JWT da diverse autorità emittenti.

Aggiungere tutti gli schemi di autenticazione da accettare. Ad esempio, il codice seguente aggiunge due schemi di autenticazione di connessione JWT con autorità emittenti diverse:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
        });

// Authorization
builder.Services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        "AzureAD");
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

Nota

Solo un'autenticazione di connessione JWT è registrata con lo schema JwtBearerDefaults.AuthenticationSchemedi autenticazione predefinito . È necessario registrare un'autenticazione aggiuntiva con uno schema di autenticazione univoco.

Aggiornare i criteri di autorizzazione predefiniti per accettare entrambi gli schemi di autenticazione. Ad esempio:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
        });

// Authorization
builder.Services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        "AzureAD");
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

Poiché viene eseguito l'override dei criteri di autorizzazione predefiniti, è possibile usare l'attributo [Authorize] nei controller. Il controller accetta quindi le richieste con JWT rilasciate dal primo o dal secondo emittente.

Vedere questo problema di GitHub sull'uso di più schemi di autenticazione.

L'esempio seguente usa Azure Active Directory B2C e un altro tenant di Azure Active Directory :

using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using System.IdentityModel.Tokens.Jwt;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "B2C_OR_AAD";
    options.DefaultChallengeScheme = "B2C_OR_AAD";
})
.AddJwtBearer("B2C", jwtOptions =>
{
    jwtOptions.MetadataAddress = "B2C-MetadataAddress";
    jwtOptions.Authority = "B2C-Authority";
    jwtOptions.Audience = "B2C-Audience";
})
.AddJwtBearer("AAD", jwtOptions =>
{
    jwtOptions.MetadataAddress = "AAD-MetadataAddress";
    jwtOptions.Authority = "AAD-Authority";
    jwtOptions.Audience = "AAD-Audience";
    jwtOptions.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateIssuerSigningKey = true,
        ValidAudiences = builder.Configuration.GetSection("ValidAudiences").Get<string[]>(),
        ValidIssuers = builder.Configuration.GetSection("ValidIssuers").Get<string[]>()
    };
})
.AddPolicyScheme("B2C_OR_AAD", "B2C_OR_AAD", options =>
{
    options.ForwardDefaultSelector = context =>
    {
        string authorization = context.Request.Headers[HeaderNames.Authorization];
        if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
        {
            var token = authorization.Substring("Bearer ".Length).Trim();
            var jwtHandler = new JwtSecurityTokenHandler();

            return (jwtHandler.CanReadToken(token) && jwtHandler.ReadJwtToken(token).Issuer.Equals("B2C-Authority"))
                ? "B2C" : "AAD";
        }
        return "AAD";
    };
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute().RequireAuthorization();
app.MapRazorPages().RequireAuthorization();

app.MapFallbackToFile("index.html");

app.Run();

Nel codice ForwardDefaultSelector precedente viene usato per selezionare uno schema predefinito per la richiesta corrente a cui i gestori di autenticazione devono inoltrare per impostazione predefinita tutte le operazioni di autenticazione. La logica di inoltro predefinita controlla prima l'impostazione più specifica ForwardAuthenticate, ForwardChallenge, ForwardForbid, ForwardSignIne ForwardSignOut , seguita dal controllo ForwardDefaultSelector, seguito da ForwardDefault. Il primo risultato non Null viene usato come schema di destinazione a cui inoltrare. Per altre informazioni, vedere Schemi di criteri in ASP.NET Core.

Per un'introduzione agli schemi di autenticazione in ASP.NET Core, vedere Schema di autenticazione.

In alcuni scenari, ad esempio applicazioni a pagina singola, è comune usare più metodi di autenticazione. Ad esempio, l'app può usare cookiel'autenticazione basata su -per accedere e l'autenticazione del bearer JWT per le richieste JavaScript. In alcuni casi, l'app può avere più istanze di un gestore di autenticazione. Ad esempio, due cookie gestori in cui uno contiene un'identità di base e uno viene creato quando è stata attivata un'autenticazione a più fattori (MFA). L'autenticazione a più fattori può essere attivata perché l'utente ha richiesto un'operazione che richiede una maggiore sicurezza. Per altre informazioni sull'applicazione dell'autenticazione a più fattori quando un utente richiede una risorsa che richiede l'autenticazione a più fattori, vedere la sezione GitHub issue Protect with MFA (Proteggere il problema di GitHub con MFA).

Uno schema di autenticazione viene denominato quando il servizio di autenticazione viene configurato durante l'autenticazione. Ad esempio:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthentication()
        .AddCookie(options => {
            options.LoginPath = "/Account/Unauthorized/";
            options.AccessDeniedPath = "/Account/Forbidden/";
        })
        .AddJwtBearer(options => {
            options.Audience = "http://localhost:5001/";
            options.Authority = "http://localhost:5000/";
        });

Nel codice precedente sono stati aggiunti due gestori di autenticazione: uno per cookies e uno per il bearer.

Nota

Se si specifica lo schema predefinito, la HttpContext.User proprietà viene impostata su tale identità. Se tale comportamento non è desiderato, disabilitarlo richiamando la forma senza parametri di AddAuthentication.

Selezione dello schema con l'attributo Authorize

Al momento dell'autorizzazione, l'app indica il gestore da usare. Selezionare il gestore con cui l'app autorizza passando un elenco delimitato da virgole di schemi di autenticazione a [Authorize]. L'attributo [Authorize] specifica lo schema di autenticazione o gli schemi da utilizzare indipendentemente dal fatto che sia configurato un valore predefinito. Ad esempio:

[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
    // Requires the following imports:
    // using Microsoft.AspNetCore.Authentication.Cookies;
    // using Microsoft.AspNetCore.Authentication.JwtBearer;
    private const string AuthSchemes =
        CookieAuthenticationDefaults.AuthenticationScheme + "," +
        JwtBearerDefaults.AuthenticationScheme;

Nell'esempio precedente i cookie gestori di connessione e vengono eseguiti e hanno la possibilità di creare e aggiungere un'identità per l'utente corrente. Specificando un solo schema, viene eseguito il gestore corrispondente.

[Authorize(AuthenticationSchemes = 
    JwtBearerDefaults.AuthenticationScheme)]
public class MixedController : Controller

Nel codice precedente viene eseguito solo il gestore con lo schema "Bearer". Tutte cookiele identità basate su vengono ignorate.

Selezione dello schema con i criteri

Se si preferisce specificare gli schemi desiderati nei criteri, è possibile impostare la AuthenticationSchemes raccolta quando si aggiungono i criteri:

services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
    {
        policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new MinimumAgeRequirement());
    });
});

Nell'esempio precedente il criterio "Over18" viene eseguito solo sull'identità creata dal gestore "Bearer". Usare il criterio impostando la [Authorize] proprietà dell'attributo Policy :

[Authorize(Policy = "Over18")]
public class RegistrationController : Controller

Usare più schemi di autenticazione

Alcune app potrebbero dover supportare più tipi di autenticazione. Ad esempio, l'app potrebbe autenticare gli utenti da Azure Active Directory e da un database utenti. Un altro esempio è un'app che autentica gli utenti sia da Active Directory Federation Services che da Azure Active Directory B2C. In questo caso, l'app deve accettare un token di connessione JWT da diverse autorità emittenti.

Aggiungere tutti gli schemi di autenticazione da accettare. Ad esempio, il codice seguente in Startup.ConfigureServices aggiunge due schemi di autenticazione di connessione JWT con autorità emittenti diverse:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-6f99-4bdc-8611-1bc8edd7f436/";
        });
}

Nota

Solo un'autenticazione di connessione JWT è registrata con lo schema JwtBearerDefaults.AuthenticationSchemedi autenticazione predefinito . È necessario registrare un'autenticazione aggiuntiva con uno schema di autenticazione univoco.

Il passaggio successivo consiste nell'aggiornare i criteri di autorizzazione predefiniti per accettare entrambi gli schemi di autenticazione. Ad esempio:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthorization(options =>
    {
        var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
            JwtBearerDefaults.AuthenticationScheme,
            "AzureAD");
        defaultAuthorizationPolicyBuilder = 
            defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
        options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    });
}

Poiché viene eseguito l'override dei criteri di autorizzazione predefiniti, è possibile usare l'attributo [Authorize] nei controller. Il controller accetta quindi le richieste con JWT rilasciate dal primo o dal secondo emittente.

Vedere questo problema di GitHub sull'uso di più schemi di autenticazione.