1. "¿Por qué me desconoces después de iniciar sesión?" (El origen del problema)
OAuth2, JWT, autenticación basada en sesiones… hay muchísimas formas de autenticar, y en la mayoría de los casos son suficientes. Yo también las he usado.
- Cuando integré OAuth2 en un cliente de correo propio o en MyGPT de ChatGPT, sentí que ofrecía una experiencia de usuario superior.
- En aplicaciones web monolíticas con Django, la autenticación por sesión es la mejor opción.
- En arquitecturas con frontend y backend separados, el JWT resultó ser el más limpio.
Sin embargo, llegó un momento en que esta combinación se vino abajo de golpe.
El culpable fue el trabajo asíncrono (Celery). Cuando el usuario pulsa un botón, el backend no ejecuta la tarea directamente; la delega a un servidor de cálculo AI o a un worker remoto. En ese instante el worker pregunta:
"Recibí la solicitud, pero ¿para quién la estoy procesando? No hay request.user."

2. El problema: "backend ↔ backend" + "worker asíncrono (Celery)"
Lo que me llevó a adoptar una API Key fue la arquitectura donde el backend se comunica con otro backend y, en medio, interviene un worker de Celery.
- El usuario envía una petición web.
- El backend coloca la "tarea" en una cola.
- El worker de Celery consume la cola y envía una petición asíncrona a algún servidor de cálculo.
En este flujo, los puntos críticos son:
- El worker no tiene request.user.
- No dispone de sesión (no es un navegador).
- El JWT se vuelve confuso (¿quién genera, dónde almacena y cómo entrega el token?).
- OAuth2 requiere interacción del usuario, por lo que resulta inviable.
Cuando JWT y sesiones dejan de ser útiles, la única pregunta que queda es:
"¿Cómo puede el worker indicar al servidor de cálculo qué cliente/tenant está detrás de la solicitud?"
3. En el mundo del worker, la "identificación" precede a la "autenticación"
En una petición web, "autenticación = login" y "login = usuario" están estrechamente ligados.
Para un worker, sin embargo, no es una persona sino una aplicación la que consume CPU, por lo que antes de hablar de autenticación necesitamos simplemente identificarla. La autenticación entre servidores se puede resolver con HMAC, claves secretas, etc.
- La tarea debe ejecutarse con los datos del cliente A.
- El resultado debe guardarse en los recursos de A.
- Facturación, permisos y cuotas deben aplicarse a A.
Intentar forzar JWT o sesiones en este contexto implica diseñar emisión, almacenamiento, transmisión, expiración y renovación de tokens, lo que genera una complejidad innecesaria y una sensación de "¿realmente deberíamos generar un JWT para una petición que no proviene del navegador?". Esa idea fue descartada de inmediato.
4. Solución: la API Key resultó ser simple y poderosa
Así que introduje una API Key, que resolvió todo de una sola vez.
- El worker envía la petición interna con un único encabezado que cubre autenticación e identificación.
- El servidor que recibe la clave la asocia instantáneamente al usuario/cliente correspondiente.
- Rotar o revocar la clave es trivial.
Ejemplo de la llamada que el backend hace al servidor de cálculo:
POST /v1/ai/jobs
Authorization: Api-Key <KEY>
Content-Type: application/json
{ "job_id": "...", "payload": {...} }
El worker no necesita request.user; simplemente envía la solicitud y el backend la procesa usando la API Key para determinar el usuario.
5. Mejora clave: enlazar la API Key con el USUARIO simplifica la operación
Lo que más me convenció fue poder asociar la clave directamente con el modelo de usuario.
Bibliotecas como rest_framework_api_key ya proporcionan la generación de claves, pero en mi caso lo esencial era "clave ↔ usuario".
Extendí AbstractAPIKey creando CustomAPIKey y lo vinculé mediante una FK al modelo de usuario.
from django.conf import settings
from rest_framework_api_key.models import AbstractAPIKey
from django.db import models
class CustomAPIKey(AbstractAPIKey):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="api_keys"
)
is_test = models.BooleanField(default=False) # distinguir claves de stage/testing
Con este vínculo, la autenticación se transforma en una funcionalidad operativa.
6. Beneficios operacionales al generar automáticamente una clave por usuario
Al crear una clave al momento del registro, surgieron varias ventajas.
1) Gestión de validez simplificada
- Consultar, desactivar o eliminar la clave de un usuario es directo.
- Cuando un usuario se da de baja, las claves vinculadas se eliminan automáticamente (cascade).
2) Rotación de claves fácil
- Ante una posible filtración, basta generar una nueva clave y revocar la anterior.
- Permitir múltiples claves facilita reemplazos sin interrupción (se despliega la nueva y se retira la vieja).
3) Facturación y cuotas basadas en usuarios
- En lugar de rastrear por clave, se controla por usuario, simplificando la lógica de planes y límites.
- No es necesario inferir "¿a quién pertenece esta API Key?" en cada petición.
4) Soporte para varias claves por usuario
El campo is_test destaca aquí. Al ser una relación FK, un mismo usuario puede poseer varias claves con diferentes propósitos.
* Clave para entorno de pruebas y otra para producción.
* Separar flujos de desarrollo y operación es sencillo.
* Los logs pueden distinguir claramente tráfico de prueba vs. real.
7) Elección de método de autenticación: no hay ganadores, solo herramientas adecuadas
En resumen, la combinación óptima depende del contexto:
- OAuth2: integración con servicios externos y flujos donde el consentimiento del usuario es esencial.
- Sesión: aplicaciones Django monolíticas, donde la rapidez de desarrollo y la simplicidad son prioritarias.
- JWT: arquitecturas frontend/backend separadas, móviles o SPA que requieren portabilidad del token.
- API Key: comunicación backend‑backend, automatizaciones, workers o tareas batch donde no interviene un usuario directo.
Cuando un worker de Celery entra en juego, intentar unificar todo bajo "autenticación basada en login" solo aumenta la complejidad. La API Key resultó ser la salida más limpia.
8. Conclusión
Los navegadores y apps manejan sesiones, JWT u OAuth2 sin problemas. Los workers, al ser procesos, necesitan identificarse. Adoptar una API Key no fue una cuestión de un mero discurso sobre seguridad, sino la solución más simple para ese punto del flujo. Al vincularla al usuario, la gestión de claves pasó de ser una preocupación técnica a convertirse en una palanca operativa.
¿Usas API Keys con frecuencia? El enfoque que describo muestra solo una faceta de su conveniencia, pero espero que sirva de inspiración para quienes enfrentan retos similares.
Artículos relacionados