Volver al blog
18 de julio de 2026 · 7 min de lectura · FastAPI, Seguridad

JWT + RBAC en FastAPI: cómo estructurar autenticación y roles en una API real

Autenticación responde "¿quién eres?". Autorización responde "¿qué puedes hacer?". Son preguntas distintas, y mezclarlas en un solo chequeo disperso por los endpoints (if user.role == "admin" or user.role == "coordinador": ... repetido veinte veces) es de las formas más comunes en que una API termina con un endpoint que se le olvidó proteger. Esto es lo que uso en APIs con FastAPI que manejan roles reales — admin, coordinador, supervisor, auxiliar en un sistema de almacén; niveles de aprobación en un sistema de viáticos — para mantener esa separación clara.

Access token corto, refresh token largo

Un JWT de acceso con vida de 15-30 minutos limita la ventana de exposición si alguien lo roba: aunque se filtre, deja de servir pronto. El problema es que eso obligaría a reloguear al usuario cada 15 minutos si no hubiera nada más — ahí entra el refresh token, con vida de días o semanas, cuyo único trabajo es pedir un access token nuevo cuando el actual expira.

Dónde se guarda cada uno importa tanto como su duración:

El costo de este esquema es que el frontend necesita lógica para refrescar el access token en cuanto expira (normalmente interceptando la respuesta 401 y reintentando tras pedir uno nuevo), pero es un costo pequeño comparado con el problema que resuelve.

Roles como tabla, no como string mágico repetido

En vez de comparar user.role == "admin" dentro de cada función de endpoint, conviene centralizar el chequeo en una dependencia de FastAPI reutilizable:

def require_role(*roles: str):
    def checker(user: User = Depends(get_current_user)):
        if user.role not in roles:
            raise HTTPException(status_code=403, detail="No autorizado")
        return user
    return checker

@router.delete("/almacenes/{id}")
def eliminar_almacen(id: int, user: User = Depends(require_role("admin"))):
    ...

La ventaja no es solo menos código repetido: es que el rol requerido queda visible en la firma del endpoint, así que revisar qué puede hacer cada rol es leer las firmas, no rastrear ifs dispersos por todo el archivo de rutas. get_current_user, a su vez, es la dependencia que decodifica el JWT del header Authorization, valida la firma y expiración, y devuelve (o falla) el usuario — se reutiliza en cada endpoint protegido sin reescribir esa lógica.

2FA como capa adicional, no como reemplazo

En sistemas donde se autorizan descuentos o montos (por ejemplo, un cotizador B2B), agregar TOTP (Time-based One-Time Password, el mismo estándar de Google Authenticator) como segundo factor para ciertas acciones sensibles reduce el riesgo si la contraseña se filtra. Un detalle que se pasa por alto seguido: el secreto del 2FA nunca debe viajar dentro del JWT. El JWT prueba que ya pasaste el login; el secreto TOTP es un dato de configuración de la cuenta que vive solo en el backend, y el flujo de verificación del código de 6 dígitos es una llamada aparte, no algo que se decodifique del token.

Errores comunes que veo repetirse

Conclusión

JWT y RBAC no son complicados por sí mismos; se vuelven riesgosos cuando la autenticación y la autorización se mezclan en el mismo chequeo ad-hoc, endpoint por endpoint. Centralizar ambas en dependencias reutilizables de FastAPI — una para "quién eres", otra para "qué rol necesitas" — hace que el sistema de permisos sea algo que se puede auditar leyendo las firmas de las funciones, no reconstruyendo lógica dispersa por todo el proyecto.

¿Necesitas una API segura con roles bien definidos?

Desarrollo backends con FastAPI, autenticación JWT, RBAC y 2FA para sistemas donde los permisos por rol importan de verdad.

Solicitar una propuesta