Minimal API com Autenticação e Autorização JWT e Bearer

Proteger uma Minimal API com JWT é uma das tarefas mais comuns para quem cria serviços HTTP no ASP.NET Core. Neste guia, você vai montar do zero uma minimal api JWT que emite tokens, valida cada requisição e separa o acesso entre usuários comuns e administradores.

O que você vai construir com Minimal API e JWT

Ao final do tutorial, a aplicação terá quatro endpoints com níveis de acesso diferentes. Assim, você consegue observar na prática como a autenticação e a autorização se complementam.

O primeiro endpoint recebe as credenciais e devolve um token. Em seguida, um endpoint aberto responde a qualquer pessoa, outro exige apenas um token válido e o último exige o perfil de administrador.

Os exemplos seguem a documentação do ASP.NET Core 9 usada como referência no material original. Além disso, todo o código fica no Program.cs e em poucas classes auxiliares, porque a ideia da minimal api JWT é justamente reduzir a cerimônia.

Conceitos fundamentais

Antes de escrever código, vale alinhar três conceitos que aparecem em todo o artigo: Minimal API, Bearer e JWT. Dessa forma, cada linha de configuração fica mais fácil de entender.

O que é uma Minimal API

A Minimal API é uma abordagem do ASP.NET Core que simplifica bastante a criação de APIs. Em vez de controllers, atributos de rota e classes base, você declara cada endpoint com métodos como MapGet e MapPost.

O objetivo é reduzir a quantidade de código em comparação com o ASP.NET Core MVC. Por isso, a abordagem combina muito bem com microsserviços e APIs pequenas.

Se você ainda não conhece o modelo, o artigo ASP.NET Minimal APIs apresenta a estrutura básica antes de entrarmos na segurança.

O que é o esquema Bearer

Bearer é um esquema de autenticação baseado em token, descrito na RFC 6750. O cliente envia o token de acesso no cabeçalho Authorization de cada requisição HTTP.

A palavra "bearer" significa portador. Ou seja, qualquer pessoa que possua o token consegue usar o recurso protegido, por isso o token precisa trafegar sempre por HTTPS.

Quando o servidor recebe um token correto, ele concede ao cliente os privilégios associados àquele token. Assim, o acesso acontece de forma segura e sem sessão guardada no servidor.

O que é um JWT e como ele se divide

O JWT (JSON Web Token) é um formato de token definido na RFC 7519 e muito usado para autenticação e troca segura de informações. Ele transporta dados em objetos JSON assinados digitalmente, de forma compacta e autônoma.

Um token JWT tem três partes separadas por pontos: o cabeçalho (header), a carga útil (payload) e a assinatura (signature). Cada parte segue a codificação Base64Url, que deixa o token seguro para URLs e cabeçalhos.

O cabeçalho define o tipo do token, que no caso é sempre "JWT", e o algoritmo de assinatura. Já a carga útil contém as claims, como a identidade do usuário, o e-mail e as permissões.

A assinatura nasce da combinação do cabeçalho, da carga útil e de uma chave secreta que só o servidor emissor conhece. Com isso, o servidor verifica a integridade dos dados e garante que ninguém alterou o token depois da emissão.

Vale um alerta importante: a carga útil fica apenas codificada, e não criptografada. Portanto, nunca coloque senhas ou dados sensíveis dentro das claims.

O artigo O que é JWT aprofunda esse formato.

Criando o projeto da Minimal API com JWT

O ponto de partida é o template web, o mais enxuto do ASP.NET Core. Ele já entrega tudo o que precisamos para expor os nossos próprios endpoints na minimal api JWT.

dotnet new web -o article-jwt-auth
cd article-jwt-auth

O primeiro comando cria a pasta article-jwt-auth com um Program.cs mínimo. Em seguida, o segundo comando entra na pasta para os próximos passos.

Instalando os pacotes via NuGet

O material original adiciona dois pacotes: Microsoft.AspNetCore.Authentication e Microsoft.AspNetCore.Authentication.JwtBearer. Eles reúnem os recursos de autenticação e o handler que entende tokens JWT.

