Autenticación JWT
JWT (JSON Web Token) es un estándar para transmitir información entre partes como un objeto JSON firmado y verificable. En APIs REST sirve para autenticación stateless: el servidor no guarda sesión, el token lleva la identidad.
Trade-off:
No se puede revocar un access token antes de su expiración. Por eso existen refresh tokens + blacklist (ver guía avanzada).
Anatomía de un JWT
El token no está encriptado (cualquiera puede decodificar el payload con base64). La firma garantiza que no fue modificado. No pongas secrets (passwords, claves API) en el payload.
Flujo básico Access + Refresh
Instalación y configuración mínima
pip install djangorestframework-simplejwt
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'AUTH_HEADER_TYPES': ('Bearer',),
}
JWTAuthenticationlee el headerAuthorization: Bearer <token>, valida firma y expiración, y setearequest.user.
URLs de autenticación
# urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('auth/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
POST /auth/token/con{username, password}→{access, refresh}POST /auth/refresh/con{refresh}→{access}
Protegiendo endpoints
# views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class PerfilView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({
"user_id": request.user.id,
"username": request.user.username,
"email": request.user.email,
})
Con IsAuthenticated, cualquier request sin token válido → 401 Unauthorized.
Probando el flujo completo
# 1. Login
curl -X POST http://localhost:8000/auth/token/ \
-H "Content-Type: application/json" \
-d '{"username": "tu_user", "password": "tu_pass"}'
# {"access": "eyJhbGciOi...", "refresh": "eyJhbGciOi..."}
# 2. Request autenticado
curl -H "Authorization: Bearer eyJhbGciOi..." \
http://localhost:8000/api/perfil/
# 3. Refresh (cuando access expira)
curl -X POST http://localhost:8000/auth/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh": "eyJhbGciOi..."}'
# {"access": "eyJhbGciOi...nuevo..."}
Errores comunes
| Error | Causa | Solución |
|---|---|---|
401 "Token is invalid or expired" |
Access expirado o firma inválida | Usar /auth/refresh/ con refresh válido |
401 "Authentication credentials not provided" |
Falta header Authorization |
Enviar Authorization: Bearer <access> |
400 "Token is blacklisted" |
Refresh en blacklist (logout) | Re-loguear usuario |
KeyError 'user_id' en custom serializer |
SimpleJWT usa user_id por defecto |
Verificar que tu User model tenga id o pk |
Buenas prácticas básicas
| Práctica | Por qué |
|---|---|
| Access token corto (15 min) | Limita exposición si token es robado |
| Refresh token largo (7-30 días) | UX: usuario no re-loguea constante |
| HTTPS obligatorio en prod | Tokens viajan en headers, sin TLS se interceptan |
SECRET_KEY fuerte y rotada |
Firma HS256 usa esta clave; si se filtra, firman tokens falsos |