Minimal
APIs - JWT Token e autenticação JWT
![]() |
Neste artigo vamos recordar como implementar a autenticação JWT usando minimal APIs. |
Para saber mais sobre o que é o token JWT e como ele funciona consulte as referências no final do artigo.
Este artigo é uma adaptação do artigo original : C# JWT Authentication .NET 6
Configurando a autenticação e autorização
Vamos criar um novo projeto usando o .NET 8 e o template ASP.NET Core Web API com o nome MinApiJwt;
A seguir vamos instalar os pacotes nugets :
Microsoft.AspNetCore.Authentication.JwtBearer
System.IdentityModel.Tokens.Jwt
Ao final o arquivo de projeto deverá possuir as seguintes referências:
<Project
Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup> < ItemGroup><PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" /> </ItemGroup> </ Project> |
Agora vamos definir no arquivo appsettings.json uma seção JwtOptions onde vamos definir os valores para a Audience, Issuer e para a chave privada que iremos usar para gerar o token JWT :
{ "JwtOptions": { "Issuer": "https://localhost:7004", "Audience": "https://localhost:7004", "PrivateKey": "kw#zopfg&ncmj287@09dopcw#1ztspk7x2b&", "ExpireSeconds": 3600 }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } |
Vamos criar uma pasta Token no projeto e nesta pasta criar um record para conter esta configuração e poder usá-la na aplicação :
internal
record
JwtOptions(
string Issuer,string Audience,string PrivateKey,int ExpireSeconds ); |
Com isso podemos ler a configuração e mapeá-la para JwtOptions configurando o serviço com o seguinte código:
var
builder = WebApplication.CreateBuilder(args); var jwtOptions = builder.Configuration.GetSection("JwtOptions") .Get<JwtOptions>(); builder.Services.Configure<JwtOptions> ... |
Agora podemos definir a autorização JwtBearer e a autenticação :
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(opts => { byte[] signingKeyBytes = Encoding.UTF8 .GetBytes(jwtOptions.PrivateKey); opts.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtOptions.Issuer, ValidAudience = jwtOptions.Audience, IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes)| }; }); builder.Services.AddAuthorization(); ... |
Gerando o Token JWT
Para gerar o token vamos criar uma classe contendo dois métodos : Connect e CreateAccessToken
Na pasta Token vamos incluir a classe TokenEndpoint com seguinte código:
using
Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace MinApiJwt.Token;
public class TokenEndpoint var formCollection = await ctx.Request.ReadFormAsync();
// obtem informação do formulário
if (formCollection.TryGetValue("username", out var userName) == false)
if (formCollection.TryGetValue("password", out var password) == false)
//cria o token de acesso(jwt token)
var accessToken = TokenEndpoint.CreateAccessToken(
//retorna o response json com o token
static string CreateAccessToken(
var signingCredentials = new SigningCredentials(
var claims = new List<Claim>() var roleClaims = permissions.Select(x => new Claim("role", x)); claims.AddRange(roleClaims);
var token = new JwtSecurityToken(
var rawToken = new JwtSecurityTokenHandler().WriteToken(token); |
Temos dois métodos estáticos um para realizar a conexão e obter os dados do formulário e outro para gerar o Token JWT.
O método
CreateAccessToken gerará o token com as
declarações:
- sub: identificador exclusivo para o usuário final
- name: nome completo do usuário final
- aud: Público(s) ao qual este Token se destina.
- role: perfis do usuário
O método Connect lê o formulário e valida cada
propriedade antes de gerar o token de acesso. Depois que tudo estiver definido,
chamamos o método CreateAccessToken.
A partir deste passo, implementaremos o endpoint para criar os JWT Tokens (Access Tokens).
A seguir temos o código da classe Program contendo os endpoints:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using MinApiJwt.Token;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
var jwtOptions = builder.Configuration
.GetSection("JwtOptions")
.Get<JwtOptions>();
builder.Services.Configure<JwtOptions>
(builder.Configuration.GetSection("JwtOptions"));
// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
//convert the string signing key to byte array
byte[] signingKeyBytes = Encoding.UTF8
.GetBytes(jwtOptions.PrivateKey);
opts.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtOptions.Issuer,
ValidAudience = jwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes)
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/", () => $"Acessado em {DateTime.UtcNow.ToShortDateString()}")
.WithOpenApi();
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.RequireAuthorization()
.WithOpenApi();
app.MapGet("/public", () => "Endpoint público !!!!")
.AllowAnonymous()
.WithOpenApi();
// as rotas privadas requerem um request autorizado
app.MapGet("/private", () => "Endpoint privado !!!!")
.RequireAuthorization()
.WithOpenApi();
// trata o endpoint do request token
app.MapPost("/tokens/connect", (HttpContext ctx, JwtOptions jwtOptions)
=> TokenEndpoint.Connect(ctx, jwtOptions))
.WithOpenApi();
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
|
Para testar podemos usar o seguinte esqueleto do endpoint neste formato:
POST /conect/token HTTP/1.1 Content-Type:: application/x-www-form-urlencoded grant_type=password&username=johndoe&password=A3ddj3wr |
E a resposta deveria ser:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Date: Thu, 27 Apr 2023 17:35:45 GMT
Server: Kestrel
Transfer-Encoding: chunked
{
"access_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJz......",
"expiration": 3600,
"type": "bearer"
}
|
Abaixo temos os request no arquivo MinApiJwt.http :
@MinApiJwt_HostAddress =
https://localhost:7107 GET {{MinApiJwt_HostAddress}}/weatherforecast/Accept: application/json ### POST https://localhost:7107/tokens/connect HTTP/1.1Content-Type: application/x-www-form-urlencoded grant_type=password&username=johndoe&password=A3ddj3wr ### GET https://localhost:7107/publicAccept: application/json ### GET https://localhost:7107/privateAccept: application/json ### GET https://localhost:7107/privateAuthorization: Bearer eyJhbGciOiJIUzUxMiIs... |
Obs: A porta deve ser alterada para a usada no seu ambiente.
E estamos
conversados...
"E no último dia, o grande dia da festa, Jesus pôs-se em pé, e clamou,
dizendo: Se alguém tem sede, venha a mim, e beba. Quem crê em mim, como diz a
Escritura, rios de água viva correrão do seu ventre."
João 7:37,38
Referências:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)