dotnet add package Microsoft.AspNetCore.Authentication
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Na prática, a infraestrutura de autenticação já faz parte do framework compartilhado do ASP.NET Core desde a versão 3.0. Por isso, o pacote realmente indispensável é o Microsoft.AspNetCore.Authentication.JwtBearer, que a documentação oficial de Minimal APIs também cita.

Com o projeto criado e os pacotes instalados, a base está pronta. Então podemos organizar a configuração e as classes de apoio.

Organizando a configuração e as classes

Nesta etapa, definimos a chave de assinatura, o modelo de usuário e os serviços que leem a configuração e geram o token. Cada peça fica em um arquivo próprio para manter o projeto legível.

Chave de assinatura no appsettings.json

Primeiro, adicione uma seção Jwt ao arquivo appsettings.json com um valor de sua escolha. A aplicação usa essa chave para assinar o token na emissão e para conferir a assinatura a cada requisição.

"Jwt": {
  "Key": "f3894067-e4b1-47ba-bee1-8ce3364cc44a"
}

O algoritmo HMAC SHA256 exige uma chave com pelo menos 256 bits, conforme a RFC 7518. Como o valor acima tem 36 caracteres, ele passa desse limite com folga.

Em produção, porém, a chave não deve ficar no appsettings.json versionado. Prefira o Secret Manager no desenvolvimento e um cofre de segredos, como o Azure Key Vault, nos demais ambientes.

Classes User e JwtSettings

Agora crie duas classes: User, no arquivo User.cs, e JwtSettings, no arquivo JwtSettings.cs. A primeira representa quem faz login e a segunda espelha a seção Jwt da configuração.

public class User
{
    public string Email { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    // Perfil padrão do exemplo; altere para testar outros acessos
    public string Role { get; set; } = "Admin";
}

public class JwtSettings
{
    public string Key { get; set; } = string.Empty;
}

Repare que a propriedade Role começa com o valor Admin. Mais adiante, você pode trocar esse valor para ver a autorização bloquear o endpoint de administrador.

Serviço que gera o token JWT na Minimal API

Além das classes de modelo, o exemplo usa duas classes estáticas: AppSettingsService, no arquivo AppSettingsService.cs, e JwtBearerService, no arquivo JwtBearerService.cs. A primeira lê a configuração e a segunda gera o token da minimal api JWT.

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public static class AppSettingsService
{
    public static JwtSettings JwtSettings { get; }

    static AppSettingsService()
    {
        // Lê o appsettings.json uma única vez, no primeiro acesso
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        JwtSettings = configuration.GetSection("Jwt").Get<JwtSettings>() ?? new JwtSettings();
    }
}

public static class JwtBearerService
{
    public static string GenerateToken(User user)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(AppSettingsService.JwtSettings.Key);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            // Claims que vão para o payload do token
            Subject = new ClaimsIdentity(
            [
                new Claim(ClaimTypes.Name, user.Name),
                new Claim(ClaimTypes.Email, user.Email),
                new Claim(ClaimTypes.Role, user.Role)
            ]),
            Expires = DateTime.UtcNow.AddHours(2),
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha256Signature)
        };

        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }
}

O método GenerateToken monta uma ClaimsIdentity com nome, e-mail e role do usuário. Depois, ele define a expiração em duas horas e assina o conteúdo com HMAC SHA256.

O original convertia a chave com Encoding.ASCII na geração e com Encoding.UTF8 na validação. Como a chave tem apenas caracteres ASCII, o resultado coincide, mas usar UTF8 nos dois lados evita surpresas se a chave mudar.

Também vale saber que, a partir do .NET 8, o handler de JwtBearer valida tokens com o JsonWebTokenHandler por padrão. Mesmo assim, tokens emitidos pelo JwtSecurityTokenHandler continuam compatíveis, porque ambos seguem o mesmo padrão JWT.

Adicionando autenticação e autorização JWT na Minimal API

