Rust

Como Usar Autenticação JWT em APIs Rust com Axum em Produção

8 min de leitura

Como Usar Autenticação JWT em APIs Rust com Axum em Produção

Fundamentos de JWT e Segurança em APIs JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma forma compacta e auto-contida de transmitir informações entre partes de forma segura. Um JWT é composto por três partes separadas por pontos: header (tipo de token e algoritmo), payload (dados do usuário) e signature (assinatura criptográfica). A grande vantagem é que o token é stateless — o servidor não precisa armazenar sessões, apenas verificar a assinatura. Em APIs REST modernas, o fluxo típico é: o cliente faz login, recebe um JWT, e envia esse token no header em requisições subsequentes. O servidor valida a assinatura antes de processar a requisição. Isso elimina a necessidade de consultar um banco de dados a cada requisição, melhorando significativamente a performance. Rust com Axum é uma escolha excelente para isso: Axum é um web framework moderno, assíncrono e com sistema de tipos robusto que facilita implementar camadas de autenticação seguras. Configuração Inicial com Axum

<h2>Fundamentos de JWT e Segurança em APIs</h2>

<p>JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma forma compacta e auto-contida de transmitir informações entre partes de forma segura. Um JWT é composto por três partes separadas por pontos: header (tipo de token e algoritmo), payload (dados do usuário) e signature (assinatura criptográfica). A grande vantagem é que o token é stateless — o servidor não precisa armazenar sessões, apenas verificar a assinatura.</p>

<p>Em APIs REST modernas, o fluxo típico é: o cliente faz login, recebe um JWT, e envia esse token no header <code>Authorization: Bearer &lt;token&gt;</code> em requisições subsequentes. O servidor valida a assinatura antes de processar a requisição. Isso elimina a necessidade de consultar um banco de dados a cada requisição, melhorando significativamente a performance. Rust com Axum é uma escolha excelente para isso: Axum é um web framework moderno, assíncrono e com sistema de tipos robusto que facilita implementar camadas de autenticação seguras.</p>

<h2>Configuração Inicial com Axum e Dependências</h2>

<p>Vamos começar adicionando as dependências necessárias ao <code>Cargo.toml</code>. Você precisa de <code>axum</code> para o framework, <code>jsonwebtoken</code> para manipular JWTs, <code>tokio</code> para runtime assíncrono, e <code>serde</code> para serialização.</p>

<pre><code class="language-toml">[dependencies]

axum = &quot;0.7&quot;

tokio = { version = &quot;1&quot;, features = [&quot;full&quot;] }

jsonwebtoken = &quot;9&quot;

serde = { version = &quot;1&quot;, features = [&quot;derive&quot;] }

serde_json = &quot;1&quot;

chrono = &quot;0.4&quot;</code></pre>

<p>Agora, vamos criar a estrutura básica. Primeiro, definimos as estruturas para o payload do JWT e as credenciais de login:</p>

<pre><code class="language-rust">use serde::{Deserialize, Serialize};

use chrono::{Utc, Duration};

use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};

#[derive(Debug, Serialize, Deserialize)]

pub struct Claims {

pub sub: String, // subject (user id)

pub exp: i64, // expiration time

pub iat: i64, // issued at

pub email: String,

}

#[derive(Deserialize)]

pub struct LoginRequest {

pub email: String,

pub password: String,

}

#[derive(Serialize)]

pub struct LoginResponse {

pub token: String,

}</code></pre>

<p>O campo <code>exp</code> define quando o token expira (em timestamp Unix). O <code>iat</code> marca quando foi emitido. Sempre defina expiração para limitar o tempo de vida do token — se roubado, terá tempo limitado de uso.</p>

<h2>Implementando Geração e Validação de JWT</h2>

<h3>Gerando Tokens</h3>

<p>Crie uma função que gera o JWT após validar as credenciais. Para simplicidade, vamos usar uma senha hardcoded (em produção, compare com hash bcrypt):</p>

<pre><code class="language-rust">const SECRET_KEY: &amp;[u8] = b&quot;sua_chave_secreta_super_segura_min_32_bytes&quot;;

pub fn generate_token(email: String, user_id: String) -&gt; Result&lt;String, jsonwebtoken::errors::Error&gt; {

let now = Utc::now();

let claims = Claims {

sub: user_id,

email,

iat: now.timestamp(),

exp: (now + Duration::hours(24)).timestamp(),

};

let token = encode(

&amp;Header::default(),

&amp;claims,

&amp;EncodingKey::from_secret(SECRET_KEY),

)?;

Ok(token)

}</code></pre>

<h3>Validando Tokens</h3>

<p>Agora implementamos a função para validar e extrair dados do token:</p>

<pre><code class="language-rust">pub fn validate_token(token: &amp;str) -&gt; Result&lt;Claims, jsonwebtoken::errors::Error&gt; {

decode::&lt;Claims&gt;(

token,

&amp;DecodingKey::from_secret(SECRET_KEY),

&amp;Validation::default(),

)

.map(|data| data.claims)

}</code></pre>

<h2>Integrando com Axum e Criando Middleware</h2>

<h3>Extrator Customizado para JWT</h3>

<p>O Axum permite criar extractors que processam requisições automaticamente. Vamos criar um que valida o JWT:</p>

<pre><code class="language-rust">use axum::{

async_trait,

extract::FromRequestParts,

http::request::Parts,

response::{IntoResponse, Response},

Json,

};

pub struct AuthenticatedUser(pub Claims);

#[async_trait]

