Partilhar via


Autorizar com um esquema específico no ASP.NET Core

Para obter uma introdução aos esquemas de autenticação no ASP.NET Core, consulte Esquema de autenticação.

Em alguns cenários, como Aplicativos de Página Única (SPAs), é comum usar vários métodos de autenticação. Por exemplo, o aplicativo pode usar a autenticação baseada cookie em para fazer logon e a autenticação de portador JWT para solicitações JavaScript. Em alguns casos, o aplicativo pode ter várias instâncias de um manipulador de autenticação. Por exemplo, dois manipuladores cookie em que um contém uma identity básica e outro é criado quando uma MFA (autenticação multifator) tiver sido disparada. A MFA pode ser disparada porque o usuário solicitou uma operação que requer segurança extra. Para obter mais informações sobre como impor a MFA quando um usuário solicita um recurso que requer MFA, consulte a seção Proteger a seção com MFA.

Um esquema de autenticação será nomeado quando o serviço de autenticação for configurado durante a autenticação. Por exemplo:

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

No código anterior, dois manipuladores de autenticação foram adicionados: um para cookies e outro para portador.

Observação

Especificar o esquema padrão faz com que a propriedade HttpContext.User seja definida como essa identity. Se esse comportamento não for desejado, desabilite-o invocando a forma sem parâmetros de AddAuthentication.

Como selecionar o esquema com o atributo Authorize

No ponto de autorização, o aplicativo indica o manipulador a ser usado. Selecione o manipulador com o qual o aplicativo autorizará passando uma lista delimitada por vírgulas de esquemas de autenticação para [Authorize]. O atributo [Authorize] especifica o esquema, ou esquema, de autenticação a serem usados independentemente de um padrão ser configurado. Por exemplo:

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

}

No exemplo anterior, os manipuladores de cookie e de portador são executados e têm a chance de criar e acrescentar uma identity para o usuário atual. Especificando apenas um único esquema, o manipulador correspondente é executado:

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

No código anterior, somente o manipulador com o esquema "Portador" é executado. Todas as identidades baseadas em cookie são ignoradas.

Como selecionando o esquema com políticas

Se preferir especificar os esquemas desejados na política, você poderá definir a coleção AuthenticationSchemes ao adicionar uma política:

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

No exemplo anterior, a política "Over18" só é executada na identity criada pelo manipulador do "Portador". Use a política definindo a propriedade[Authorize] do atributo Policy:

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

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

Usar vários esquemas de autenticação

Alguns aplicativos podem precisar dar suporte a vários tipos de autenticação. Por exemplo, seu aplicativo pode autenticar usuários do Azure Active Directory e de um banco de dados de usuários. Outro exemplo é um aplicativo que autentica usuários do Serviços de Federação do Active Directory (AD FS) e do Azure Active Directory B2C. Nesse caso, o aplicativo deve aceitar um token de portador JWT de vários emissores.

Adicione todos os esquemas de autenticação que você gostaria de aceitar. Por exemplo, o código a seguir adiciona dois esquemas de autenticação de portador JWT com emissores diferentes:

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

Observação

Apenas uma autenticação de portador JWT é registrada com o esquema de autenticação padrão JwtBearerDefaults.AuthenticationScheme. A autenticação adicional deve ser registrada com um esquema de autenticação exclusivo.

Atualize a política de autorização padrão para aceitar os dois esquemas de autenticação. Por exemplo:

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

Como a política de autorização padrão é substituída, é possível usar o atributo [Authorize] em controladores. Em seguida, o controlador aceita solicitações com JWT emitidas pelo primeiro ou segundo emissor.

Consulte essa edição do GitHub ao usar vários esquemas de autenticação.

O exemplo a seguir usa o Azure Active Directory B2C e outro locatário do 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();

