Commit Graph

7 Commits

Author SHA1 Message Date
Inna 79520a57ff El login exige un correo, no un nombre de cuenta
Con Battle.net se entra con el correo, pero el campo aceptaba cualquier cosa: el
<form> lleva `noValidate`, así que el navegador no comprobaba su propio
type="email", y el servidor solo miraba que no estuviera vacío. Entrar con «15#1»
llegaba hasta `authenticate` y devolvía «Correo o contraseña incorrectos», que
manda a buscar el fallo donde no está.

Se valida en el servidor (autoritativo) y en el cliente (aviso inmediato), con un
mensaje propio: «Introduce tu correo electrónico, no el nombre de la cuenta.».

`EMAIL_RE` es a propósito permisiva —solo exige una @ con algo a cada lado, sin
punto en el dominio ni restricción a Gmail—: el registro solo admite Gmail HOY,
pero de las 17 cuentas Battle.net que existen, 16 NO son de Gmail (INNA@INNA.CL,
y hasta Q@Q sin dominio). Validar de más al entrar las habría dejado fuera a
todas. Comprobado contra los 14 correos reales: 0 bloqueadas, y rechaza 15#1,
17#1 y demás nombres de cuenta.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 18:57:25 +00:00
Inna b9803adeb9 web-next: rediseño de security-token, recover, vote-points + rename-guild
- /security-token: diseño completo del original (info + advertencia + NOTA 7 días),
  fecha en formato HH:MM:SS DD-MM-YYYY, botón "Token enviado", enlace a /recover.
- /recover: 4 opciones (contraseña por usuario, nombre de cuenta, token de seguridad
  y enlace de activación por correo); recuperación de token nueva; Turnstile por envío.
- /vote-points: caja de info con las 7 secciones Q&A + panel "Sitios de votación" con
  tarjetas inline-div (imagen, PV, último voto), botón refrescar; abre el sitio y acredita
  PV al volver. lib/vote.ts + getVoteSitesForAccount.
- /rename-guild: renombra una hermandad de la que la cuenta es Maestro por 1000 PD +
  token de seguridad; SOAP .guild rename con comillas; reembolsa los PD si SOAP falla.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:06:49 +00:00
Inna 3573b7eb40 Recuperación de cuenta en Next.js (3 flujos + reset), con Turnstile
- lib/recover.ts: requestRecovery(type,email) -> password (crea token en
  home_passwordreset + email), accountname (email con las cuentas), activation
  (reenvía enlace); respuesta genérica anti-enumeración. resetPassword(token,pass)
  re-deriva el verifier SRP6 v2 (bnetMakeRegistration) y actualiza battlenet_accounts,
  marca el token usado.
- Routes /api/auth/recover (Turnstile) y /api/auth/reset-password.
- Páginas app/[locale]/recover (RecoverForm: selector de tipo + email + Turnstile) y
  reset-password (ResetForm con token de la URL). Catálogos Recover, Reset.

Verificado: recover sin captcha -> captchaFailed; reset token inválido -> invalidLink;
páginas 200. Reutiliza home_passwordreset de Django. Crypto de reset ya validada.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 23:07:16 +00:00
Inna aa61f79a55 Cloudflare Turnstile en login y registro (Next.js)
- lib/turnstile.ts: verifyTurnstile(token, ip) contra siteverify (fail-closed).
- components/Turnstile.tsx: widget cliente (carga el script de Cloudflare, callback
  con el token). Site key por NEXT_PUBLIC_TURNSTILE_SITE_KEY; secreto server-only.
- LoginForm/RegisterForm: widget + token en el POST; submit deshabilitado hasta
  resolver el captcha (si hay site key).
- Routes login/register: verifican el token antes de continuar (error captchaFailed).
- Catálogos: captchaFailed.

Verificado: POST sin token -> captchaFailed en login y registro (enforcement server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 23:04:22 +00:00
Inna f332fc39ca Registro + activación por email en Next.js (SRP6 + nodemailer)
- lib/mail.ts: transporte SMTP (nodemailer, mismas creds Gmail que Django).
- lib/register.ts: registerAccount (valida, comprueba email existente, crea fila en
  home_accountactivation reutilizando la tabla de Django, envía email de activación);
  activateAccount (crea battlenet_accounts con SRP6 v2 + account con SRP6 Grunt, como
  activate_account_view: expansion=2, battlenet_index=1; borra la activación).
- Route handlers /api/auth/register y /api/auth/activate.
- Páginas app/[locale]/register (RegisterForm cliente) y app/[locale]/activate-account
  (ActivateClient auto-POST al abrir el enlace). Textos en messages (Register, Activate).

Validado: validaciones (missingFields/passwordMismatch/invalidEmail/passwordTooLong);
ciclo completo activación->crea bnet+account->login OK con la cuenta creada
(needsSelection=false), password mala->invalidCredentials. Todo en TS, crypto validada.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 22:53:09 +00:00
Inna e00a439dd9 Cierra el flujo de login: logout, selector de cuenta y /account con guardas
- Route handlers: /api/auth/logout (destroy sesión), /api/auth/select-account
  (fija la cuenta de juego elegida, valida que pertenece a la bnet en sesión).
- app/[locale]/select-account: página SSR (guarda: sin bnetId -> login) + lista de
  cuentas de juego (SelectAccountForm cliente) -> /account.
- app/[locale]/account: página protegida (sin bnetId -> login; sin username ->
  select-account) con datos de sesión + LogoutButton.
- Textos en messages (Account, SelectAccount).

Verificado: guardas locale-aware (ES /account -> /login, EN /en/account -> /en/login,
307), logout OK, select-account sin sesión 401. Redirect i18n con next-intl.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 22:46:42 +00:00
Inna 60c2e44afd Login end-to-end en Next.js: iron-session + auth SRP6 + página i18n
- lib/session.ts: sesión cifrada httpOnly con iron-session (SESSION_SECRET en
  .env.local). SessionData {bnetId, bnetEmail, username, accountId}.
- lib/auth.ts: authenticate(email,password) verifica contra battlenet_accounts con
  bnetVerify (SRP6 v2); getGameAccounts(bnetId). Resiliente si acore no está.
- app/api/auth/login/route.ts: POST -> autentica, fija sesión, resuelve cuentas de
  juego (1 -> auto; 0/varias -> needsSelection). Devuelve JSON.
- app/[locale]/login: página SSR + LoginForm (cliente, next-intl, router i18n).
  Textos en messages/*.json (namespace Login).
- tsconfig target ES2020 (literales BigInt de bnet.ts).

Validado end-to-end: con una cuenta creada por bnet.py (como haría AzerothCore),
el login TS la verifica OK y emite la cookie de sesión; password incorrecta ->
invalidCredentials; sin campos -> missingFields. Página en ES y EN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 22:42:23 +00:00