impl&lt;S&gt; FromRequestParts&lt;S&gt; for AuthenticatedUser

where

S: Send + Sync,

{

type Rejection = JsonResponse;

async fn from_request_parts(parts: &amp;mut Parts, _state: &amp;S) -&gt; Result&lt;Self, Self::Rejection&gt; {

let header = parts

.headers

.get(&quot;authorization&quot;)

.and_then( | h | h.to_str().ok()) .ok_or_else(|| JsonResponse::Unauthorized)?;

let token = header

.strip_prefix(&quot;Bearer &quot;)

.ok_or_else(|| JsonResponse::Unauthorized)?;

let claims = validate_token(token)

.map_err(|_| JsonResponse::Unauthorized)?;

Ok(AuthenticatedUser(claims))

}

}

pub enum JsonResponse {

Unauthorized,

}

impl IntoResponse for JsonResponse {

fn into_response(self) -&gt; Response {

match self {

JsonResponse::Unauthorized =&gt; (

axum::http::StatusCode::UNAUTHORIZED,

Json(serde_json::json!({&quot;error&quot;: &quot;Unauthorized&quot;})),

).into_response(),

}

}

}</code></pre>

<h3>Rotas de Autenticação e Recursos Protegidos</h3>

<pre><code class="language-rust">use axum::{routing::{post, get}, Router};

async fn login(

Json(payload): Json&lt;LoginRequest&gt;,

) -&gt; Result&lt;Json&lt;LoginResponse&gt;, String&gt; {

// Validação simples (em produção, use bcrypt)

if payload.password != &quot;senha_correta&quot; {

return Err(&quot;Invalid credentials&quot;.to_string());

}

let token = generate_token(payload.email.clone(), &quot;user_123&quot;.to_string())

.map_err(|_| &quot;Failed to generate token&quot;.to_string())?;

Ok(Json(LoginResponse { token }))

}

async fn protected_route(

AuthenticatedUser(claims): AuthenticatedUser,

) -&gt; Json&lt;serde_json::Value&gt; {

Json(serde_json::json!({

&quot;message&quot;: format!(&quot;Bem-vindo, {}!&quot;, claims.email),

&quot;user_id&quot;: claims.sub

}))

}

#[tokio::main]

async fn main() {

let app = Router::new()

.route(&quot;/login&quot;, post(login))

.route(&quot;/protected&quot;, get(protected_route));

let listener = tokio::net::TcpListener::bind(&quot;127.0.0.1:3000&quot;)

.await

.unwrap();

axum::serve(listener, app).await.unwrap();

}</code></pre>

<p>Quando um cliente faz POST em <code>/login</code> com credenciais válidas, recebe um JWT. Ao acessar <code>/protected</code> com o header <code>Authorization: Bearer &lt;token&gt;</code>, o extrator <code>AuthenticatedUser</code> valida automaticamente e passa as claims para o handler.</p>

<h2>Considerações de Segurança em Produção</h2>

<p>A chave secreta deve ser <strong>nunca</strong> hardcoded. Use variáveis de ambiente ou um vault de segurança. Implementar refresh tokens com tempo de vida curto também é crucial: o access token expira em minutos, e um refresh token (armazenado com segurança) permite obter novos access tokens sem fazer login novamente.</p>

<p>Sempre use HTTPS em produção, configure CORS apropriadamente se sua API é consumida por navegadores, e considere adicionar rate limiting na rota de login contra ataques de força bruta. Valide o formato do token antes de decodificar, e registre tentativas de acesso não autorizado para monitoramento de segurança.</p>

<h2>Conclusão</h2>

<p>Você aprendeu a implementar autenticação JWT em Rust com Axum de forma prática. Os pontos principais foram: (1) JWT é um padrão stateless e seguro ideal para APIs modernas, (2) Axum fornece extractors que facilitam validar tokens automaticamente em requisições, e (3) em produção, gerenciar secrets, implementar refresh tokens e usar HTTPS são obrigatórios. O código aqui é um ponto de partida sólido que você pode expandir com reset de senha, rate limiting e persistência real de usuários.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.rs/axum/" target="_blank" rel="noopener noreferrer">Documentação oficial do Axum</a></li>

<li><a href="https://tools.ietf.org/html/rfc7519" target="_blank" rel="noopener noreferrer">RFC 7519 - JSON Web Token (JWT)</a></li>

<li><a href="https://docs.rs/jsonwebtoken/" target="_blank" rel="noopener noreferrer">Crate jsonwebtoken no Docs.rs</a></li>

<li><a href="https://tokio.rs/" target="_blank" rel="noopener noreferrer">Tokio async runtime</a></li>

<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer">OWASP - Authentication Cheat Sheet</a></li>

</ul>

Comentários

Mais em Rust

Guia Completo de Criando Tipos de Erro Customizados em Rust
Guia Completo de Criando Tipos de Erro Customizados em Rust

Por que Customizar Tipos de Erro em Rust? Em Rust, tratamento de erros é uma...

Como Usar Estratégias de Recuperação de Falhas em Sistemas Rust em Produção
Como Usar Estratégias de Recuperação de Falhas em Sistemas Rust em Produção

Introdução às Estratégias de Recuperação em Rust Rust oferece um sistema de t...

Crates.io: Publicando e Consumindo Bibliotecas Rust: Do Básico ao Avançado
Crates.io: Publicando e Consumindo Bibliotecas Rust: Do Básico ao Avançado

Introdução ao Crates.io e Ecossistema Rust Crates.io é o repositório oficial...