Skip to content

0050. Alta de cuentas solo por invitación: retiro del signup público

Estado

Aceptada. Supersede ADR-0013 (registro en 3 fases).

Contexto

El admin web nació con dos vías paralelas para crear la primera cuenta de una organización: el signup público (POST /auth/signupverify-email OTP → onboarding, ADR-0013) y, desde platform-rbac-backend/platform-onboarding-tenants (2026-07-20/21), el alta desde plataforma (el superadmin invita a un admin, con o sin nombrar la empresa de antemano — ver ADR-0049). El signup público quedó documentado como transitorio en plataforma/superadmin-flow.md desde que apareció la vía de plataforma, pendiente de retiro en el backlog (deprecar-signup-publico).

Con el modo delegado (ADR-0049) operativo, la pregunta que bloqueaba el retiro — "¿cómo nombra su empresa un cliente que llega por invitación, sin auto-registro público?" — ya tenía respuesta: el superadmin invita sin nombrar la empresa y el cliente la nombra al aceptar, vía el mismo POST /auth/onboarding que ya usaba el signup. Sin ese hueco, el signup público pasó a ser una puerta de alta redundante: dejaba registros abandonados en pending_verification (7 días de gracia, job de expiración) y acoplaba 16 specs e2e del backend a una superficie que ya no reflejaba cómo se dan de alta cuentas en producción.

Decisión

Se retira el signup público por completo: POST /auth/signup, POST /auth/verify-email, POST /auth/resend-code y las páginas/rutas correspondientes del admin (SignUpPage, VerifyEmailPage, /signup, /verify). Toda cuenta nueva entra por POST /auth/accept-invitation — de tenant, de plataforma directa, o de plataforma delegada (ADR-0049) — que ya bifurca según si el invitado tiene membresía.

Lo que sobrevive intacto (no quedan huérfanos, corrigiendo el relevamiento inicial de esta tarea, ver ADR-0049 §"Efecto colateral"):

  • POST /auth/onboarding, el OnboardingPage del admin y todo el scope onboarding (issueOnboardingToken, OnboardingJwtGuard, JWT_ONBOARDING_SECRET) — alimentados ahora solo por la invitación delegada.
  • El estado pending_onboarding — sigue vivo, producido por accept-invitation en vez de verify-email.
  • accept-invitation, compartido por los tres flujos de alta.

Lo que queda sin productor pero se conserva (para no forzar una migración destructiva de tipo en la misma tarea):

  • El valor pending_verification del enum UserStatus — las filas existentes se purgan en la migración, pero el valor del enum no se elimina.
  • El valor email_verification de la tabla verification_tokens.

El job expire-pending-accounts se repropósita: deja de cubrir pending_verification (sin productor) y pasa a limpiar usuarios invited sin membresía con más de 7 días — el hueco de invitaciones delegadas abandonadas que abrió el modo delegado y que hasta ahora nadie limpiaba.

signupAdmin() (helper de los 16 specs e2e del backend) deja de pasar por el flujo HTTP y siembra directo por Prisma (organización + rol Administrador + curvas de alimentación por defecto + membresía + signin para los tokens) — reproduce exactamente lo que hacía AuthService.onboarding(), sin acoplar los specs a la superficie de plataforma que reemplazó al signup.

Consecuencias

  • Breaking intencional: /auth/signup, /auth/verify-email, /auth/resend-code pasan a 404. Los únicos consumidores eran el admin (actualizado en la misma tarea) y Postman/docs; mobile nunca los llamó.
  • SignInPage pierde el link "¿No tienes cuenta? Regístrate" — sin auto-registro no hay nada a lo que apuntar; se decidió no reemplazarlo por un texto informativo, dejando el formulario solo con signin + "¿olvidaste tu contraseña?".
  • El shape de SigninPendingResponseDto.status se angosta a 'pending_onboarding' (antes admitía también 'pending_verification').
  • camaroneras_mobile pierde una mitad de su chequeo isPending (pending_verification || pending_onboarding) sin cambio de código: el mensaje "Completa tu registro desde la web" sigue siendo correcto para la mitad que sobrevive.

Referencias

  • Tarea deprecar-signup-publico (2026-07-22).
  • ADR-0049 — decisión previa que desbloqueó este retiro.
  • camaroneras_backend/src/modules/auth/ (controller/service), src/jobs/expire-pending- accounts.job.ts, test/helpers/bootstrap.ts (signupAdmin), migración 20260722152139_drop_pending_verification_default.
  • camaroneras_admin/src/features/auth/ (páginas, api, hooks, schemas), src/app/router.tsx.
  • camaroneras_docs/plataforma/auth-flow.md, admin-ui-flow.md, auth-mobile-flow.md, superadmin-flow.md, arquitectura/index.md (contratos actualizados).
  • _planning/_backlog/backlog.md → entrada deprecar-signup-publico (derivados anotados: enum pending_verification, valor email_verification, camaroneras_mobile diferido).