En tancar el mòdul 7 vam deixar una pregunta incòmoda damunt la taula. La funció comprarEntrades de src/repositoris/compres-sql.js resol la sobrevenda amb una transacció impecable, bloqueja la fila de la sessió, descompta l'aforament i emet les entrades. I rep un usuariId… que arriba al cos de la petició. És a dir: del que el client vulgui enviar. Qualsevol pot comprar en nom de Lucía, consultar les comandes de Marc, publicar un esdeveniment al Teatro Almendra sense ser-ne l'organitzador o anul·lar entrades alienes.

En aquest mòdul omplim aquest buit. I comencem per allò que gairebé ningú explica bé: què és exactament autenticar, en què es diferencia d'autoritzar, per què l'HTTP ens ho posa difícil i quines estratègies existeixen per resoldre-ho. Aquesta lliçó no escriu gairebé gens de codi: construeix el mapa mental sense el qual les cinc lliçons següents són receptes que es copien sense entendre.

Contingut

  1. Autenticació davant d'autorització
  2. Factors d'autenticació i MFA
  3. El problema central: l'HTTP no té estat
  4. Estratègies per mantenir la identitat entre peticions
  5. Galetes explicades de debò
  6. El model d'amenaces d'Escena Viva
  7. Per què no s'implementa criptografia pròpia
  8. El disseny escollit per a Escena Viva
  9. Dades personals i RGPD
  10. Errors comuns i consells
  11. Exercicis
  12. Conclusió

  1. Autenticació davant d'autorització

Són dues preguntes diferents que es fan en moments diferents.

Autenticació Autorització
Pregunta Qui ets? Què pots fer?
Moment En entrar (i a cada petició, en verificar la credencial) A cada acció concreta
Resultat Una identitat: usuari 64f…, rol organitzador Un permís: sí / no
Fallada típica Credencial invàlida Identitat vàlida però sense permís
Codi HTTP 401 Unauthorized (mal batejat: significa «no autenticat») 403 Forbidden

A Escena Viva la diferència és ben concreta:

  • Autenticació: la Lucía escriu [email protected] i la seva contrasenya. El servidor comprova que la contrasenya coincideix amb el que té desat i conclou: aquesta petició ve de la Lucía.
  • Autorització: la Lucía, ja identificada, demana GET /api/comandes/ped-042. El servidor comprova que aquesta comanda és seva. Si és d'en Marc, la resposta és 403 (o 404, ja veurem per què de vegades convé).

Confondre-les produeix forats reals. Els dos errors clàssics:

  1. Autenticar i donar per fet el permís. «Si ha iniciat sessió, és que pot.» Així neix la fallada més habitual de les APIs: GET /api/comandes/:id retorna la comanda a qualsevol usuari autenticat que endevini l'identificador. Es diu referència directa insegura a objectes (IDOR), i l'ataquem a la lliçó 08-05.
  2. Autoritzar sense autenticar bé. Confiar en un rol que arriba del client, o en un usuariId del cos —exactament el que fa avui comprarEntrades—. L'autorització es recolza sobre l'autenticació: si la base és de paper, l'edifici també.

Hi ha un tercer concepte que convé anomenar per no barrejar-lo: identificació és dir qui ets (el correu); autenticació és demostrar-ho (la contrasenya). Un identificador no és mai una credencial. Enviar només usuariId és identificació pura, i per això avui l'API és indefensable.

  1. Factors d'autenticació i MFA

Un factor és una categoria de prova. N'hi ha tres de clàssiques:

Factor Què és Exemples Debilitat principal
Alguna cosa que saps Coneixement secret Contrasenya, PIN, resposta secreta Es reutilitza, es filtra, s'endevina
Alguna cosa que tens Possessió d'un objecte Mòbil amb aplicació TOTP, clau FIDO2, targeta Es perd o es roba; l'SMS s'intercepta (SIM swapping)
Alguna cosa que ets Tret biomètric Empremta, cara No es pot canviar si es compromet; sol desblocar un factor local, no viatjar per la xarxa

MFA (autenticació multifactor) és fer servir factors de categories diferents. Contrasenya + codi TOTP és MFA. Contrasenya + pregunta secreta no ho és: totes dues són «alguna cosa que saps», i totes dues es filtren a la mateixa base de dades.

