Harden the re-auth step on the irreversible DELETE β close the password-oracle hole and give OAuth-only accounts a real path.
Scope
- Password path: the re-entry check on
DELETE /api/users/me/data runs through the SAME failure accounting as sign-in β bad attempt increments failedLoginAttempts / triggers lockUntil β plus the strict limiters.auth rate limiter on the route. (Without both, a stolen session cookie turns the endpoint into an unthrottled password-guessing oracle.)
- OAuth-only path:
POST /api/users/me/data/confirm issues a single-use, ~15-minute confirmation token, stored hashed on the user doc (same pattern as resetPasswordToken), delivered to the verified account email; DELETE accepts { confirmToken } as the re-auth proof. Token invalidated on use and on expiry; issuing route rate-limited.
DataErasureConfirm Zod schema covers both proofs (password XOR confirmToken).
GET/POST export routes get a moderate limiter (fan-out endpoints).
DoD
Depends on: export+erasure controller.
Created via /dev:issue Β· contract v2 2026-07-03
Harden the re-auth step on the irreversible DELETE β close the password-oracle hole and give OAuth-only accounts a real path.
Scope
DELETE /api/users/me/dataruns through the SAME failure accounting as sign-in β bad attempt incrementsfailedLoginAttempts/ triggerslockUntilβ plus the strictlimiters.authrate limiter on the route. (Without both, a stolen session cookie turns the endpoint into an unthrottled password-guessing oracle.)POST /api/users/me/data/confirmissues a single-use, ~15-minute confirmation token, stored hashed on the user doc (same pattern asresetPasswordToken), delivered to the verified account email;DELETEaccepts{ confirmToken }as the re-auth proof. Token invalidated on use and on expiry; issuing route rate-limited.DataErasureConfirmZod schema covers both proofs (passwordXORconfirmToken).GET/POSTexport routes get a moderate limiter (fan-out endpoints).DoD
/verifygreen.Depends on: export+erasure controller.
Created via /dev:issue Β· contract v2 2026-07-03