Compartilhar via


Configurar a autenticação do portador JWT no ASP.NET Core

Por Damien Bowden

A autenticação JWT (JSON Web Token) do portador é comumente utilizada para APIs. Embora funcione de forma semelhante à autenticação cookie, o provedor de identidade emite um JWT ou tokens após autenticação bem-sucedida. Esses tokens podem ser enviados a outros servidores para autenticação, ao contrário dos cookies que só são enviados de volta para o domínio emissor. Um JWT é um token autocontido que encapsula informações para um recurso de API ou um cliente. O cliente que solicitou o JWT pode solicitar dados de um recurso de API usando o cabeçalho de autorização e um token de portador.

A Autenticação do Portador JWT fornece:

  • Autenticação: ao usar o JwtBearerHandler, os tokens de portador são essenciais para a autenticação. O JwtBearerHandler valida o token e extrai a identidade do usuário de suas declarações.
  • Autorização: os tokens de portador habilitam a autorização fornecendo uma coleção de declarações que representam as permissões do usuário ou do aplicativo, assim como um cookie.
  • Autorização Delegada:: quando um token de acesso específico do usuário é usado para autenticar entre APIs em vez de um token de acesso em todo o aplicativo, esse processo é conhecido como autorização delegada.

Para obter uma introdução à JWT Bearer Authentication, consulte JSON Web Tokens.Exibir ou baixar o código de exemplo

Esse artigo aborda os seguintes tópicos:

  • Tipos de token
  • Usando tokens JWT para proteger uma API
  • Como o OIDC/OAuth se encaixa nisso?
  • Implementar a autenticação de token de portador JWT
  • Abordagens recomendadas para criar um JWT

Tipos de token

Há diversos tipos de tokens e formatos. Não é recomendável gerar seus próprios tokens de acesso ou tokens de ID, exceto para teste. Tokens autocriados que não seguem os padrões estabelecidos:

  • Pode levar a vulnerabilidades de segurança.
  • São adequados apenas para sistemas fechados.

Recomenda-se usar OpenID Connect 1.0 ou um padrão OAuth para criar tokens de acesso destinados ao acesso à API.

Tokens de acesso

Tokens de acesso:

  • São cadeias de caracteres usadas por um aplicativo cliente para fazer solicitações ao servidor que implementa uma API.
  • Pode variar no formato. Diferentes APIs podem usar diferentes formatos para os tokens.
  • Podem ser criptografados.
  • Nunca deve ser lido ou interpretado por um cliente Web ou aplicativo de IU que contém o token de acesso.
  • Destinam-se exclusivamente a fazer solicitações a uma API.
  • Normalmente, são enviados para a API no cabeçalho de solicitação de Autorização como um token de portador.

Consulte O Framework de Autorização OAuth 2.0

Tokens de acesso de aplicativo e tokens de acesso delegados

Os tokens de acesso podem ser tanto tokens de acesso de aplicativo ou tokens de acesso delegados. Os tokens têm diferentes declarações e são gerenciados e armazenados de forma diferente. Um token de acesso do aplicativo normalmente é armazenado no aplicativo até expirar, enquanto um token de acesso delegado é armazenado para cada usuário, seja em um cookie ou em um cache de servidor seguro.

É recomendável usar tokens de acesso de usuário delegados sempre que um usuário estiver envolvido. As APIs a jusante podem solicitar um token de acesso de usuário delegado em nome do usuário autenticado.

Tokens de acesso restrito do remetente

Os tokens de acesso podem ser usados como tokens de portador ou tokens restritos ao remetente para acessar recursos. Os tokens restritos ao remetente exigem que o cliente solicitante comprove a posse de uma chave privada para usar o token. Comprovar a posse de uma chave privada garante que o token não possa ser usado de forma independente. Os tokens restritos ao remetente podem ser implementados de duas formas:

Tokens de ID

Tokens de ID são tokens de segurança que confirmam a autenticação bem-sucedida de um usuário. Os tokens permitem que o cliente verifique a identidade do usuário. O servidor de token JWT emite tokens de ID que contêm declarações com informações do usuário. Os tokens de ID estão sempre em formato JWT.