Ordenats per robustesa real, de menys a més: SMS < correu electrònic < TOTP (aplicació de codis) < clau física FIDO2/WebAuthn. L'SMS és millor que res, però és l'anella que trenquen els atacs dirigits.

A Escena Viva implementarem el primer factor ben fet (contrasenya, lliçó 08-02) i deixarem el segon factor documentat com a extensió natural: el compte d'un administrador que pot anul·lar entrades i canviar rols és exactament el lloc on l'MFA deixa de ser opcional.

  1. El problema central: l'HTTP no té estat

L'HTTP és un protocol sense estat: cada petició és independent i el servidor no recorda res de l'anterior. És una virtut de disseny (permet escalar, balancejar, reintentar), però significa que:

Encara que la Lucía s'identifiqui a POST /auth/entrada, la petició següent POST /api/compres arriba com si el servidor no l'hagués vist mai.

L'única sortida és que cada petició transporti una prova d'identitat. Tota l'autenticació web es redueix a respondre tres preguntes sobre aquesta prova:

  1. On es desa al client? (galeta, memòria de JavaScript, magatzem natiu)
  2. Com viatja? (galeta automàtica, capçalera Authorization)
  3. Com la verifica el servidor? (buscant-la en un magatzem, o comprovant una signatura)

Les estratègies que vénen a continuació són combinacions diferents d'aquestes tres respostes.

  1. Estratègies per mantenir la identitat entre peticions

