docs: sync from backend 259f3fb — H19-H21 auth save consolidation

This commit is contained in:
Siavash Sameni
2026-06-07 10:09:14 +04:00
parent ae10a16481
commit aac297d241
2 changed files with 17 additions and 6 deletions

View File

@@ -12,6 +12,16 @@ entries on top. Maintained by agents per the rule in `../AGENTS.md`.
--- ---
### 2026-06-07 — backend@259f3fb, frontend@d9a59bd — DB audit H19-H21 auth save consolidation
**Commits:** `259f3fb` `d9a59bd`
**Touched:** backend `src/services/auth/authController.ts`, `__tests__/db-audit-auth-controller-saves.test.ts`, `scripts/smoke/db-audit-service-regressions.sh`, `package.json`, `package-lock.json`; frontend `Dockerfile`, `package.json`; docs `09 - Audits/DB Query & Schema Audit - 2026-06-06.md`, `09 - Audits/Activity Log.md`
**Why:** Close High H19-H21 from the DB Query & Schema Audit. Login, Google sign-in, and Telegram auth now use the token helper without immediate persistence, stage audited mutations, and perform one final user save through the transactional save path. Telegram Mini App retry behavior remains preserved with no replay/dedup rejection added.
**Verification:** backend `npm test -- --runTestsByPath __tests__/db-audit-auth-controller-saves.test.ts __tests__/auth-store-pg-query.test.ts --runInBand` (2 suites / 18 tests), `npm run typecheck`, `scripts/smoke/db-audit-service-regressions.sh` (19 suites / 77 tests), backend/frontend scoped `git diff --check`; frontend/backend version metadata confirmed at v2.9.37. Pushed to Forgejo.
**Linked docs updated:** [[09 - Audits/DB Query & Schema Audit - 2026-06-06]]
---
### 2026-06-07 — backend@5d7d2af, frontend@ade7352 — DB audit H10 sweep balance probe parallelism ### 2026-06-07 — backend@5d7d2af, frontend@ade7352 — DB audit H10 sweep balance probe parallelism
**Commits:** `5d7d2af` `ade7352` **Commits:** `5d7d2af` `ade7352`

View File

@@ -86,6 +86,7 @@ updated: 2026-06-07
| M17: profile email verification pending-email race → single conditional SQL `UPDATE` with conflict outcome handling | `c3ad979` v2.9.34 | | M17: profile email verification pending-email race → single conditional SQL `UPDATE` with conflict outcome handling | `c3ad979` v2.9.34 |
| C2/M26: `DrizzleChatRepo.findRows` unbounded chat fetch + JS pagination → bounded row scans, SQL pagination for SQL-pushable predicates, `findOne` `LIMIT 1`/id fast path, type pushdown, and archived-chat index | `8835068` v2.9.35 | | C2/M26: `DrizzleChatRepo.findRows` unbounded chat fetch + JS pagination → bounded row scans, SQL pagination for SQL-pushable predicates, `findOne` `LIMIT 1`/id fast path, type pushdown, and archived-chat index | `8835068` v2.9.35 |
| H10: `sweepDerivedDestinations` sequential token-balance RPC probes → bounded parallel balance probe phase before sequential sweep/broadcast handling | `5d7d2af` v2.9.36 | | H10: `sweepDerivedDestinations` sequential token-balance RPC probes → bounded parallel balance probe phase before sequential sweep/broadcast handling | `5d7d2af` v2.9.36 |
| H19-H21: login, Google sign-in, and Telegram auth duplicate user saves → token helper save bypass plus one final user persistence after audited mutations | `259f3fb` v2.9.37 |
--- ---
@@ -365,33 +366,33 @@ Registration completes in separate writes: (1) `referrer.save()` increments refe
--- ---
### 19. login calls user.save() twice in the same request path without a transaction ### 19. login calls user.save() twice in the same request path without a transaction | **FIXED** `259f3fb` v2.9.37
> **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:405-426` > **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:405-426`
The login handler calls `user.save()` twice: once for `lastLoginAt` (line 407) and again after appending the refresh token (line 425). Each `save()` triggers the full `savePgUser` UPSERT + token-DELETE-INSERT + passkey-DELETE-INSERT sequence. No transaction spans both writes; a crash between them can leave an inconsistent token state. The login handler calls `user.save()` twice: once for `lastLoginAt` (line 407) and again after appending the refresh token (line 425). Each `save()` triggers the full `savePgUser` UPSERT + token-DELETE-INSERT + passkey-DELETE-INSERT sequence. No transaction spans both writes; a crash between them can leave an inconsistent token state.
**Fix:** Mutate all fields (`lastLoginAt`, `refreshTokens`) before calling `user.save()` once, wrapped in the transaction described in the savePgUser finding. **Fix:** Implemented in `259f3fb`: `issueTokensForUser` now accepts `{ save: false }` so login mutates `lastLoginAt` and `refreshTokens` first, then calls `user.save()` once through the transactional save path before setting the cookie/response.
--- ---
### 20. Google sign-in calls user.save() twice in the same request without a transaction ### 20. Google sign-in calls user.save() twice in the same request without a transaction | **FIXED** `259f3fb` v2.9.37
> **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:1381-1416` > **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:1381-1416`
`googleSignIn` calls `existingUser.save()` at line 1398 (profile/lastLoginAt update), then again at line 1416 after pushing the refresh token. Two separate `savePgUser` executions each with their own DELETE+INSERT loops, without a shared transaction. `googleSignIn` calls `existingUser.save()` at line 1398 (profile/lastLoginAt update), then again at line 1416 after pushing the refresh token. Two separate `savePgUser` executions each with their own DELETE+INSERT loops, without a shared transaction.
**Fix:** Consolidate all mutations and call `save()` once, wrapped in a transaction. **Fix:** Implemented in `259f3fb`: profile/email-verification cleanup, `lastLoginAt`, and refresh-token mutation are staged together, then `existingUser.save()` runs once through the transactional save path.
--- ---
### 21. Telegram auth performs up to 4 separate user.save() calls without a transaction ### 21. Telegram auth performs up to 4 separate user.save() calls without a transaction | **FIXED** `259f3fb` v2.9.37
> **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:459-613` > **Category:** Missing Transaction | **File:** `src/services/auth/authController.ts:459-613`
`telegramAuth` can call `user.save()` for a new user (line 533), `user.save()` inside `issueTokensForUser` (line 148), and a try-catch save for mutations (line 558). Each invokes the full `savePgUser` path. With no transaction, a failure between any two leaves the DB inconsistent. `telegramAuth` can call `user.save()` for a new user (line 533), `user.save()` inside `issueTokensForUser` (line 148), and a try-catch save for mutations (line 558). Each invokes the full `savePgUser` path. With no transaction, a failure between any two leaves the DB inconsistent.
**Fix:** Merge all mutations and issue a single `save()` inside a transaction before responding. **Fix:** Implemented in `259f3fb`: new-user creation, `telegramVerified`, lazy referral-code backfill, `lastLoginAt`, and refresh-token mutation are staged before one `user.save()`; Mini App retry behavior remains unchanged, with no replay/dedup rejection added to the Telegram login path.
--- ---