Introducción: por qué OAuth 2.0 y JWT son el estándar de la autorización moderna
\nEn el desarrollo web moderno, la autenticación y autorización seguras son componentes críticos de cualquier aplicación. OAuth 2.0 y JWT (JSON Web Tokens) se han convertido en el estándar de facto para construir sistemas distribuidos. OAuth 2.0 proporciona un protocolo de acceso delegado, mientras que JWT es un formato de token compacto y autónomo.
\nEn este artículo, con ejemplos prácticos en TypeScript, analizaremos cómo implementar el ciclo completo de autorización: desde la emisión de tokens hasta su validación y renovación. Aprenderá cómo proteger endpoints de API, implementar refresh tokens y manejar errores típicos.
\n\n1. Configuración básica del proyecto e instalación de dependencias
\nPara empezar, crearemos un proyecto en Node.js con TypeScript. Asegúrese de tener instalados Node.js (versión 18+) y npm.
mkdir oauth-jwt-typescript\ncd oauth-jwt-typescript\nnpm init -y\nnpm install express cors dotenv jsonwebtoken bcrypt\nnpm install -D typescript @types/express @types/cors @types/jsonwebtoken @types/bcrypt ts-node nodemon\nnpx tsc --init\nConfiguraremos tsconfig.json para trabajar con módulos ES y tipos estrictos. En el archivo .env agregaremos las claves secretas:
ACCESS_TOKEN_SECRET=your-super-secret-access-key\nREFRESH_TOKEN_SECRET=your-super-secret-refresh-key\nACCESS_TOKEN_EXPIRES_IN=15m\nREFRESH_TOKEN_EXPIRES_IN=7d\n\n2. Implementación de generación y verificación de tokens JWT
\nCrearemos una utilidad src/utils/jwt.ts para trabajar con tokens. Usaremos dos tipos de tokens: Access Token (de corta duración) y Refresh Token (de larga duración).
import jwt from 'jsonwebtoken';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\ninterface TokenPayload {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n}\n\nexport const generateAccessToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET!, {\n expiresIn: process.env.ACCESS_TOKEN_EXPIRES_IN || '15m',\n });\n};\n\nexport const generateRefreshToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n expiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',\n });\n};\n\nexport const verifyAccessToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.ACCESS_TOKEN_SECRET!) as TokenPayload;\n};\n\nexport const verifyRefreshToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.REFRESH_TOKEN_SECRET!) as TokenPayload;\n};\nTenga en cuenta: usamos tipado estricto para el payload. Esto garantiza que el token siempre contendrá los campos necesarios.
\n\n3. Creación de middleware para proteger rutas
\nAhora implementaremos un middleware que verificará la presencia y validez del Access Token en el encabezado Authorization. Crearemos src/middleware/auth.ts:
import { Request, Response, NextFunction } from 'express';\nimport { verifyAccessToken } from '../utils/jwt';\n\nexport interface AuthRequest extends Request {\n user?: {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n };\n}\n\nexport const authenticate = (req: AuthRequest, res: Response, next: NextFunction): void => {\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n\n if (!token) {\n res.status(401).json({ error: 'Access token is required' });\n return;\n }\n\n try {\n const decoded = verifyAccessToken(token);\n req.user = decoded;\n next();\n } catch (error) {\n res.status(403).json({ error: 'Invalid or expired access token' });\n }\n};\n\nexport const authorizeRole = (...roles: string[]) => {\n return (req: AuthRequest, res: Response, next: NextFunction): void => {\n if (!req.user || !roles