Até aqui, temos um usuário e um serviço que gera o token com as claims desse usuário. Agora precisamos avisar a minimal api JWT que ela deve autenticar requisições, autorizar perfis e interpretar tokens nesse formato.

Com essa configuração, a aplicação valida o token a cada requisição. Assim, conseguimos definir quais perfis acessam cada endpoint.

builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        // Apenas para desenvolvimento local; em produção mantenha true
        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(AppSettingsService.JwtSettings.Key)),
            ValidateIssuer = false,
            ValidateAudience = false
        };
    });

builder.Services.AddAuthorization();

O método AddAuthentication registra o esquema Bearer como padrão para autenticar e para desafiar requisições sem token. Em seguida, o AddJwtBearer informa como validar a assinatura com a mesma chave usada na emissão.

Neste exemplo, ValidateIssuer e ValidateAudience ficam desligados para simplificar. Em um cenário real, porém, ligue as duas validações e defina ValidIssuer e ValidAudience, pois elas impedem que tokens de outro sistema sejam aceitos.

Registrando o middleware

Em seguida, ative os middlewares de autenticação e autorização no mesmo Program.cs, logo depois do builder.Build(). A ordem importa: primeiro a aplicação identifica o usuário e só depois decide o que ele pode acessar.

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

Segundo a documentação da Microsoft, o WebApplication registra esses middlewares automaticamente quando você chama AddAuthentication ou AddAuthorization. Ainda assim, a chamada explícita deixa a ordem visível e se torna necessária quando outros middlewares, como o CORS, precisam rodar antes.

Criando os endpoints protegidos da Minimal API com JWT

Ainda no Program.cs, vamos adicionar quatro endpoints. Cada um demonstra um nível diferente de proteção na minimal api JWT.

Endpoint de autenticação

O primeiro endpoint recebe o usuário, confere as credenciais e devolve o token. Por isso, ele precisa aceitar chamadas anônimas.

app.MapPost("/v1/authenticate/", [AllowAnonymous] (User user) =>
{
    // Validação fixa apenas para fins didáticos
    if (user.Email == "user@domain.com" && user.Password == "StrongPassword")
        return Results.Ok(JwtBearerService.GenerateToken(user));

    return Results.Unauthorized();
});

A comparação com valores fixos serve só para o exemplo. Em um sistema real, você consultaria o banco de dados e compararia o hash da senha, nunca a senha em texto puro.

Endpoints anônimo, autenticado e de administrador

O segundo endpoint não exige autenticação nem autorização. Para isso, basta marcar o handler com o atributo AllowAnonymous.

app.MapGet("/v1/anonymous/", [AllowAnonymous] () => "Anonymous");

O terceiro endpoint exige apenas um token válido. Nesse caso, chamamos o método RequireAuthorization sem parâmetros.

app.MapGet("/v1/user/", () => "User").RequireAuthorization();

Por fim, o quarto endpoint exige autenticação e o perfil Admin. Para isso, combinamos o atributo Authorize com a propriedade Roles e mantemos o RequireAuthorization.

app.MapGet("/v1/admin/", [Authorize(Roles = "Admin")] () => "Administrator")
    .RequireAuthorization();

Quando o token está ausente, expirado ou com assinatura inválida, a API responde 401 Unauthorized. Já quando o token é válido, mas o usuário não tem a role exigida, a resposta passa a ser 403 Forbidden.

Testando a Minimal API JWT no Postman

Com tudo pronto, execute a aplicação com dotnet run. Em seguida, use o Postman para disparar as requisições contra a minimal api JWT.

Primeiro, envie um POST para /v1/authenticate/ com o e-mail e a senha no corpo em JSON. A resposta traz o token que você vai reaproveitar nas próximas chamadas.

Postman enviando credenciais ao endpoint de autenticação da minimal api JWT e recebendo o token na resposta
Requisição de login que devolve o token de acesso.

Depois, inclua o token no cabeçalho das requisições que exigem autenticação, sempre neste formato:

Key: Authorization
Value: Bearer YOUR_TOKEN

