Introdução
\nO desenvolvimento web moderno é impensável sem autenticação e autorização seguras de usuários. Os dois padrões mais populares — OAuth 2.0 e JWT (JSON Web Tokens) — tornaram-se soluções industriais de facto. Se você programa em Rust, está com sorte: esta linguagem oferece não apenas o mais alto desempenho, mas também bibliotecas poderosas para implementar esses protocolos.
\nNeste artigo, vamos explicar o que são OAuth 2.0 e JWT, como eles funcionam juntos e escrever um exemplo simples, mas funcional, de autorização em Rust usando o framework actix-web e a crate jsonwebtoken. O artigo é voltado para iniciantes, então vamos passo a passo — da teoria ao código.
O que são OAuth 2.0 e JWT?
\nAntes de escrever código, é importante entender os conceitos. OAuth 2.0 é um protocolo de autorização que permite que aplicativos obtenham acesso limitado aos recursos do usuário sem transmitir sua senha. Ele é usado quando você faz login em um site via Google, GitHub ou VK.
\nJWT é um formato de token (JSON Object Signing and Encryption) que contém declarações (claims) sobre o usuário. O token é assinado pelo servidor, portanto o cliente não pode falsificá-lo. No contexto do OAuth 2.0, o JWT é frequentemente usado como access token (token de acesso).
\nCenário típico: o usuário insere login e senha, o servidor os verifica, gera um token JWT e o retorna ao cliente. O cliente envia este token em cada requisição no cabeçalho Authorization: Bearer <token>. O servidor verifica a assinatura e extrai os dados do usuário.
Configuração do projeto em Rust
\nVamos criar um novo projeto:
\ncargo new rust_oauth_jwt\ncd rust_oauth_jwt\nAdicione as dependências necessárias no Cargo.toml:
[dependencies]\nactix-web = "4"\nactix-rt = "2"\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"\njsonwebtoken = "9"\nchrono = { version = "0.4", features = ["serde"] }\ndotenv = "0.15"\nenv_logger = "0.11"\nlog = "0.4"\nCrie um arquivo .env na raiz do projeto para armazenar a chave secreta:
JWT_SECRET=my_super_secret_key_change_in_production\nJWT_EXPIRATION_HOURS=24\nCarregue as variáveis de ambiente no main.rs:
use dotenv::dotenv;\nuse std::env;\n\nfn main() {\n dotenv().ok();\n env_logger::init();\n // ... resto do código\n}\n\nCriação da estrutura do usuário e do token JWT
\nPrimeiro, vamos definir o modelo de usuário e a estrutura de claims para o JWT. Crie o arquivo models.rs:
use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Serialize, Deserialize, Clone)]\npub struct User {\n pub id: i32,\n pub username: String,\n pub password_hash: String, // Em um projeto real, armazene o hash, não a senha!\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n pub sub: String, // subject (id do usuário)\n pub username: String,\n pub exp: usize, // timestamp de expiração\n}\nAgora, crie um utilitário para trabalhar com JWT em jwt_utils.rs:
use chrono::{Duration, Utc};\nuse jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};\nuse crate::models::Claims;\n\npub fn create_jwt(user_id: &str, username: &str) -> Result<String, jsonwebtoken::errors::Error> {\n let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");\n let expiration_hours: i64 = std::env::var("JWT_EXPIRATION_HOURS")\n .unwrap_or_else(|_| "24".to_string())\n .parse()\n .unwrap();\n\n let expiration = Utc::now()\n .checked_add_signed(Duration::hours(expiration_hours))\n .expect("valid timestamp")\n .timestamp() as usize;\n\n let claims = Claims {\n sub: user_id.to_owned(),\n username: username.to_owned(),\n