Implementando
Autenticação nas Minimal APIs
![]() |
Neste artigo explorar o conceito de AuthGuard , — uma abordagem personalizada para autenticar rotas — e demonstra como proteger um endpoint de API mínima que busca uma lista de produtos de um banco de dados. |
Para mostrar isso vamos abordar a criação de uma minimal API, e fazer a adição de um AuthGuard para proteger a rota e a implementação da lógica do guard com trechos de código abrangentes.
Pré-requisitos :
- SDK do .NET 6.0
ou posterior
- Um entendimento básico de ASP.NET Core
- EF Core
Configurando o Projeto e o Contexto do Banco de Dados
Criando um novo projeto ASP.NET Core Web API:
dotnet new
webapi -n ProductApi
cd ProductApi
Vamos incluir no projeto os pacotes:
dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 9.0.6 Microsoft.AspNetCore.Authentication.JwtBearer
Para fins de
demonstração, vamos supor que temos um modelo
Product e um
DbContext
correspondente para interagir com o banco de dados. Aqui está um exemplo
simplificado:
|
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class ProductContext : DbContext { public ProductContext(DbContextOptions<ProductContext> options) : base(options) { } public DbSet<Product> Products { get; set; } } |
Criando uma Minimal API para buscar produtos
Agora vamos criar um endpoint na API para buscar produtos incluindo o código abaixo na classe Program:
| var builder =
WebApplication.CreateBuilder(args); builder.Services.AddDbContext<ProductContext>(opt => // Para simplificar, usando banco de dados em memória opt.UseInMemoryDatabase("ProductsDb")); builder.Services.AddAuthorization(); builder.Services.AddAuthentication(); var app = builder.Build(); app.MapGet("/products", async (ProductContext db) => await db.Products.ToListAsync()) .RequireAuthorization(); // É aqui que adicionaremos nosso AuthGuard app.UseAuthentication(); app.UseAuthorization(); app.Run(); |
Estamos usando
Entity Framework Core com um banco de dados em memória para
demonstração. A parte crucial aqui é o método
.RequireAuthorization(), indicando que a rota
está protegida e requer autenticação.
Implementando a Lógica do AuthGuard
O recurso AuthGuard no ASP.NET Core essencialmente
envolve a configuração de serviços de autenticação e autorização para proteger
suas rotas. Precisamos definir esquemas de autenticação e políticas que serão
usados por nossa API para autenticar requisições.
Para este exemplo,
vamos supor que estamos usando tokens JWT para autenticação. Você normalmente
configuraria o serviço de bearer token JWT no Program.cs ou em um método de
configuração dedicado:
using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Text; ... builder.Services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8 .GetBytes(builder.Configuration["Jwt:Key"])) }; }); |
Para complementar a implementação do AuthGuard no ASP.NET Core com autenticação JWT, você precisará fornecer configurações que incluem o emissor (issuer), o público (audience) e uma chave secreta usada para assinar os tokens.
Essas
configurações são tipicamente armazenadas no arquivo appsettings.json
do seu projeto ASP.NET Core.
O emissor (Jwt:Issuer) é
uma string que identifica a entidade principal que emitiu o JWT. É uma forma de
garantir que o token foi emitido por uma autoridade confiável.
Aqui está
um exemplo de conteúdo do arquivo appsettings.json com a seção
de configurações JWT:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "Jwt": { "Key": "minhasupersenhasecretaeocultapraxuxu", "Issuer": "https://macoratti.com.br", "Audience": "https://macoratti.net" } } |
Nesta configuração
de exemplo temos:
Jwt:Key é sua chave secreta usada para
assinar os tokens JWT. Esta deve ser uma string longa, aleatória e mantida
segura.
Jwt:Issuer é o emissor do token JWT. Isso pode
ser o domínio da sua aplicação ou qualquer identificador que faça sentido para
sua aplicação. Ele é usado para validar o campo iss do token.
Jwt:Audience é o destinatário pretendido do token JWT, tipicamente o
domínio ou identificador da sua API. Ele é usado para validar o campo aud do
token.
Ao configurar o serviço de bearer token JWT em Program.cs, você usa essas configurações para configurar o TokenValidationParameters. A estrutura do ASP.NET Core lê essas configurações de appsettings.json e as usa para validar os tokens recebidos (conforme mostrado no trecho de código acima).
Autenticando Requisições
Com o AuthGuard (autenticação JWT) configurado,
qualquer requisição para /products deve incluir um token JWT
válido no cabeçalho Authorization.
Veja como um cliente pode buscar dados
da rota protegida:
| GET /products HTTP/1.1 Host: localhost:5000 Authorization: Bearer <seu_token_jwt_aqui> |
Somente
requisições com um token JWT válido poderão acessar os dados. Requisições sem um
token ou com um token inválido serão rejeitadas com o código de status
401 Unauthorized.
Ao implementar um AuthGuard
usando os mecanismos de autenticação e autorização do ASP.NET Core, você pode
proteger seus endpoints de forma eficaz. Este artigo demonstrou uma abordagem
prática para adicionar um AuthGuard a uma API mínima para buscar uma lista de
produtos de um banco de dados, focando na simplicidade e modularidade dos
recursos de segurança do ASP.NET Core.
Observação: a
chave para uma segurança eficaz não é apenas adicionar camadas de proteção, mas
também entender e configurá-las adequadamente para atender às necessidades da
sua aplicação.
Para que um cliente possa acessar nosso endpoint
/products protegido, ele primeiro precisa obter um token
JWT. Isso é feito geralmente através de um endpoint de login ou
autenticação, onde o cliente envia suas credenciais (usuário e senha) e, se
válidas, recebe um token de volta.
Acessando a API Protegida (Geração e Uso do Token)
Para que um
cliente possa acessar nosso endpoint /products protegido, ele
primeiro precisa obter um token JWT. Isso é feito geralmente através de um
endpoint de login ou autenticação, onde o cliente envia suas credenciais
(usuário e senha) e, se válidas, recebe um token de volta.
1. Criando um
Endpoint de Login/Autenticação (para Gerar o Token)
Para fins de
demonstração, criaremos um endpoint POST /login muito simples.
Em um cenário real, você validaria as credenciais contra um banco de dados de
usuários. Aqui, faremos uma validação "mockada".
Passo 1:
Adicionar um Modelo para as Credenciais de Login
Crie uma nova
classe LoginRequest.cs em seu projeto:
| namespace ProductApi; public class LoginRequest { public string? Username { get; set; } public string? Password { get; set; } } |
Passo 2:
Configurar a Geração do Token JWT no Program.cs
Precisamos de
uma lógica para criar o token. Isso envolve definir as claims (informações sobre
o usuário), o tempo de expiração e a assinatura do token usando a chave secreta
configurada.
Adicione o seguinte código ao seu Program.cs,
antes de var app = builder.Build();:
app.MapPost("/login", (LoginRequest loginRequest) => { // Em um cenário real, você validaria o username e password // contra seu banco de dados ou serviço de identidade. // Para esta demonstração, vamos simular um usuário válido. if (loginRequest.Username == "macoratti" && loginRequest.Password == "password123") { var issuer = builder.Configuration["Jwt:Issuer"]; var audience = builder.Configuration["Jwt:Audience"]; var key = Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key not configured.")); var securityKey = new SymmetricSecurityKey(key); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); // Adicionar claims (informações sobre o usuário) var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, loginRequest.Username), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(ClaimTypes.Role, "Admin") // Exemplo de claim de role }; var token = new JwtSecurityToken( issuer: issuer, audience: audience, claims: claims, expires: DateTime.Now.AddMinutes(30), // Token expira em 30 minutos signingCredentials: credentials); var jwtToken = new JwtSecurityTokenHandler().WriteToken(token); return Results.Ok(new { Token = jwtToken }); } else { return Results.Unauthorized(); // 401 Unauthorized } }); |
Explicação do
Endpoint /login:
- Ele recebe um LoginRequest (com Username e Password).
- Faz uma validação "mockada" (testuser, password123).
- Se as credenciais
forem válidas, ele constrói um JwtSecurityToken usando as configurações do
appsettings.json.
- Adiciona claims (assuntos, IDs, roles) ao token.
-
Define um tempo de expiração para o token.
- Assina o token com a chave
secreta.
- Retorna o token JWT como uma string na resposta.
2.
Acessando a API Protegida Usando o Token
Agora que temos um
endpoint para gerar o token, podemos mostrar como um cliente (por exemplo, via
Postman, cURL ou código C#) faria as requisições.
Exemplo com cURL (para
Linha de Comando)
Passo 1: Obter o Token de Acesso
Primeiro, faça uma requisição POST para o endpoint /login
para obter o token:
curl -X POST \ http://localhost:7098/login \ -H 'Content-Type: application/json' \ -d '{ "username": "testuser", "password": "password123" }' |
A resposta será algo parecido com:
| { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0dXNlciIsImp0aSI6Ijkw YjEwZTc3LTc4ZDUtNGJhMC1hYzQ3LTczZWEwYTdkM2Y5NyIsImh0dHA6Ly9zY2hlbWFzLm 1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluIi wiZXhwIjoxNzA3MjYzNDkwLCJpc3MiOiJodHRwczovL3lvdXJkb21haW4uY29tIiwiYXVkIjoiaHR 0cHM6Ly95b3VyYXBpLnlvdXJkb21haW4uY29tIn" } |
Copie o valor da
propriedade "token".
Passo 2: Usar o Token para Acessar o
Endpoint Protegido (/products)
Agora, use o token obtido no
cabeçalho Authorization com o prefixo Bearer:
| curl -X GET \ http://localhost:7098/products \ -H 'Authorization: Bearer <seu_token_jwt_copiado_aqui>' |
E estamos conversados...
"Bendito o Deus e Pai de nosso Senhor Jesus Cristo, o
qual nos abençoou com todas as bênçãos espirituais nos lugares celestiais em
Cristo"
Efésios 1:3
Referências:
NET - Unit of Work - Padrão Unidade de ...