La autenticación con OAuth2 y JWT en FastAPI combina un flujo estándar de inicio de sesión con tokens firmados. El cliente envía usuario y contraseña una vez, recibe un token Bearer de corta duración y lo presenta en las rutas protegidas. JWT no es cifrado: quien tenga el token puede leer su contenido, aunque no puede modificarlo sin invalidar la firma.
Este tutorial amplía la guía para crear una API REST con FastAPI. En producción, exige HTTPS, guarda los usuarios en una base de datos, limita los intentos de acceso y mantén las claves fuera del código.
Dependencias y flujo de autenticación
uv add "fastapi[standard]" pyjwt "pwdlib[argon2]"
El endpoint /token recibe un formulario OAuth2, comprueba la contraseña y devuelve access_token y token_type. OAuth2PasswordBearer extrae el token del encabezado Authorization. Una dependencia valida firma, caducidad y usuario antes de ejecutar la ruta.
from datetime import datetime, timedelta, timezone
import jwt
from pwdlib import PasswordHash
ALGORITHM = "HS256"
password_hash = PasswordHash.recommended()
def create_access_token(subject: str, secret: str) -> str:
expires = datetime.now(timezone.utc) + timedelta(minutes=20)
return jwt.encode({"sub": subject, "exp": expires}, secret, algorithm=ALGORITHM)
def verify_password(plain: str, encoded: str) -> bool:
return password_hash.verify(plain, encoded)
Obtén secret de una fuente segura de la infraestructura. Un valor escrito en el archivo deja de ser secreto cuando entra en Git.
Valida el token en una dependencia
from typing import Annotated
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def current_username(
token: Annotated[str, Depends(oauth2_scheme)],
) -> str:
error = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Credenciales no válidas",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if not isinstance(username, str):
raise error
except jwt.InvalidTokenError:
raise error
return username
Indica una lista fija de algoritmos y no confíes en el encabezado del token. Tras leer sub, consulta al usuario y comprueba que su cuenta siga activa. El identificador debe ser estable y no contener información confidencial.
Crea el login y protege rutas
from typing import Annotated
from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordRequestForm
app = FastAPI()
@app.post("/token")
async def login(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
user = await find_user(form.username)
if user is None or not verify_password(form.password, user.password_hash):
raise HTTPException(status_code=401, detail="Credenciales no válidas")
token = create_access_token(user.username, SECRET_KEY)
return {"access_token": token, "token_type": "bearer"}
@app.get("/me")
async def me(username: Annotated[str, Depends(current_username)]):
return {"username": username}
Devuelve el mismo error para un usuario inexistente y una contraseña incorrecta. Para permisos, añade scopes o una dependencia de autorización independiente. Autenticar determina quién es el usuario; autorizar determina qué puede hacer.
Lista de control para producción
Define una caducidad corta, rotación de claves, relojes sincronizados y revocación. Los tokens de acceso no deben convertirse en sesiones eternas. Si utilizas refresh tokens, protégelos, rótalos, permite revocarlos y detecta su reutilización. No registres contraseñas ni tokens completos.
Prueba tokens caducados, firmas incorrectas, usuarios desactivados y encabezados ausentes. La documentación oficial de FastAPI sobre OAuth2 y JWT, consultada el 28 de julio de 2026, mantiene el flujo de referencia con hash y tokens Bearer.