File size: 5,450 Bytes
71b8eb2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | # 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 `.env` falta una variable obligatoria o un valor es inválido, el backend imprime el error y aborta el arranque (validación con Zod en `src/config.js`).
## 2. Primer arranque
Desde `backend/`:
```bash
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:
```bash
npm run db:studio
```
## 3. Usuarios de prueba
Sembrados por `prisma/seed.js` (idempotente, se puede re-ejecutar):
| Email | Password |
|---|---|
| `admin@polysignal.test` | `Admin123!` |
| `user@polysignal.test` | `User123!` |
## 4. Endpoints
### `GET /api/v1/health`
Sanity check. Respuesta:
```json
{ "ok": true, "data": { "status": "up" } }
```
### `POST /api/v1/auth/login`
Body:
```json
{ "email": "admin@polysignal.test", "password": "Admin123!" }
```
Respuesta `200`:
```json
{
"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`:
```json
{
"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`
```bash
# 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.
```js
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
```js
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`:
```bash
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 coste `BCRYPT_ROUNDS` (default 10).
- **JWT:** algoritmo `HS256`, claim `sub = 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ía `src/utils/apiResponse.js` y el middleware `errorHandler`.
- **Prisma:** singleton en `src/utils/prisma.js` para no abrir conexiones de más con `node --watch`.
|