Estratègia Com viatja Estat al servidor Avantatges Inconvenients Quan triar-la
Bàsica HTTP Authorization: Basic base64(usuari:clau) a cada petició Cap Trivial d'implementar Envia la contrasenya un cop i un altre; sense tancament de sessió; sense caducitat; inutilitzable sense TLS Gairebé mai en producció; eines internes i proves ràpides
Sessió de servidor + galeta Galeta amb un identificador aleatori, enviada pel navegador automàticament Sí: la sessió viu al servidor Revocació instantània; la galeta no revela res; madura i ben entesa Estat compartit (necessita Redis amb diversos processos); vulnerable a CSRF si no es protegeix; incòmoda entre dominis Aplicacions web renderitzades al servidor i SPAs del mateix lloc
Token autocontingut (JWT) Authorization: Bearer <token> No (el token s'autoverifica) Sense estat, escala fàcilment; val entre dominis i per a mòbils; útil entre microserveis No es pot revocar sense afegir estat; si es desa malament al navegador, l'XSS el roba; càrrega útil visible APIs consumides per mòbil, SPAs entre dominis, servei a servei
Clau d'API Capçalera pròpia (X-API-Key) o Authorization Sí (taula de claus) Simple per a integracions; fàcil de rotar i revocar per client Identifica una aplicació, no una persona; sense caducitat natural; es filtra als repositoris Integracions màquina a màquina, webhooks, serveis interns
OAuth 2.0 / OpenID Connect Token emès per un tercer (Google, GitHub, Auth0) Depèn del flux Delega les contrasenyes al proveïdor; SSO; consentiment granular Complex; dependència d'un tercer; molts fluxos i moltes maneres d'equivocar-se «Entrar amb Google», accés a APIs de tercers, identitat corporativa

I la pregunta pràctica, resposta sense ambigüitats:

  • Aplicació mòbil nativa: tokens (JWT d'accés curt + refresc al magatzem segur del sistema). Les galetes encaixen malament fora del navegador.
  • Web amb servidor que renderitza HTML: sessions de servidor amb galeta. És l'opció més segura i senzilla; el navegador ja sap fer la seva part.
  • Servei a servei: claus d'API o credencials de client d'OAuth 2.0, amb rotació i àmbit reduït. Mai la contrasenya d'una persona.
  • SPA + API del mateix lloc: qualsevol de les dues; sessions si hi ha un únic origen, tokens si hi ha diversos clients.

Escena Viva és una API amb front-end estàtic i aspiracions d'aplicació mòbil. Per això acabarà en JWT, però amb la galeta recuperada allà on de debò importa (el token de refresc).

  1. Galetes explicades de debò

Tot el que ve després es recolza en galetes, així que convé entendre-les al detall. Una galeta és un parell nom/valor que el servidor demana al navegador que desi i reenviï automàticament.

HTTP/1.1 200 OK
Set-Cookie: sid=Zk9x3Qb7...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1800
Content-Type: application/json

A partir d'aquí, el navegador inclou pel seu compte:

GET /api/comandes HTTP/1.1
Host: api.escenaviva.test
Cookie: sid=Zk9x3Qb7...

Aquest automatisme és el gran avantatge de les galetes (no cal programar res al client) i també el seu gran perill (s'envien encara que la petició l'origini una altra web: això és CSRF).

Atribut Què fa Atac que mitiga
HttpOnly JavaScript no la pot llegir (document.cookie no la veu) XSS: encara que injectin un script, no poden exfiltrar la galeta
Secure Només viatja per HTTPS Intercepció a la xarxa (Wi-Fi oberta, proxy)
SameSite=Strict No s'envia en cap petició originada per un altre lloc CSRF (protecció màxima; trenca la navegació des d'enllaços externs)
SameSite=Lax S'envia en navegacions de nivell superior amb GET, no en POST ni en peticions en segon pla CSRF en el 95 % dels casos, sense trencar els enllaços entrants
SameSite=None S'envia sempre; obliga a Secure Cap: és per a escenaris entre dominis i exigeix defensa CSRF explícita
Domain Amplia la galeta als subdominis (Domain=escenaviva.test) Ometre'l la limita a l'amfitrió exacte: menys abast, menys risc
Path Limita a quines rutes s'envia (Path=/auth/refrescar) Redueix l'exposició del token de refresc
Max-Age / Expires Caducitat. Sense ells, la galeta és de sessió i mor en tancar el navegador Limita la finestra d'una galeta robada
Prefix __Host- El navegador exigeix Secure, Path=/ i cap Domain Impedeix que un subdomini compromès planti galetes al domini pare

Tres regles que aplicarem sense excepció:

  1. Tota galeta d'autenticació porta HttpOnly i Secure.
  2. SameSite=Lax per defecte; None només amb una raó escrita i protecció CSRF addicional.
  3. La galeta mai conté dades de l'usuari, només un identificador opac o un token signat.

  1. El model d'amenaces d'Escena Viva

Modelar amenaces és preguntar-se, abans d'escriure codi: qui ataca, què vol i què passa si ho aconsegueix?

Atacant Què vol Com ho intentaria avui Impacte
Revenedor Acaparar entrades del Festival de Jazz Automatitzar POST /api/compres sense límit Aforament exhaurit en segons; pèrdua de reputació
Tafaner amb l'API oberta Veure comandes d'altri GET /api/comandes/ped-001, ped-002… Fuita de dades personals; incident RGPD
Suplantador Comprar en nom de la Lucía Enviar l'usuariId de la Lucía al cos Càrrecs indeguts, entrades robades
Competidor d'una sala Publicar o alterar esdeveniments aliens POST /api/esdeveniments sense ser organitzador d'aquella sala Sabotatge del catàleg
Automatització de credencials Entrar amb contrasenyes filtrades d'altres llocs Milers de POST /auth/entrada Comptes compromesos en massa

I els atacs que cal conèixer pel seu nom, perquè les lliçons següents s'hi refereixen constantment:

  • XSS (Cross-Site Scripting): s'injecta JavaScript en una pàgina i s'executa amb els permisos de l'usuari. És el motiu d'HttpOnly i de no desar tokens a localStorage.
  • CSRF (Cross-Site Request Forgery): una altra web fa que el teu navegador enviï una petició autenticada sense que ho sàpigues, aprofitant l'enviament automàtic de galetes. Es combat amb SameSite i tokens sincronitzadors (08-03).
  • Fixació de sessió: l'atacant planta un identificador de sessió conegut abans de l'entrada i espera que la víctima hi entri. Defensa: regenerar l'identificador en iniciar sessió (08-03).
  • Segrest de sessió: robar una sessió ja vàlida (per XSS, xarxa insegura o registres mal fets). Defensa: HttpOnly, Secure, caducitat curta, rotació.
  • Força bruta: provar moltes contrasenyes contra un compte. Defensa: hash lent i límit d'intents (08-02, 08-06).
  • Farciment de credencials (credential stuffing): provar parells correu/contrasenya filtrats d'altres serveis contra el nostre. Defensa: límits, detecció d'anomalies, comprovació contra llistes de contrasenyes filtrades i MFA.

  1. Per què no s'implementa criptografia pròpia

La regla, sense matisos: no dissenyis criptografia i no implementis primitives criptogràfiques. Fes servir biblioteques revisades per la comunitat durant anys.

Les raons no són d'humilitat, són tècniques:

  • Un algorisme pot ser correcte i, tot i així, filtrar informació pel temps d'execució, pel consum de memòria o pels missatges d'error.
  • Els detalls que decideixen la seguretat són invisibles: farciments, vectors d'inicialització, modes d'operació, generació d'aleatorietat.
  • La criptografia trencada no falla de manera visible: continua retornant resultats perfectes fins al dia que algú la trenca.

El mateix val per als esquemes de token casolans. La temptació clàssica és desar a la galeta una cosa com ara usuari=lucia|rol=administrador|caduca=... i signar-ho «amb un hash del secret i les dades concatenades». Aquest disseny concret és vulnerable a extensió de longitud, a confusió de separadors i a comparacions no constants. JWT existeix precisament perquè aquest problema ja està resolt, amb especificació pública i biblioteques auditades.

El que sí que farem amb node:crypto és allò que està pensat per fer-se servir: generar bytes aleatoris (randomBytes), generar el hash de tokens d'un sol ús (createHash) i comparar en temps constant (timingSafeEqual, que ja vam veure al mòdul 3 amb els Buffers, i que aquí per fi pren sentit).

  1. El disseny escollit per a Escena Viva

Aquestes són les decisions del mòdul, escrites ara perquè les cinc lliçons següents en siguin la implementació:

Decisió Elecció Motiu
Magatzem de credencials Usuari de Mongoose (M7) amb hashContrasenya i select: false El model ja existeix; només li faltava la contrasenya
Hash de contrasenyes bcrypt amb factor de cost mesurat Estàndard madur, sense dependències natives problemàtiques
Sessió de servidor express-session + Passport local, ensenyat i muntat És la base conceptual i la millor opció per a moltes aplicacions
Autenticació oficial de l'API JWT d'accés (15 min) + refresc en galeta httpOnly Front-end estàtic i futur client mòbil
Autorització RBAC amb els rols ja modelats + propietat del recurs Tres rols clars; la propietat cobreix el que RBAC no cobreix
Política d'accés Centralitzada a src/autoritzacio/politica.js Es prova sola (M9) i no es dispersa en if

I el mapa de rutes resultant:

Zona Rutes Qui hi entra
Pública GET /api/esdeveniments, GET /api/esdeveniments/:id, GET /api/sessions Qualsevol
Autenticació POST /auth/registre, /auth/entrada, /auth/refrescar, /auth/sortida Qualsevol (amb límits estrictes)
Autenticada POST /api/compres, GET /api/comandes, GET /api/comandes/:id Assistent (només el que és seu)
Organitzador POST /api/esdeveniments, PATCH /api/esdeveniments/:id/publicar, GET /api/sales/:sala/vendes Organitzador d'aquella sala
Administració PATCH /api/usuaris/:id/rol, PATCH /api/entrades/:codi/anullar Administrador

El flux que implementarem entre 08-02 i 08-04:

sequenceDiagram
  participant C as Client (front-end)
  participant A as API Escena Viva
  participant D as Base de dades

  C->>A: POST /auth/registre {correu, contrasenya, nom}
  A->>A: Validar amb zod i normalitzar el correu
  A->>A: bcrypt.hash(contrasenya, cost)
  A->>D: Crear Usuari {correu, hashContrasenya, rol: 'assistent'}
  A-->>C: 201 {id, correu, nom, rol}  (sense el hash)

  C->>A: POST /auth/entrada {correu, contrasenya}
  A->>D: Cercar usuari per correu (+select hashContrasenya)
  A->>A: bcrypt.compare (temps constant)
  alt Credencials correctes
    A->>D: Desar el hash del token de refresc
    A-->>C: 200 {tokenAcces} + Set-Cookie: refresc (HttpOnly)
  else Credencials incorrectes
    A-->>C: 401 missatge generic i identic
  end

  C->>A: GET /api/comandes (Authorization: Bearer tokenAcces)
  A->>A: Verificar signatura, exp, iss, aud
  A->>D: Consultar comandes filtrant per req.usuari.id
  A-->>C: 200 [comandes propies]

Fixa't en l'últim pas: la consulta filtra per l'usuari autenticat, no compara després. És la diferència entre una API segura i una amb IDOR.

  1. Dades personals i RGPD

Autenticar implica desar dades personals, i això té conseqüències legals a més de tècniques.

  • Minimització: desa només allò imprescindible. Per vendre entrades n'hi ha prou amb correu, nom i rol. Ni DNI, ni telèfon, ni data de naixement «per si de cas».
  • Finalitat: les dades recollides per vendre entrades no es fan servir per a màrqueting sense un consentiment separat.
  • Dret de supressió: hi ha d'haver una via real per esborrar un compte. Compte: les entrades venudes solen tenir obligacions comptables, així que l'habitual és anonimitzar l'usuari i conservar la comanda.
  • Contrasenyes: un hash bcrypt és una dada personal a la pràctica. Tracta'l com a tal.
  • Registres: els logs amb correus i IPs també són dades personals; necessiten política de retenció.
  • Notificació de bretxes: si es filtren credencials hi ha obligació de notificar dins de termini. Tenir l'incident previst per escrit, abans que passi, forma part de la feina.

A Escena Viva fem servir correus ficticis amb domini .test precisament per no manejar dades reals durant el curs.

Errors Comuns i Consells

  • Creure que 401 significa «sense permís». Significa «no autenticat» (el nom de l'especificació és desafortunat). Sense permís és 403. Confondre'ls fa que els clients redirigeixin a l'entrada un usuari que ja hi ha entrat.
  • Desar el rol al client i confiar-hi. Amagar un botó al front-end no és autorització: la petició es pot enviar igualment amb curl.
  • Fer servir localStorage per al token perquè «és més còmode». Un sol XSS el converteix en un robatori d'identitat silenciós. Ho veurem en detall a 08-04.
  • Afegir SameSite=None per «arreglar» un problema de CORS. Sol ser el símptoma d'un disseny de dominis mal pensat, i obre la porta al CSRF.
  • Pensar que el TLS resol l'autenticació. L'HTTPS protegeix el transport, no diu qui ets ni què pots fer.
  • Consell: escriu el model d'amenaces abans del codi, encara que siguin deu línies en un fitxer. És el que evita implementar defenses per a atacs que no t'afecten i oblidar el que sí.
  • Consell: distingeix sempre credencial d'identificador. Si una dada n'hi ha prou per actuar en nom d'algú, és una credencial i mereix protecció de credencial.

Exercicis

Exercici 1: classificar peticions

Per a cadascuna d'aquestes situacions d'Escena Viva, indica si la fallada és d'autenticació o d'autorització, i quin codi HTTP correspon:

  1. Algú crida POST /api/compres sense cap credencial.
  2. En Marc, amb sessió vàlida, demana GET /api/comandes/ped-042, que és de la Lucía.
  3. L'organitzador de la Sala Bóveda intenta publicar evt-001, del Teatro Almendra.
  4. S'envia un token d'accés caducat.
  5. Un assistent intenta PATCH /api/usuaris/:id/rol.

Exercici 2: triar estratègia

Justifica en dues o tres frases quina estratègia de la taula de la secció 4 triaries per a:

  1. Un tauler intern d'administració d'Escena Viva, servit pel mateix servidor amb plantilles HTML.
  2. Una futura aplicació mòbil d'Escena Viva per validar entrades a la porta de l'Auditorio Ribera.
  3. Un servei de facturació que crida cada nit GET /api/sales/:sala/vendes.

Exercici 3: capçalera Set-Cookie

Escriu la capçalera Set-Cookie completa per al token de refresc d'Escena Viva sabent que: l'API viu a api.escenaviva.test, el token només s'ha d'enviar a /auth/refrescar, dura 30 dies, no ha de ser llegible per JavaScript, només viatja per HTTPS i no volem que s'enviï des d'altres llocs. Explica cada atribut.

Solucions

Exercici 1

Cas Tipus Codi
1 Autenticació (no hi ha identitat) 401
2 Autorització (identitat vàlida, recurs aliè) 403 o 404 per no revelar-ne l'existència
3 Autorització (rol correcte, propietat incorrecta) 403
4 Autenticació (credencial ja no vàlida) 401, indicant que ha caducat perquè el client refresqui
5 Autorització (rol insuficient) 403

El cas 3 és especialment instructiu: el rol organitzador és correcte i, tot i així, l'operació s'ha de denegar. RBAC tot sol no basta; cal comprovar la propietat del recurs (08-05).

Exercici 2

  1. Sessions de servidor amb galeta. És un únic origen, el servidor renderitza HTML, el navegador gestiona la galeta sense codi propi i la revocació és immediata: si acomiades un administrador, esborres la seva sessió i queda fora a l'instant.
  2. JWT d'accés curt + token de refresc desat al magatzem segur del sistema operatiu. Les galetes no encaixen fora del navegador i el validador d'entrades ha de funcionar amb connectivitat intermitent; un token signat amb caducitat curta es verifica sense consultar la base a cada lectura.
  3. Clau d'API (o credencials de client OAuth 2.0) amb àmbit de només lectura sobre vendes, rotable i revocable de manera independent. No és una persona: no ha de tenir contrasenya ni sessió.

Exercici 3

Set-Cookie: refresc=<token>; HttpOnly; Secure; SameSite=Strict; Path=/auth/refrescar; Max-Age=2592000
  • HttpOnly: document.cookie no la veu, així que un XSS no la pot exfiltrar.
  • Secure: només per HTTPS; mai en clar per la xarxa.
  • SameSite=Strict: no s'envia en peticions originades per altres llocs, cosa que anul·la el CSRF sobre el punt d'entrada de refresc.
  • Path=/auth/refrescar: no s'adjunta a les crides normals de l'API, i en redueix l'exposició en logs i proxies.
  • Max-Age=2592000: 30 dies en segons; caducitat explícita en comptes de galeta de sessió.
  • Sense Domain: la galeta queda lligada a l'amfitrió exacte api.escenaviva.test, sense propagar-se als subdominis.

Conclusió

Ja tenim el mapa. Autenticar és demostrar qui ets; autoritzar és decidir què pots fer; confondre-ho és la causa dels forats més cars. L'HTTP no té estat, així que cada petició ha de portar una prova d'identitat, i les estratègies per aconseguir-ho —bàsica, sessions, JWT, claus d'API, OAuth— tenen cadascuna el seu terreny. Les galetes, amb els seus atributs HttpOnly, Secure i SameSite, són la infraestructura sobre la qual es recolza gairebé tot, i cada atribut neutralitza un atac concret. Coneixem el model d'amenaces d'Escena Viva, sabem que no inventarem criptografia i tenim el disseny escrit: sessions per entendre, JWT amb refresc en galeta per produir, RBAC més propietat del recurs per autoritzar.

Falta el primer de tot: que existeixi una contrasenya per verificar. El model Usuari fa des de la lliçó 07-02 que porta el seu camp rol i cap camp de contrasenya, a propòsit. A la lliçó següent, Registre d'Usuaris i Hash de Contrasenyes, omplim aquest buit: per què mai no es desa una contrasenya en clar ni amb SHA-256, què fa realment bcrypt, com se'n tria el factor de cost, com s'escriu un POST /auth/registre que no filtri quins correus existeixen, i com es fan bé els tokens de verificació i de recuperació, que és on més projectes es trenquen.

Curs de Node.js: De Principiant a Avançat

Mòdul 1: Introducció a Node.js

Mòdul 2: Conceptes Bàsics

Mòdul 3: Sistema de Fitxers i E/S

Mòdul 4: HTTP i Servidors Web

Mòdul 5: NPM i Gestió de Paquets

Mòdul 6: Framework Express.js

Mòdul 7: Bases de Dades i ORMs

Mòdul 8: Autenticació i Autorització

Mòdul 9: Proves i Depuració

Mòdul 10: Temes Avançats

Mòdul 11: Desplegament i DevOps

Mòdul 12: Projectes del Món Real

© Copyright 2026. Tots els drets reservats