Este documento consolida todo lo relacionado con seguridad en la aplicación: cómo se generan y validan tokens, control de roles/alcance por sucursal, CORS, hashing de contraseñas, manejo de errores 401/403, y cómo el frontend consume y aplica tokens.
Contenido rápido:
- Tipo de autenticación: OAuth2 (password) / JWT (Bearer)
- Hashing:
bcryptvíapasslib - Token signing: HS256 con
SECRET_KEYconfigurable - Verificación y dependencias FastAPI:
app.api.deps(ej.:CurrentUser,get_current_admin_user,ensure_bodega_in_scope) - CORS: configurable desde variables de entorno (
BACKEND_CORS_ORIGINS,FRONTEND_HOST)
-
Creación del token:
- Módulo:
backend/app/core/security.py - Función:
create_access_token(subject, expires_delta)que genera un JWT con payload{ "exp": <expiry>, "sub": <subject> }firmado consettings.SECRET_KEYy algoritmoHS256. - Expiración:
ACCESS_TOKEN_EXPIRE_MINUTES(configurable enbackend/app/core/config.py, por defecto 60248 = 8 días).
- Módulo:
-
Verificación del token (dependencia):
- Módulo:
backend/app/api/deps.py - Se usa
OAuth2PasswordBearerindicandotokenUrlensettings.API_V1_STR + '/login/access-token'. get_current_user(session, token)decodifica conjwt.decode(token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]), valida payload contraTokenPayload(modelo) y recuperaUserdesde la DB.- Errores: si el token es inválido o el usuario no existe/está inactivo se levantan HTTP errors 403, 404 o 400 según el caso.
- Módulo:
- Implementación:
passlibcon esquemabcrypt(archivobackend/app/core/security.py). - Funciones:
get_password_hash(password)yverify_password(plain, hashed).
-
Roles y superusuario:
User.is_superusermarca superusuarios.id_rolidentifica roles en la DB; por convención el rolAdministradorse detecta comoid_rol == 1.
-
Dependencias útiles (en
app.api.deps):get_current_active_superuser(current_user): validacurrent_user.is_superuser, levanta 403 si no.get_current_admin_user(current_user): permite superusuarios o usuarios conid_rol == 1.
-
Helpers:
is_admin_user(current_user): True si superuser oid_rol == 1.get_user_scope(current_user): devuelveNonepara admin (acceso total) o{"id_sucursal": N}para usuarios limitados a una sucursal. Muchos endpoints aplican este scope server-side.ensure_bodega_in_scope(session, bodega_id, current_user): valida que la bodega pertenezca a la sucursal del usuario (si no es admin) — lanza 404 si la bodega no existe o 403 si está fuera de alcance.
- Backend: usa HTTP status codes apropiados: 401/403 para autenticación/autorización, 404 para recursos no encontrados, 409 para conflictos (ej. capacidad insuficiente en recepción).
- Frontend: en
frontend/src/main.tsxhay un manejador global que atrapaApiErrorcon status 401/403, borraaccess_tokendelocalStoragey redirige a/login.
-
Configuración central:
backend/app/core/config.py.BACKEND_CORS_ORIGINS(read from env) yFRONTEND_HOST.- Propiedad derivada
all_cors_originsque normaliza orígenes y añadeFRONTEND_HOST.
-
En
backend/app/main.pysisettings.all_cors_originstiene valores se instalaCORSMiddlewarecon:allow_origins = settings.all_cors_originsallow_credentials = Trueallow_methods = ['*']allow_headers = ['*']
Recomendación: en producción restringir allow_origins a orígenes concretos y revisar allow_credentials según si se usan cookies seguras.
SECRET_KEYestá enbackend/app/core/config.pyy por defecto se genera, pero la validación_enforce_non_default_secrets()obliga a cambiar valores peligrosos (changethis) y a no dejar secrets por defecto en entornos no locales.- Recomendación operativa: almacenar
SECRET_KEY, credenciales DB, y credenciales Redis/SMTP en variables de entorno seguras / vault.
-
Almacenamiento:
frontend/src/hooks/useAuth.ts- En login exitoso se guarda
localStorage.setItem('access_token', response.access_token). - Logout borra
access_tokeny redirige.
- En login exitoso se guarda
-
Envío automático:
frontend/src/client/core/OpenAPI.tsdefine la configuración global yOpenAPI.TOKENse establece enfrontend/src/main.tsxaasync () => localStorage.getItem('access_token') || ''.frontend/src/client/core/request.tsengetHeaders()recuperaTOKENy, si existe, añade la cabeceraAuthorization: Bearer <token>.- Resultado: todas las llamadas generadas por el cliente OpenAPI incluyen el header Authorization si hay token en
localStorage.
- Crear bodega:
POST /bodegas/tienedependencies=[Depends(get_current_admin_user)]— solo admins pueden crear bodegas. - Endpoints que usan
CurrentUseryget_user_scopefiltran la data restornada por sucursal cuando el usuario no es admin. ensure_bodega_in_scopese usa en endpoints que manipulan o consultan bodegas/lotes para validar alcance.
- Implementar Refresh Tokens y/o revocación de tokens para permitir logout centralizado y rotación segura.
- Considerar tokens de corta duración + refresh token con almacenamiento seguro (HttpOnly cookie) para mitigar XSS.
- Añadir rate-limiting en el proxy (traefik/nginx) o middleware para evitar abusos y brute-force.
- Registrar métricas y alertas de seguridad (intentos 401/403, tasas de fallo en login, uso de tokens expirados).
- Revisar CORS en producción: usar lista blanca explícita y no
'*'enallow_headerssi no es necesario. - Evaluar uso de autenticación basada en claves para integraciones machine-to-machine y scopes finos para APIs externas.
Archivos de referencia (implementación):
backend/app/core/security.py— creación/verificación de JWT y hashing de contraseñas.backend/app/api/deps.py— dependencias para obtenerCurrentUser,get_current_admin_user,get_current_active_superuser,get_user_scope,ensure_bodega_in_scope.backend/app/core/config.py—SECRET_KEY,ACCESS_TOKEN_EXPIRE_MINUTES,BACKEND_CORS_ORIGINS,FRONTEND_HOST.backend/app/main.py— instalación deCORSMiddlewareconsettings.all_cors_origins.frontend/src/hooks/useAuth.ts— login/logout y almacenamiento enlocalStorage.frontend/src/main.tsx— configuraciónOpenAPI.TOKENy manejo global de errores 401/403.frontend/src/client/core/request.ts— añadeAuthorizationheader cuandoOpenAPI.TOKENretorna valor.
Si quieres, genero además:
- Un checklist operativo para desplegar con buenas prácticas (env vars, TLS, CORS, rate limit).
- Un ejemplo de flujo con
curly cómo probar expiración y revocación simulada.
Estado: creado security.md.
-
Tokens vs sesiones:
- La aplicación usa JWT de acceso (Bearer token) almacenado en
localStorage. Esto simplifica el cliente SPA pero expone tokens a XSS si no hay mitigaciones estrictas. - Alternativa más segura para SPAs: usar
access_tokencorto +refresh_tokenenHttpOnlycookie (solo el servidor puede leerla). Considera este enfoque para reducir riesgo de robo de tokens desde XSS.
- La aplicación usa JWT de acceso (Bearer token) almacenado en
-
Sesiones persistentes y multi-dispositivo:
- Si se requieren sesiones por dispositivo, mantener una tabla
user_sessionsen BD conuser_id,device_info,issued_at,expires_at,revoked. - Al hacer logout o revocar sesión, marcar
revoked=truey filtrar tokens en endpoints (por ejemplo, guardarjtien DB al emitir token y verificarlo en cada request o en un cache redis).
- Si se requieren sesiones por dispositivo, mantener una tabla
-
Revocación:
- Implementar lista de revocación (blacklist) en Redis para tokens revocados o usar short-lived tokens con refresh tokens que pueden revocarse.
-
Requisito: todas las conexiones públicas deben usar HTTPS.
- En producción el TLS debe terminar en el proxy inverso (Traefik / Nginx) o en el LB (ALB, Cloud Load Balancer).
- En entornos locales puede usarse TLS de desarrollo, pero nunca habilitar comunicaciones no cifradas en producción.
-
Certificados:
- Usar certificados gestionados (Let's Encrypt, ACME) o certificados de CA internos rotados automáticamente.
- Habilitar renovación automática y monitorizar expiración.
-
Protocolos y cifrados recomendados:
- TLS 1.2 mínimo, preferible TLS 1.3.
- Preferir suites modernas (ECDHE + AES-GCM o ChaCha20-Poly1305).
- Evitar RC4, DES, 3DES, and deprecated ciphers and key sizes < 2048 (for RSA).
-
HSTS y cabeceras de seguridad:
- Establecer
Strict-Transport-Security(HSTS) conincludeSubDomains; preloadcuando sea apropiado. - Añadir cabeceras:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy,Permissions-Policy, yContent-Security-Policypara mitigar XSS.
- Establecer
-
Base de datos:
- Habilitar cifrado a nivel de disco (EBS encryption, Azure Disk Encryption) y/o cifrado a nivel de BD si el proveedor lo soporta (TDE en algunos RDBMS).
- Para campos sensibles (PII, tokens de terceros) considerar cifrado campo-a-campo con claves gestionadas en KMS.
-
Backups:
- Asegurar que los backups están cifrados y accesibles solo por servicios/usuarios autorizados.
- Mantener políticas de retención y pruebas de restauración periódicas.
-
Simétrico:
- AES-256-GCM para cifrado de datos en reposo y para cifrado de secretos en aplicaciones.
-
Asimétrico:
- RSA/ECDSA para intercambio de claves y certificados TLS.
-
Firma/HMAC:
- JWTs actualmente usan
HS256(HMAC + SHA256) — esto usa una clave simétrica (SECRET_KEY). Para mayor seguridad en entornos con múltiples servicios, considerarRS256(clave pública/privada) para permitir verificación sin compartir la clave privada.
- JWTs actualmente usan
-
Gestión de claves:
- Usar un KMS (AWS KMS, GCP KMS, Azure KeyVault, HashiCorp Vault) para generar, almacenar y rotar claves.
- No almacenar claves en código ni en repositorios.
-
Qué loggear (mínimo):
- Eventos de autenticación: login success/fail, token refresh, token revocado.
- Operaciones críticas: creación/edición/eliminación de recursos (bodegas, usuarios, roles), cambios de privilegios.
- Accesos denegados (401/403) y anomalías en tasa de acceso (picos de autenticación fallida).
-
Consideraciones de privacidad y seguridad en logs:
- Nunca loggear contraseñas en texto ni secret tokens completos. Redactar o hashear valores sensibles.
- Enmascarar PII cuando sea posible.
-
Integración y almacenamiento de logs:
- Enviar logs a un sistema centralizado (ELK, Loki, Datadog, Splunk) con transporte seguro (TLS) y control de acceso.
- Habilitar alertas para eventos críticos (varios 401s/403s, intentos de fuerza bruta).
-
Retención y cumplimiento:
- Definir políticas de retención y borrado según normativas (ej. GDPR), y asegurar posibilidad de auditoría.
-
Validación y saneamiento de entradas:
- Usar modelos/serializers (p.ej. pydantic/SQLModel) para validar datos y evitar inyección.
- Evitar concatenar SQL; usar ORM/queries parametrizadas.
-
Protecciones contra CSRF/XSS:
- Si se usan cookies de autenticación (HttpOnly), habilitar
SameSitey CSRF tokens donde aplique. - Aplicar
Content-Security-Policyy sanitizar HTML de entradas de usuario.
- Si se usan cookies de autenticación (HttpOnly), habilitar
-
Seguridad en cabeceras y políticas:
- Implementar
CSP,HSTS,X-Frame-Options,X-Content-Type-Options.
- Implementar
- Backups cifrados y acceso restringido.
- Pruebas periódicas de restauración y documentación del procedimiento RTO/RPO.
- Monitorizar métricas de seguridad: tasas de login fallido, tokens expirados, 401/403, picos de tráfico.
- Preparar playbooks de respuesta: revocar keys, rotar
SECRET_KEYsi es comprometida, invalidar sesiones desde la DB.
- Obtener token (ejemplo):
curl -X POST "https://api.example.com/api/v1/login/access-token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=usuario@example.com&password=MiPass"- Llamada autenticada con token:
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
https://api.example.com/api/v1/bodegas- Verificar TLS (OpenSSL):
openssl s_client -connect api.example.com:443 -tls1_2- Secrets: Store
SECRET_KEY, DB credentials, and API keys in KMS or env vars outside repo. - TLS: Enforce TLS 1.2+/1.3, automatic certificate renewal.
- Tokens: Use short-lived access tokens; implement refresh tokens with revocation support.
- Backups: Encrypt backups and test restores monthly.
- Logging: Centralize logs, redact secrets, alert on anomalies.
- CORS: Restrict
allow_originsin production to known frontends. - Rate limit: Apply rate limiting at proxy for auth endpoints.