No código anterior, ForwardDefaultSelector é usado para selecionar um esquema padrão para a solicitação atual para a qual os manipuladores de autenticação devem encaminhar todas as operações de autenticação por padrão. A lógica de encaminhamento padrão verifica a configuração mais específica ForwardAuthenticate, ForwardChallenge, ForwardForbid, ForwardSignIn e ForwardSignOut primeiro, seguida pela verificação do ForwardDefaultSelector, seguido por ForwardDefault. O primeiro resultado não nulo é usado como o esquema de destino para o qual encaminhar. Para obter mais informações, consulte Esquemas de política no ASP.NET Core.

Para obter uma introdução aos esquemas de autenticação no ASP.NET Core, consulte Esquema de autenticação.

Em alguns cenários, como Aplicativos de Página Única (SPAs), é comum usar vários métodos de autenticação. Por exemplo, o aplicativo pode usar a autenticação baseada cookie em para fazer logon e a autenticação de portador JWT para solicitações JavaScript. Em alguns casos, o aplicativo pode ter várias instâncias de um manipulador de autenticação. Por exemplo, dois manipuladores cookie em que um contém uma identity básica e outro é criado quando uma MFA (autenticação multifator) tiver sido disparada. A MFA pode ser disparada porque o usuário solicitou uma operação que requer segurança extra. Para obter mais informações sobre como impor a MFA quando um usuário solicita um recurso que requer MFA, consulte a seção Proteger a seção com MFA.

Um esquema de autenticação será nomeado quando o serviço de autenticação for configurado durante a autenticação. Por exemplo:

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

No código anterior, dois manipuladores de autenticação foram adicionados: um para cookies e outro para portador.

Observação

Especificar o esquema padrão faz com que a propriedade HttpContext.User seja definida como essa identity. Se esse comportamento não for desejado, desabilite-o invocando a forma sem parâmetros de AddAuthentication.

Como selecionar o esquema com o atributo Authorize

No ponto de autorização, o aplicativo indica o manipulador a ser usado. Selecione o manipulador com o qual o aplicativo autorizará passando uma lista delimitada por vírgulas de esquemas de autenticação para [Authorize]. O atributo [Authorize] especifica o esquema, ou esquema, de autenticação a serem usados independentemente de um padrão ser configurado. Por exemplo:

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

No exemplo anterior, os manipuladores de cookie e de portador são executados e têm a chance de criar e acrescentar uma identity para o usuário atual. Especificando apenas um único esquema, o manipulador correspondente é executado.

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

No código anterior, somente o manipulador com o esquema "Portador" é executado. Todas as identidades baseadas em cookie são ignoradas.

Como selecionando o esquema com políticas

Se preferir especificar os esquemas desejados na política, você poderá definir a coleção AuthenticationSchemes ao adicionar uma política:

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

No exemplo anterior, a política "Over18" só é executada na identity criada pelo manipulador do "Portador". Use a política definindo a propriedade[Authorize] do atributo Policy:

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

Usar vários esquemas de autenticação

Alguns aplicativos podem precisar dar suporte a vários tipos de autenticação. Por exemplo, seu aplicativo pode autenticar usuários do Azure Active Directory e de um banco de dados de usuários. Outro exemplo é um aplicativo que autentica usuários do Serviços de Federação do Active Directory (AD FS) e do Azure Active Directory B2C. Nesse caso, o aplicativo deve aceitar um token de portador JWT de vários emissores.

Adicione todos os esquemas de autenticação que você gostaria de aceitar. Por exemplo, o código a seguir no Startup.ConfigureServices adiciona dois esquemas de autenticação de portador JWT com emissores diferentes:

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

Observação

Apenas uma autenticação de portador JWT é registrada com o esquema de autenticação padrão JwtBearerDefaults.AuthenticationScheme. A autenticação adicional deve ser registrada com um esquema de autenticação exclusivo.

A próxima etapa é atualizar a política de autorização padrão para aceitar ambos os esquemas de autenticação. Por exemplo:

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

Como a política de autorização padrão é substituída, é possível usar o atributo [Authorize] em controladores. Em seguida, o controlador aceita solicitações com JWT emitidas pelo primeiro ou segundo emissor.

Consulte essa edição do GitHub ao usar vários esquemas de autenticação.