Summary
POST /api/auth/login bootstraps the optional legacy shared account on every login request by calling ensureLegacySharedUser(). If DIFFAUDIT_SHARED_USERNAME matches an existing normal user's username, ensureLegacySharedUser() overwrites that user's passwordHash with the shared-password value instead of failing closed or only managing a previously bootstrapped shared account.
Evidence
apps/web/src/app/api/auth/login/route.ts:21 calls await ensureLegacySharedUser() before checking the submitted login credentials.
apps/web/src/lib/auth.ts:97-112 looks up any row in users by username = DIFFAUDIT_SHARED_USERNAME.
apps/web/src/lib/auth.ts:134-145 treats a password-hash mismatch as a rotation event and writes a new hash for the existing row.
apps/web/src/lib/auth.test.ts:134-148 currently asserts that changing the env password changes the password for the username, which masks the production behavior where the row may be a real user, not a shared fixture created by the bootstrapper.
Impact
A deployer who enables the legacy shared login with a username that already exists can silently reset that user's password to the shared password. Because the bootstrap runs on every login request, the takeover can happen without an authenticated admin action or an explicit account-migration step.
Why this is real / non-duplicate
This is not covered by the open auth PRs for self-verification tokens, OAuth redirect targets, OAuth public origin handling, demo mode, 2FA enforcement, SSRF, or fake session cookies. The affected boundary is the local password-auth bootstrap path and its database ownership invariant.
Acceptance criteria
ensureLegacySharedUser() must only rotate credentials for a row that was created/owned by the legacy shared-account bootstrap path.
- If the configured shared username collides with an existing normal user row, the helper must fail closed and not change that user's password hash.
- Login behavior must continue to work for a newly bootstrapped legacy shared user and for safe rotations of that bootstrapped user.
- Focused tests must cover collision refusal and allowed bootstrap-owned rotation.
Notify: Developer Agent
Summary
POST /api/auth/loginbootstraps the optional legacy shared account on every login request by callingensureLegacySharedUser(). IfDIFFAUDIT_SHARED_USERNAMEmatches an existing normal user's username,ensureLegacySharedUser()overwrites that user'spasswordHashwith the shared-password value instead of failing closed or only managing a previously bootstrapped shared account.Evidence
apps/web/src/app/api/auth/login/route.ts:21callsawait ensureLegacySharedUser()before checking the submitted login credentials.apps/web/src/lib/auth.ts:97-112looks up any row inusersbyusername = DIFFAUDIT_SHARED_USERNAME.apps/web/src/lib/auth.ts:134-145treats a password-hash mismatch as a rotation event and writes a new hash for the existing row.apps/web/src/lib/auth.test.ts:134-148currently asserts that changing the env password changes the password for the username, which masks the production behavior where the row may be a real user, not a shared fixture created by the bootstrapper.Impact
A deployer who enables the legacy shared login with a username that already exists can silently reset that user's password to the shared password. Because the bootstrap runs on every login request, the takeover can happen without an authenticated admin action or an explicit account-migration step.
Why this is real / non-duplicate
This is not covered by the open auth PRs for self-verification tokens, OAuth redirect targets, OAuth public origin handling, demo mode, 2FA enforcement, SSRF, or fake session cookies. The affected boundary is the local password-auth bootstrap path and its database ownership invariant.
Acceptance criteria
ensureLegacySharedUser()must only rotate credentials for a row that was created/owned by the legacy shared-account bootstrap path.Notify: Developer Agent