Auth — Guía de uso
Login con email + password. El backend emite un JWT (HS256, default 1h) que viaja en Authorization: Bearer <token>. No hay registro público en esta fase: los usuarios se crean vía el seeder. No hay roles — requireAuth solo valida que el usuario esté logueado y activo, para que pueda mantener sus preferencias.
1. Variables de entorno
Copiar backend/.env.example a backend/.env y rellenar:
| Variable | Default | Notas |
|---|---|---|
NODE_ENV |
development |
development | test | production |
PORT |
7860 |
Puerto del backend (HF Spaces requiere 7860) |
DATABASE_URL |
file:./polysignal.db |
Path SQLite relativo a prisma/schema.prisma |
JWT_SECRET |
— (obligatorio) | Mínimo 32 chars. Generar con openssl rand -hex 48 |
JWT_EXPIRES_IN |
1h |
Formato jsonwebtoken (1h, 15m, 7d, ...) |
BCRYPT_ROUNDS |
10 |
Entre 4 y 15 |
CORS_ORIGIN |
http://localhost:5173 |
Origen del frontend en dev |
LOG_LEVEL |
info |
trace/debug/info/warn/error/fatal |
Si el
.envfalta una variable obligatoria o un valor es inválido, el backend imprime el error y aborta el arranque (validación con Zod ensrc/config.js).
2. Primer arranque
Desde backend/:
npm install # instala deps
npm run db:migrate -- --name init_auth # solo la primera vez (ya hecho)
npm run db:seed # crea los 2 usuarios de prueba
npm run dev # arranca en http://localhost:7860
Para inspeccionar la DB en una UI:
npm run db:studio
3. Usuarios de prueba
Sembrados por prisma/seed.js (idempotente, se puede re-ejecutar):
| Password | |
|---|---|
admin@polysignal.test |
Admin123! |
user@polysignal.test |
User123! |
4. Endpoints
GET /api/v1/health
Sanity check. Respuesta:
{ "ok": true, "data": { "status": "up" } }
POST /api/v1/auth/login
Body:
{ "email": "admin@polysignal.test", "password": "Admin123!" }
Respuesta 200:
{
"ok": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"user": { "id": 1, "email": "admin@polysignal.test" }
}
}
Errores:
| HTTP | code | cuándo |
|---|---|---|
400 |
VALIDATION_ERROR |
Email inválido o password < 8 chars |
401 |
INVALID_CREDENTIALS |
Email no existe, usuario desactivado, o password incorrecta |
429 |
TOO_MANY_REQUESTS |
Más de 5 intentos en 15 min desde la misma IP |
GET /api/v1/auth/me
Requiere header Authorization: Bearer <token>. Respuesta 200:
{
"ok": true,
"data": {
"user": {
"id": 1,
"email": "admin@polysignal.test",
"isActive": true,
"createdAt": "2026-05-16T07:11:43.000Z"
}
}
}
Errores:
| HTTP | code | cuándo |
|---|---|---|
401 |
UNAUTHORIZED |
Sin header, token mal formado, expirado, manipulado, o usuario desactivado |
5. Ejemplos con curl
# 1) Login y guardar token en variable
TOKEN=$(curl -s -X POST http://localhost:7860/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@polysignal.test","password":"Admin123!"}' \
| jq -r '.data.token')
# 2) Llamar a /me con el token
curl -s http://localhost:7860/api/v1/auth/me \
-H "Authorization: Bearer $TOKEN" | jq
6. Login desde el frontend (referencia)
El JWT es opaco para el front: basta con guardarlo (sessionStorage o estado en memoria) y enviarlo en cada request protegido.
const res = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const json = await res.json();
if (!json.ok) throw new Error(json.error.code);
const { token, user } = json.data;
// guardar token y user
// requests autenticados
fetch('/api/v1/auth/me', { headers: { Authorization: `Bearer ${token}` } });
En dev, Vite proxea
/api/*al backend (localhost:7860); no hace falta CORS si va por el proxy, pero ya está configurado por si el front llama directo.
7. Cómo proteger nuevos endpoints
import { requireAuth } from '../middlewares/requireAuth.js';
router.get('/positions', requireAuth, controller.list);
Dentro del controller, req.user ya está disponible con { id, email, isActive, createdAt } — suficiente para filtrar datos del usuario logueado (ej. sus preferencias, posiciones, watchlist).
8. Rotar JWT_SECRET
Genera uno nuevo y reemplaza el valor de JWT_SECRET en backend/.env:
openssl rand -hex 48
Al cambiar el secreto, todos los tokens emitidos previamente quedan invalidados (los clientes deben volver a hacer login).
9. Notas técnicas
- Hashing:
bcryptjs(puro JS, sin compilación nativa — más portable a HF Spaces) con costeBCRYPT_ROUNDS(default 10). - JWT: algoritmo
HS256, claimsub = user.id,email,iat,exp. - Rate limit en
/auth/login:express-rate-limit, 5 intentos / 15 min / IP. - Validación de body: zod schema en
src/auth/auth.validators.js. - Errores formateados: todas las respuestas siguen
{ ok, data }o{ ok:false, error:{ code, message, details? } }víasrc/utils/apiResponse.jsy el middlewareerrorHandler. - Prisma: singleton en
src/utils/prisma.jspara no abrir conexiones de más connode --watch.