Os tokens de ID nunca devem ser usados para acessar APIs.

Outros tokens

Há muitos tipos de tokens, incluindo tokens de acesso e ID, conforme especificado pelos padrões OpenID Connect e OAuth. Os tokens de atualização podem ser usados para atualizar um aplicativo de IU sem autenticar novamente o usuário. Tokens JAR OAuth podem enviar solicitações de autorização com segurança. Fluxos de credenciais verificáveis usam tipos JWT para emitir ou verificar credenciais. É fundamental usar tokens conforme as especificações. Consulte os links de padrões fornecidos mais adiante neste artigo para obter mais informações.

Usando tokens JWT para proteger uma API

Ao usar tokens de acesso JWT para autorização de API, a API concede ou nega acesso com base no token fornecido. Se a solicitação não estiver autorizada, uma resposta 401 ou 403 será retornada. A API não deve redirecionar o usuário para o provedor de identidade para obter um novo token ou solicitar permissões adicionais. O aplicativo que consome a API é responsável por adquirir um token adequado. Isso garante uma separação clara das preocupações entre a API (autorização) e o aplicativo cliente consumidor (autenticação).

Observação

O HTTP também permite o retorno 404 por não autorizado, de modo a não vazar informações sobre a existência de recursos para clientes não autorizados.

401 Não Autorizado

Uma resposta 401 Não autorizado indica que o token de acesso fornecido não atende aos padrões necessários. Isso pode ser devido a diversos motivos, como:

  • Assinatura inválida: a assinatura do token não corresponde, o que sugere possíveis adulterações.
  • Expiração: o token expirou e não é mais válido.
  • Declarações incorretas: declarações críticas dentro do token, como a audiência (aud) ou o emissor (iss), estão ausentes ou inválidas.

Observação

No RFC 9110 da Semântica HTTP: o servidor que gera uma resposta 401 deve enviar um campo de cabeçalho WWW-Authenticate (Seção 11.6.1) contendo pelo menos um desafio aplicável ao recurso de destino.

As especificações OAuth fornecem diretrizes detalhadas sobre as declarações necessárias e sua validação.

403 Proibido

Uma resposta 403 Proibido geralmente indica que o usuário autenticado não tem as permissões necessárias para acessar o recurso solicitado. Isso é diferente de problemas de autenticação, por exemplo, um token inválido, e não está relacionado às declarações padrão dentro do token de acesso.

No ASP.NET Core, você pode impor a autorização usando:

Requisitos e políticas: defina requisitos personalizados, por exemplo, "Deve ser um administrador" e associe-os às políticas. Autorização baseada em função: atribua usuários a funções, por exemplo, "Administrador", "Editor" e restrinja o acesso com base nessas funções.

Qual função tem OIDC e/ou OAuth ao usar tokens de portador?

Quando uma API usa tokens de acesso JWT para autorização, a API valida apenas o token de acesso, não como o token foi obtido.

O OpenID Connect (OIDC) e o OAuth 2.0 fornecem estruturas padronizadas e seguras para aquisição de token. A aquisição de token varia conforme o tipo de aplicativo. Devido à complexidade da aquisição de token seguro, é altamente recomendável confiar nesses padrões:

  • Para aplicativos que atuam em nome de um usuário e de um aplicativo: o OIDC é a escolha preferencial, permitindo acesso delegado do usuário. Em aplicativos Web, o fluxo de código confidencial com Chave de Prova para Troca de Código (PKCE) é recomendado para segurança aprimorada.
  • Se o aplicativo não tiver usuário: o fluxo de credenciais do cliente OAuth 2.0 é adequado para obter tokens de acesso do aplicativo.

Implementar a autenticação de token de portador JWT

O pacote Nuget Microsoft.AspNetCore.Authentication.JwtBearer pode ser usado para validar os tokens portador JWT.

Os tokens de portador JWT devem ser totalmente validados em uma API. O seguinte deve ser validado:

  • Assinatura, para confiança e integridade. Isso garante que o token foi criado pelo serviço de token seguro designado e não foi adulterado.
  • Declaração do emissor com o valor esperado.
  • Declaração de audiência com o valor esperado.
  • Expiração do token.

