PLUTO-56 ·
plutoOnboarding: allowlisted-but-no-profile users (loginDeniedNoProfile) should be routed to request access, not dead-bounced
- Ref
PLUTO-56(#928)- Project
pluto- Status
- done
- Priority
- normal
- Type
- task
- Assigned
- —
- Created by
- wi-cli-venus
- Created
- 2026-06-12T06:03:17.157Z
- Updated
- 2026-06-12T06:28:10.254Z
- Closed
- 2026-06-12T06:28:10.254Z
Questions
No questions.
Event log
-
Elazar go (2026-06-12): 'make it easier'. From appEvents: loginDeniedNoProfile fired 202x (ongoing to 2026-06-11), only 49 converted to access requests -> most blocked people just leave. Goal: when a user authenticates with Google but has no Pluto profile, give them a clear, easy path to request access (e.g. route to /solicitar-acceso with context + CTA, or pre-fill an access request) instead of a dead bounce. INVESTIGATE current behavior first (where do no-profile users land today? auth/callback gate) and propose the UX — do NOT pre-commit a specific implementation. Touches the auth gate -> audit design-ping BEFORE coder implements + audit diff review before push. Related: PLUTO-54 adds email logging to the same loginDeniedNoProfile path (sequence 54 before 56 to avoid collision; same coder).
-
AUDIT DESIGN-PING (audit-pluto-cc, PASS-to-dispatch). Root cause of 202->49: no-profile is NOT a signOut — auth/callback/route.ts:169-175 keeps session alive and redirects /login?error=no-profile, where login-client.tsx:51-76 shows a RED error card + a 2nd-click 'Solicitar acceso' link. Rejection-framed => users bounce. The email-domain-denied case (L99) goes DIRECT to /solicitar-acceso?reason=email-domain. The asymmetry is the bug. FIX (minimal, ~1-line + copy): (1) callback L175 redirect -> /solicitar-acceso?reason=no-profile (session already alive, no signOut — adding one re-creates the loop L96-98 warns about). (2) solicitar-acceso/page.tsx ~L25 add reason==='no-profile' Alert, WELCOMING copy not error-red: 'No encontramos un perfil asociado a tu cuenta. Completá el formulario para solicitar acceso.', keep AccessRequestForm initialEmail emailLocked. (3) distinct requestSource e.g. 'no_profile' vs email-domain 'login_mismatch' for conversion measurement. INVARIANTS: isAllowlistedEmail gate stays first (L72); pre-registered branch L107-167 untouched, only L169-175 changes; email from verified session data.user.email + emailLocked, never user-typed (enumeration guard), no observable already-has-profile/pending diff; keep email-domain vs no-profile DISTINCT; NO auto-create, stays human via createAccessRequest. Touches auth gate -> audit diff-review before push.
-
Shipped: no-profile onboarding redirect. auth/callback no-profile branch now redirects to /solicitar-acceso?reason=no-profile (was /login?error=no-profile dead-end) with welcoming copy + distinct requestSource='no_profile' on the access request, session kept alive. Converts a confused login error into a working access-request flow. SHA 38b399a / v1.67.27. Audit PASS:38b399a - deploy READY+aliased, live version+deploymentId matched, zero runtime errors. Conversion lift (202 no-profile -> access-request) measurable via requestSource='no_profile' rows - db's surface. Residual /login?error=no-profile no-session fallback (L19) left intact -> PLUTO-60.