fix(auth): rate-limit OTP verification to block brute force (#70) - #73
Merged
MistryVishwa merged 1 commit intoJul 6, 2026
Merged
Conversation
…shwa#70) The password-reset OTP verification endpoint had no attempt limit, so an attacker could guess against the ~900k possible 6-digit codes within the 10-minute validity window and take over accounts. Track failed guesses per issued code and invalidate it once the budget is spent: - add failed_attempts column to password_reset_otps (new migration + base table migration for fresh installs) - increment the counter on each wrong guess and surface remaining attempts - after 5 incorrect guesses, delete the code and respond 429 so a fresh reset request is required Correct codes within the limit continue to verify normally.
|
@Rudra-clrscr is attempting to deploy a commit to the vishwamistrylearning-1037's projects Team on Vercel. A member of the Team first needs to authorize it. |
MistryVishwa
approved these changes
Jul 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #70
Problem
The password-reset OTP verification endpoint (
app/api/auth/verify-otp/route.ts)had no attempt limit, lockout, or delay. Although codes are 6 digits with a
10-minute expiry, an attacker could repeatedly guess against the ~900,000
possible codes within the validity window and take over an account.
Fix
Track failed guesses per issued code and invalidate the code once the budget
is spent (the approach proposed in the issue):
failed_attemptscolumn onpassword_reset_otpssupabase/migration-otp-failed-attempts.sql— idempotentADD COLUMN IF NOT EXISTSfor existing deploymentssupabase/migration-otp-table.sql— column added to theCREATE TABLEso fresh installs get it tooverify-otplogicBecause
forgot-passworddeletes and re-inserts a fresh OTP (counter back to 0),requesting a new code restores the attempt budget.
Acceptance criteria
Deployment note
The migration must be applied to the Supabase database before/with deploy,
otherwise the
failed_attemptsread/update will error.Known limitation
The counter is a read-modify-write and is not atomic, so tightly concurrent
requests could slip a few extra guesses past the threshold. The count is still
bounded to a tiny number — nowhere near brute-force range. Happy to follow up
with an atomic Postgres RPC increment if preferred.
Testing
tsc --noEmit: clean on the changed route (no new type errors)..env/Supabase, and it requires the migration appliedto the DB. Suggested manual check once deployed — request a code, submit 5
wrong guesses, confirm the 5th returns
429and the code no longer works.