O prefixo Bearer seguido de um espaço é obrigatório. Sem ele, o handler não reconhece o esquema e devolve 401.

Postman chamando um endpoint protegido com o cabeçalho Authorization preenchido com o token Bearer
Chamada a um endpoint protegido usando o token no cabeçalho.

Além disso, sinta-se à vontade para alterar a role padrão na classe User. Com outra role, o endpoint /v1/user/ continua respondendo, enquanto o /v1/admin/ passa a devolver 403.

Para testes locais, o ASP.NET Core também oferece a ferramenta dotnet user-jwts. Ela cria tokens de desenvolvimento e ajusta a configuração do projeto automaticamente.

Inspecionando o token no jwt.io

Para entender o que viaja dentro do token, cole o valor recebido no jwt.io. A ferramenta decodifica o cabeçalho e a carga útil e mostra cada claim separadamente.

Token JWT decodificado no jwt.io exibindo header, payload com claims de nome, e-mail e role, e assinatura
Cabeçalho, carga útil e assinatura do token decodificados.

Repare que qualquer pessoa consegue ler o payload sem conhecer a chave. Isso confirma o alerta anterior: a assinatura protege a integridade, mas não esconde o conteúdo.

Boas práticas de segurança para Minimal API com JWT

O exemplo prioriza a didática, então alguns pontos merecem atenção antes de levar a minimal api JWT para produção. A lista abaixo resume os ajustes mais importantes.

  • Guarde a chave fora do código, em variáveis de ambiente ou em um cofre de segredos.
  • Ative ValidateIssuer e ValidateAudience e informe valores esperados.
  • Mantenha RequireHttpsMetadata como true fora do ambiente local.
  • Use expirações curtas e, se necessário, um fluxo de refresh token.
  • Nunca compare senhas em texto puro; armazene e compare hashes.

Para quem quer ir além, o artigo boas práticas de segurança em APIs ASP.NET Core cobre outros cuidados. Também vale conhecer a versão com controllers em autenticação e autorização com JWT e Bearer no ASP.NET 7.

Código fonte

O projeto completo deste tutorial está disponível no repositório jfs-dev/article-jwt-auth. Dessa forma, você pode clonar, executar e comparar com a sua implementação.

Estrutura do projeto article-jwt-auth com Program.cs, classes User, JwtSettings e serviços de token
Visão geral do projeto de exemplo no repositório.

Conclusão sobre Minimal API com JWT

Com poucas classes e algumas linhas no Program.cs, você montou uma minimal api JWT completa. Ela emite tokens assinados, valida cada requisição e separa o acesso por perfil.

O mais importante é entender o papel de cada peça. O esquema Bearer define como o token viaja, o JWT define o formato e o ASP.NET Core faz a validação.

A partir dessa base, o próximo passo natural é endurecer a configuração com emissor, audiência e segredos protegidos. Assim, a mesma estrutura serve de ponto de partida para APIs reais.

FAQ

Preciso instalar o pacote Microsoft.AspNetCore.Authentication?

Não nas versões atuais do ASP.NET Core, porque a autenticação já faz parte do framework compartilhado. O pacote necessário para validar JWT é o Microsoft.AspNetCore.Authentication.JwtBearer.

Qual a diferença entre 401 e 403 na API?

O 401 indica token ausente, expirado ou inválido. Já o 403 indica que o usuário se autenticou, mas não tem permissão para aquele endpoint.

O conteúdo do JWT é criptografado?

Não, o payload fica apenas codificado em Base64Url e qualquer pessoa consegue lê-lo. A assinatura só garante que ninguém alterou o conteúdo.

Preciso chamar UseAuthentication e UseAuthorization?

O WebApplication registra os dois middlewares automaticamente quando os serviços existem. A chamada explícita ajuda quando você precisa controlar a ordem do pipeline.

Como gerar tokens de teste sem criar um endpoint de login?

Use a ferramenta dotnet user-jwts, que cria tokens de desenvolvimento e configura o projeto. Ela aceita opções como role e scope.