As declarações a seguir são necessárias para tokens de acesso OAuth 2.0: iss, exp, aud, sub, client_id, iat e jti.

Se alguma dessas declarações ou valores estiver incorreta, a API deverá retornar uma resposta 401.

Validação básica do token de portador JWT

Uma implementação básica do AddJwtBearer pode validar apenas a audiência e o emissor. A assinatura deve ser validada para que o token seja confiável e não tenha sido adulterado.

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtOptions =>
{
	jwtOptions.Authority = "https://{--your-authority--}";
	jwtOptions.Audience = "https://{--your-audience--}";
});

Validação explícita do token portador JWT

O método AddJwtBearer fornece várias configurações. Alguns provedores de token seguro usam um endereço de metadados não padrão e o parâmetro pode ser configurado de forma explícita. A API pode aceitar vários emissores ou audiências.

Não é necessário definir os parâmetros de forma explícita. As definições dependem dos valores de declaração do token de acesso e do servidor seguro de tokens usado para validar o token de acesso. Você deve usar os valores padrão, se possível.

Consulte Mapeamento de declarações detalhes de MapInboundClaims.

builder.Services.AddAuthentication()
.AddJwtBearer("some-scheme", jwtOptions =>
{
	jwtOptions.MetadataAddress = builder.Configuration["Api:MetadataAddress"];
	// Optional if the MetadataAddress is specified
	jwtOptions.Authority = builder.Configuration["Api:Authority"];
	jwtOptions.Audience = builder.Configuration["Api:Audience"];
	jwtOptions.TokenValidationParameters = new TokenValidationParameters
	{
		ValidateIssuer = true,
		ValidateAudience = true,
		ValidateIssuerSigningKey = true,
		ValidAudiences = builder.Configuration.GetSection("Api:ValidAudiences").Get<string[]>(),
		ValidIssuers = builder.Configuration.GetSection("Api:ValidIssuers").Get<string[]>()
	};

	jwtOptions.MapInboundClaims = false;
});

JWT com vários esquemas

Geralmente, as APIs precisam acomodar tokens de acesso de vários emissores. O suporte a vários emissores de token em uma API pode ser realizado da seguinte forma:

  • APIs separadas: crie APIs distintas com esquemas de autenticação dedicados para cada emissor.
  • AddPolicyScheme Esse método pode definir vários esquemas de autenticação e implementar a lógica para selecionar o esquema apropriado com base em propriedades de token (por exemplo, emissor, declarações). Essa abordagem permite maior flexibilidade em uma única API.

Forçando a autenticação do portador

SetDefaultPolicy pode ser usado para exigir autenticação em todas as solicitações, mesmo para endpoints sem um atributo [Authorize]. SetDefaultPolicy configura a política usada para endpoints com o atributo [Authorize] e já vem configurada para exigir usuários autenticados. Consulte a Exigência de documentação de usuários autenticados para obter mais detalhes.

var requireAuthPolicy = new AuthorizationPolicyBuilder()
	.RequireAuthenticatedUser()
	.Build();

builder.Services.AddAuthorizationBuilder()
	.SetDefaultPolicy(requireAuthPolicy);

O atributo Authorize também pode ser usado para forçar a autenticação. Se vários esquemas forem usados, o esquema de portador geralmente precisará ser definido como o esquema de autenticação padrão ou especificado por meio de [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme]).

