basquetWi + New ticket

Access-request dedup PREVENTION: attach Google identity to pre-created account on confident match

Done high pppm-pluto-cc

EVO-74 root cause: a pre-created account + the student's self-registered Google login spawns a SECOND account instead of linking. Fix the access-request match/completion flow to ATTACH the Google identity (supabaseAuthId/sub) to the existing pre-created user row on a confident match (legajo/DNI/canonical-email/name), NOT create a new user. Class-A: audit design-ping BEFORE implement + diff review BEFORE push. Design Q to settle first: which match confidence auto-links vs routes to /solicitar-acceso human-match. Must respect the live-DB Role Resolution rule (userRoles, never JWT claim) and NO auto account creation.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Questions

No questions.

Activity

  • wi-cli-venus created · 4w ago
    parent=#2214
  • wi-cli-venus note · 4w ago
    EVO-79 fleet ruling (pmmaster, converged, no Elazar fork): NEVER auto-link on human-entered key (DNI/legajo/name); ONLY auto-linkable signal = OAuth-proven canonical-email exact -> exactly ONE live pre-created account, genuine-Google re-verified at approve. Match engine + admin one-click-attach = unforked core. Pluto v1 = full audit-pluto-ca design: (1) identity persistence on accessRequests (new nullable cols requesterAuthId, requesterGoogleSub, resolvedUserId FK; captured from server session at submitAccessRequest); (2) centralized candidate resolver used by list/detail/attach/ALL create-guards, strong signals=canonical-email|normalized-legajo|normalized(tipoDoc,numDoc), name=display-only, excludes deleted/personas(ownerUserId NOT NULL)/system/disabled, returns all strong candidates+conflict state (no LIMIT/rows[0]); (3) atomic CAS attach (FOR UPDATE on req+target, refuse if target authId non-null & differs, idempotent if equal, CAS UPDATE ... WHERE authId IS NULL RETURNING, handle 23505, set archive context, never authId=NULL); (4) close every dup create path (approveAccessRequest unmatched + /admin/usuarios/nuevo?solicitud) via resolver fail-closed; (5) harden existing callback canonical-email auto-link to same CAS/conflict semantics (kept, NOT removed - email-exact tier is permitted). Identity anchor = existing UNIQUE users.authId; NO new users.googleSub col (sub forensic-only on accessRequests). Normalized legajo = upper/trim, strip approved prefix, remove all non-alnum, strip leading zeros, reject empty; 212/15==OD-0212/15; identical SQL+app-side.
  • wi-cli-venus note · 4w ago
    REVERSAL (fleet, audit-venus-ca refutation, ratified): email-exact auto-link tier DROPPED. Uniqueness != personhood — target users.email is import-dirty (reassigned/mistyped/placeholder), so a clean single canonical-email match can bind the OAuth-proven requester into the WRONG person's clinical record; no CAS/conflict check catches a clean-but-wrong single match. NO auto-link tier anywhere. Pluto callback canonical-email auto-link = CONVERTED to human-confirm routing (surface unique candidate to admin one-click-attach, never auto-bind; design the live-UX transition, don't blind-rip). Canonical-email remains a resolver STRONG signal (shown candidate) but never auto-binds. Everything else UNCHANGED: identity persistence, centralized resolver, atomic CAS on human-initiated attach, create-path INSERT-interlocks, Role Resolution untouched. Lands on original admin-attach-only recommendation.
  • wi-cli-venus note · 4w ago
    ADDENDUM (enamel SSOT ruling): users.googleSub ADDED to same DDL batch (nullable, backfilled from auth.users identities[].sub, UNIQUE partial WHERE deletedAt IS NULL - audit's call). GUARDRAIL: correlation key ONLY, never authz/Role-Resolution input. Coder stamps at create/attach/first-login. Audit acceptance bar for callback conversion: NO users identity UPDATE in callback (regression proof required); callback only routes to no-profile/access-request flow with exact-email as a shown admin candidate; identity-anchor persistence required before that route can submit; CAS only inside explicit admin attach; already-open request for same requesterAuthId => idempotent page state, not 2nd insert/500.
  • wi-cli-venus note · 4w ago
    CANONICAL legajo normalization rule (FINAL, supersedes the earlier design-read 'strip prefix + all non-alphanumerics + leading zeros' wording): REUSE the shipped NORMALIZE_LEGAJO_SQL (admin-shared.ts) AS-IS = upper -> trim -> strip '^OD-' -> keeps '/' -> strip leading zeros. Acceptance pair 212/15 == OD-0212/15 both -> '212/15'. Rationale: strip-all is more aggressive and false-merges slash-position-differing legajos (21/215 & 212/15 -> '21215'), which under human-confirm-all surfaces a WRONG candidate for one-click mis-attach = the EVO-74 false-bind harm; keep-'/' minimizes false merges + reuses the shipped roster-import/email-change matcher (zero blast radius, resolver<->attach-recheck consistent). Do NOT introduce a new normal form or migrate call-sites. D2/C3 RULING: active-existing-email branch = block + force explicit human attach (matched CAS), never approve-without-binding; soft-deleted-restore = reactivation stays separate+explicit THEN attach under the same identity CAS (Approve must not implicitly resurrect authz + bind identity) - restore-bind design deferred to a separate WI.
  • wi-cli-venus note · 4w ago
    STAGING (pmmaster hold + Elazar re-deciding login-link policy): login-branch (callback email-auto-link conversion to human-confirm + entangled C1 relax-submit-exact-email) is HELD, split to follow-up. Elazar likely lands KEEP callback auto-link + async review of only the name-mismatch subset (human-confirm-all overcorrected — batch-imported emails exist precisely to auto-link on login; real misbind risk is narrow: target email mistyped onto another real person's inbox). PLUTO-617 v1 SCOPE = CORE only: centralized resolver + all-signal create-path fail-closed dup-detection (both create paths) + atomic CAS attach + googleSub stamp/backfill + D2 fail-closed branches (active-existing-email->human attach, soft-deleted->block). This closes the dominant create-path dup vector without waiting on the login re-decision. Callback branch = separate follow-up.
  • wi-cli-venus note · 4w ago
    LOGIN-LINK POLICY FINAL (Elazar, supersedes human-confirm-all AND the venus-driven conversion; lands on the ORIGINAL harden-in-place spec): (1) callback EXACT OAuth-proven canonical-email -> one-live-account AUTO-LINK STAYS - cancel the human-confirm conversion, harden CAS-safe only (atomic CAS authId bind + genuine-Google re-verify + fail-closed if not exactly one live acct), do NOT remove; narrow mistyped-target-email residual on exact fast path Elazar-accepted. (2) similarity name/DNI/legajo/fuzzy NEVER auto-links -> suggest/confirm workflow (resolver candidates + admin CAS attach = confirm-link, never spawn). (3) create-path fail-closed interlock stays. (4) resolver/atomic-attach/googleSub-unique unchanged, HOLD released. Callback-harden folds BACK into 617 v1 (no longer a deferred branch). Audit 'callback write-free/no-identity-UPDATE' bar WITHDRAWN for the exact-email branch (it legitimately CAS-writes authId); no-silent-bind still holds for similarity.
  • wi-cli-venus completed · 4w ago
    EVO-79 access-request dedup PREVENTION shipped. SHA 97b8286 / v2.21.1, Class-A PTD PASS + audit PASS:97b8286. Delivered: centralized candidate resolver (canonical-email|normalized-legajo(slash-preserving)|normalized-documento; name display-only; excludes deleted/persona/system/disabled), create-path fail-closed interlocks on BOTH paths (approve-unmatched + createUserFromForm) on ALL strong signals, atomic CAS attach (both anchors, named-constraint 23505 classification), identity persistence (accessRequests.requesterAuthId/requesterGoogleSub/resolvedUserId + users.googleSub UNIQUE partial, migration 083 d09ed6b), hardened exact-email callback auto-link fast path (CAS-safe, genuine-Google revalidate, fail-closed if not exactly-one-live), similarity=admin-confirm-only, soft-delete branch fails closed. Follow-ups: PLUTO-620 (soft-delete restore+attach two-step), PLUTO-622 (user-facing self-suggestion surface).
task
4w ago by wi-cli-venus
4w ago
2026-07-20 08:26