Tu API en FastAPI funciona, devuelve datos bien, pasa tus pruebas en Postman — y cualquiera que encuentre la URL puede llamarla sin pedir permiso. Proteger una API con JWT (JSON Web Tokens) no es tan complicado como suena: en menos de 50 líneas tienes un endpoint de login que entrega un token y una dependencia que lo valida en cada ruta protegida.
Por qué JWT y no una simple API key
Una API key es un secreto fijo que vive en texto plano en algún header. Funciona para integraciones internas, pero no identifica a un usuario ni expira sola. Un JWT, en cambio, es un token firmado que contiene datos del usuario (su ID, su rol) y una fecha de expiración — el servidor lo verifica sin tener que consultar la base de datos en cada petición, porque la firma ya garantiza que nadie lo alteró.
El flujo completo en 4 pasos
- ▸Login: el usuario manda su correo y contraseña a un endpoint /token.
- ▸Verificación: FastAPI compara la contraseña contra el hash guardado en la base de datos (nunca la contraseña en texto plano).
- ▸Emisión del token: si coincide, se firma un JWT con el ID del usuario y una expiración de, por ejemplo, 30 minutos.
- ▸Validación: cada ruta protegida usa una dependencia que lee el token del header Authorization, lo decodifica y rechaza la petición si expiró o la firma no coincide.
Este flujo — login, hash de contraseñas, emisión y validación de tokens — es justo la parte de “autenticación y seguridad” que cubre Fundamentos de consumo y construcción de APIs, el curso que lleva de FastAPI básico a una API con base de datos en SQLAlchemy y documentación automática en Swagger.
El código mínimo que necesitas
Con la librería python-jose para firmar y passlib para el hash de contraseñas, la dependencia que protege una ruta se ve así:
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
SECRET_KEY = "cambia-esto-por-una-variable-de-entorno"
def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=401, detail="Token invalido")
return user_id
except JWTError:
raise HTTPException(status_code=401, detail="Token invalido o expirado")
@app.get("/perfil")
def leer_perfil(user_id: str = Depends(get_current_user)):
return {"user_id": user_id}Esa dependencia es reutilizable: la agregas con Depends(get_current_user) a cualquier endpoint que quieras proteger, y FastAPI se encarga de rechazar la petición antes de que tu lógica de negocio corra.
Nota el detalle del tiempo de expiración: 30 minutos es un balance razonable entre seguridad y comodidad. Un token que dura días es casi tan riesgoso como no tener autenticación si alguien lo intercepta; uno que dura 2 minutos obliga al usuario a loguearse todo el tiempo y termina siendo tan molesto que la gente busca cómo evitarlo.
El error que más se repite: la clave secreta en el código
Nunca dejes SECRET_KEY como un string fijo en el repositorio. Si tu API es pública en GitHub, esa clave ya no es secreta, y con ella cualquiera puede firmar tokens válidos para tu sistema.
La forma correcta es leerla de una variable de entorno (os.getenv("SECRET_KEY")) y nunca comitearla. Si ya la subiste por error, rotáala de inmediato — cambiar el valor invalida todos los tokens firmados con la clave vieja.
Documentarlo para que otros lo usen bien
FastAPI genera automáticamente la documentación en Swagger (/docs) y ahí mismo puedes probar el login y pegar el token para probar las rutas protegidas sin salir del navegador. Es la diferencia entre entregar una API y entregar una API que el siguiente desarrollador puede usar sin preguntarte nada.
Antes de llegar aquí
Si todavía no te sientes cómodo con funciones, diccionarios y manejo de errores en Python, vale la pena pasar primero por Python para todos — JWT y FastAPI son mucho más fáciles de entender cuando la sintaxis base ya no te distrae.
Qué falta para producción
Este código mínimo ya protege rutas, pero le faltan dos cosas antes de usarlo en un proyecto real: un token de refresco (para no obligar al usuario a loguearse cada 30 minutos) y un límite de intentos de login fallidos, para que nadie pueda probar contraseñas a la fuerza contra tu endpoint. Ninguna de las dos es complicada de agregar, pero se olvidan con facilidad cuando el enfoque está solo en “que funcione”.
También conviene separar los roles dentro del mismo token — un campo role junto al ID del usuario te permite decidir, en la misma dependencia, si alguien puede entrar a una ruta de administrador o solo a la suya. Es mucho más simple que mantener una tabla de permisos aparte para una API pequeña.
Lo que te llevas
Fundamentos de consumo y construcción de APIs tiene certificado, proyectos prácticos y forma parte de la ruta Data Engineer de DataPath. Al terminarlo no solo sabes levantar una API: sabes protegerla, documentarla y desplegarla en la nube, que es lo que de verdad te distingue en una entrevista técnica.