Autorização nos controladores

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{

Autorização em APIs mínimas:

app.MapGet("/hello", [Authorize] () => "Hi");

A manipulação insegura de tokens de acesso, como autenticação fraca ou armazenamento de tokens no armazenamento vulnerável do lado do cliente, pode levar a vulnerabilidades de segurança significativas. Por exemplo, armazenar tokens de acesso diretamente no navegador usando armazenamento local, armazenamento de sessão ou web workers. A seção abaixo contém as práticas recomendadas para aplicativos que usam e criam tokens de acesso.

Padrões de uso

Padrões como OpenID Connect ou OAuth devem sempre ser usados ao criar tokens de acesso. Os tokens de acesso não devem ser criados em aplicativos de produção sem aderir às precauções de segurança descritas neste artigo. A criação de tokens de acesso deve ser limitada a cenários de teste.

Usar chaves assimétricas

As chaves assimétricas devem sempre ser usadas ao criar tokens de acesso. A chave pública está disponível nos pontos de extremidade conhecidos e os clientes de API podem validar a assinatura do token de acesso usando a chave pública.

Nunca crie um token de acesso a partir de uma solicitação de nome de usuário/senha

Você NÃO deve criar um token de acesso a partir de uma solicitação de nome de usuário/senha. Solicitações de nome de usuário/senha não são autenticadas e são vulneráveis a ataques de falsificação de identidade e phishing. Os tokens de acesso só devem ser criados usando um fluxo OpenID Connect ou um fluxo padrão OAuth. Desviar desses padrões pode resultar em um aplicativo inseguro.

Uso de cookies

Para aplicativos Web seguros, um back-end é necessário para armazenar tokens de acesso em um servidor confiável. Apenas um cookie seguro somente para HTTP é compartilhado no navegador do cliente. Consulte a documentação de autenticação OIDC para saber como fazer isso em um aplicativo Web ASP.NET Core.

APIs downstream

As APIs ocasionalmente precisam acessar dados do usuário de APIs downstream em nome do usuário autenticado no aplicativo de chamada. Embora implementar um fluxo de credenciais do cliente OAuth seja opcional, requer total confiança entre os dois aplicativos de API. Uma abordagem mais segura envolve usar uma estratégia de confiança zero com um token de acesso de usuário delegado. Essa abordagem:

  • Aprimora a segurança concedendo à API apenas as permissões necessárias para esse usuário específico.
  • Requer que a API crie o novo token de acesso para o usuário que chama o aplicativo e a API.

Há diversas formas de implementar uma estratégia de confiança zero com um token de acesso de usuário delegado:

Usar o OAuth 2.0 Token Exchange para solicitar um novo token de acesso delegado

Essa é uma boa forma de implementar esse requisito, mas é complicado se você precisar implementar o fluxo OAuth.

Consulte Troca de tokens OAuth 2.0

Usar o Microsoft Identity Web em nome do Flow para solicitar um novo token de acesso delegado

Usar a biblioteca de autenticação da Web Identity Microsoft é a abordagem mais fácil e segura. Ele só funciona com Microsoft Entra ID e Microsoft Entra External ID.

Para obter mais informações, consulte Plataforma de identidade da Microsoft e o fluxo On-Behalf-Of do OAuth 2.0.

Usar o mesmo token de acesso delegado enviado à API

Essa abordagem não é difícil de implementar, mas o token de acesso tem acesso a todas as APIs downstream. O proxy reverso yarp pode ser usado para implementar isso.

Usar o fluxo de credenciais do cliente OAuth e usar um token de acesso do aplicativo

Isso é fácil de implementar, mas o aplicativo cliente tem acesso completo ao aplicativo e não um token de acesso delegado. O token deve ser armazenado em cache no aplicativo de API do cliente.

Observação

Qualquer segurança de aplicativo para aplicativo também funciona. A autenticação de certificado ou, no Azure, uma identidade gerenciada, podem ser usadas.

Manipulação de tokens de acesso

Ao usar tokens de acesso em um aplicativo cliente, os tokens de acesso precisam ser rotacionados, persistidos e armazenados em algum lugar no servidor. Em um aplicativo web, cookies são usados para proteger a sessão e podem ser usados para armazenar tokens por meio da opção SaveTokens.

SaveTokens não atualizará automaticamente os tokens de acesso, mas essa funcionalidade está planejada para o .NET 10. Siga https://github.com/dotnet/aspnetcore/issues/8175 para obter atualizações. Enquanto isso, você pode atualizar manualmente o token de acesso como demonstrado no Blazor Web App com a documentação do OIDC ou usar um pacote NuGet de terceiros, como Duende.AccessTokenManagement.OpenIdConnect para manipular e gerenciar tokens de acesso no aplicativo cliente. Para obter mais informações, consulte o Gerenciamento de tokens Duende.

Observação

Se estiver implantando na produção, o cache deverá funcionar em uma implantação de várias instâncias. Geralmente, é necessário um cache persistente.

Alguns servidores de token seguro criptografam os tokens de acesso. Os tokens de acesso não exigem nenhum formato. Ao usar a introspecção OAuth, um token de referência é usado em vez de um token de acesso. Um aplicativo cliente (IU) nunca deve abrir um token de acesso, pois o token de acesso não se destina a isso. Apenas uma API para a qual o token de acesso foi criado deve abrir o token de acesso.

  • Não abra tokens de acesso em um aplicativo da interface do usuário
  • Não enviar o token de ID às APIs
  • Os tokens de acesso podem ter qualquer formato
  • Os tokens de acesso podem ser criptografados
  • Tokens de acesso expiram e precisam ser renovados
  • Os tokens de acesso são persistentes em um servidor de back-end seguro

YARP (mais um proxy reverso)

YARP (Outro Proxy Reverso) é uma tecnologia útil para lidar com solicitações HTTP e encaminhar as solicitações para outras APIs. O YARP pode implementar a lógica de segurança para adquirir novas credenciais de acesso. O YARP é frequentemente usado ao adotar a arquitetura de segurança de Back-end para Front-end (BFF).

Para exemplos de Blazor que usam YARP para implementar o padrão BFF, consulte os seguintes artigos:

Para obter um Blazor exemplo que usa YARP para implementar o padrão BFF, consulte Proteja um ASP.NET Core Blazor Web App com OpenID Connect (OIDC).

Para obter mais informações, consulte auth0: o padrão Backend para Frontend.

Testar APIs

Testes de integração e contêineres com tokens de acesso podem ser usados para testar APIs seguras. Os tokens de acesso podem ser criados usando a ferramenta dotnet user-jwts.

Aviso

Certifique-se de que problemas de segurança não sejam introduzidos na API para fins de teste. O teste se torna mais desafiador quando tokens de acesso delegados são usados, pois esses tokens só podem ser criados por meio de uma interface do usuário e de um fluxo do OpenID Connect. Se uma ferramenta de teste for usada para criar tokens de acesso delegados, os recursos de segurança deverão ser desabilitados para teste. É fundamental que esses recursos só estejam desabilitados no ambiente de teste.

Crie ambientes de teste dedicados e isolados onde os recursos de segurança possam ser desabilitados ou modificados com segurança. Verifique se essas alterações estão estritamente limitadas ao ambiente de teste.

Usar Swagger UI, Curl e outras ferramentas de interface de API

A interface do usuário do Swagger e o Curl são excelentes ferramentas de interface do usuário para testar APIs. Para que as ferramentas funcionem, a API pode produzir um documento OpenAPI e este pode ser carregado na ferramenta de teste do cliente. Um fluxo de segurança para adquirir um novo token de acesso pode ser adicionado ao arquivo OpenAPI da API.

Aviso

Não implante fluxos de teste de segurança inseguros na produção.

Ao implementar uma interface do usuário do Swagger para uma API, geralmente você não deve implantar a interface do usuário em produção, pois a segurança deve ser enfraquecida para permitir que isso funcione.

Mapear declarações do OpenID Connect

Consulte a seguinte documentação:

Mapeamento, personalização e transformação de declarações no ASP.NET Core

Padrões

Token Web JSON (JWT)

Estrutura de Autorização OAuth 2.0

OAuth 2.0 Demonstrando Prova de Posse DPoP

OAuth 2.0 JWT-Secured Solicitação de Autorização (JAR) RFC 9101

Autenticação de cliente OAuth 2.0 Mutual-TLS e tokens de acesso associados a certificados

OpenID Connect 1.0

Plataforma de identidade da Microsoft e o fluxo On-Behalf-Of de OAuth 2.0

Troca de tokens OAuth 2.0

Perfil Token Web JSON (JWT) para Tokens de Acesso OAuth 2.0

Semântica HTTP RFC 9110