Introducción: ¿por qué OAuth 2.0 y JWT en Rust?
\n\nLas aplicaciones web modernas rara vez prescinden de un sistema de autenticación y autorización. OAuth 2.0 es el estándar de la industria para la delegación de acceso, y JWT (JSON Web Tokens) es una forma compacta y segura de transferir afirmaciones entre partes. Rust, gracias a su rendimiento y seguridad de memoria, se está volviendo cada vez más popular para crear servidores de alta carga. En este artículo, analizaremos en la práctica cómo implementar la autorización OAuth 2.0 con JWT en Rust, utilizando crates populares y enfoques modernos.
\n\n1. Preparación del proyecto e instalación de dependencias
\n\nPara empezar, crearemos un nuevo proyecto en Rust y agregaremos las dependencias necesarias. Usaremos actix-web para el servidor web, jsonwebtoken para trabajar con JWT, serde para la serialización y reqwest para solicitudes HTTP a proveedores OAuth externos.
# Cargo.toml\n[package]\nname = "oauth-jwt-rust"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\nactix-web = "4"\nactix-rt = "2"\njsonwebtoken = "9"\nserde = { version = "1.0", features = ["derive"] }\nserde_json = "1.0"\nreqwest = { version = "0.12", features = ["json"] }\ntokio = { version = "1", features = ["full"] }\ndotenv = "0.15"\nchrono = { version = "0.4", features = ["serde"] }\nuuid = { version = "1", features = ["v4"] }\n\nCrearemos un archivo .env para almacenar las claves secretas:
JWT_SECRET=your_super_secret_key_here\nGITHUB_CLIENT_ID=your_github_client_id\nGITHUB_CLIENT_SECRET=your_github_client_secret\n\n2. Estructura básica de los tokens JWT
\n\nJWT consta de tres partes: encabezado, carga útil y firma. Definiremos las estructuras para trabajar con tokens.
\n\n// src/jwt.rs\nuse chrono::{Utc, Duration};\nuse jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n pub sub: String, // identificador de usuario\n pub email: String, // email del usuario\n pub exp: usize, // tiempo de expiración\n pub iat: usize, // tiempo de emisión\n}\n\npub fn create_jwt(user_id: &str, email: &str) -> Result {\n let now = Utc::now();\n let expiration = now + Duration::hours(24); // el token vive 24 horas\n\n let claims = Claims {\n sub: user_id.to_string(),\n email: email.to_string(),\n exp: expiration.timestamp() as usize,\n iat: now.timestamp() as usize,\n };\n\n let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");\n encode(\n &Header::default(),\n &claims,\n &EncodingKey::from_secret(secret.as_bytes()),\n )\n}\n\npub fn validate_jwt(token: &str) -> Result {\n let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");\n let token_data = decode::(\n token,\n &DecodingKey::from_secret(secret.as_bytes()),\n &Validation::default(),\n )?;\n Ok(token_data.claims)\n} \n\n3. Implementación de OAuth 2.0 con GitHub
\n\nConsideremos el escenario clásico de OAuth 2.0: la autorización a través de GitHub. El usuario es redirigido a GitHub, después de la confirmación recibe un código que intercambiamos por un access token.
\n\n3.1. Endpoint para iniciar el flujo OAuth
\n\n// src/oauth.rs\nuse actix_web::{web, HttpResponse, HttpRequest};\nuse reqwest::Client;\nuse serde_json::Value;\n\npub async fn github_login() -> HttpResponse {\n let client_id = std::env::var("GITHUB_CLIENT_ID").unwrap();\n let redirect_uri = "http://localhost:8080/auth/github/callback";\n \n let auth_url = format!(\n "https://github.com/login/oauth/authorize?client_id={}&redirect_uri={}&scope=user:email",\n client_id, redirect_uri\n );\n \n HttpRes