Skip to content

fix(auth): bind session signup to the JWT-verified phone number#132

Merged
ralyodio merged 1 commit into
profullstack:masterfrom
AliaksandrNazaruk:fix/verify-sms-phone-binding
Jul 2, 2026
Merged

fix(auth): bind session signup to the JWT-verified phone number#132
ralyodio merged 1 commit into
profullstack:masterfrom
AliaksandrNazaruk:fix/verify-sms-phone-binding

Conversation

@AliaksandrNazaruk

Copy link
Copy Markdown
Contributor

Problem

The session-based branch of POST /api/auth/verify-sms (useSession + auth header) validates the JWT and obtains the verified user, but never compares user.phone to the request-body phoneNumber. Unlike the OTP branch (which binds the phone via verifyOtp), it then persists the client-supplied phoneNumber verbatim as the new user's phone_number.

So a caller holding any valid Supabase session (their own verified number, or an anonymous session) can complete registration claiming a phone number they never verified — phone spoofing / pre-registration of a target number (see #131).

Fix

Right after getUser succeeds, bind the requested phone to the JWT-verified phone (compared as bare digits, so a leading + difference doesn't cause false rejects) and reject anonymous/phoneless sessions:

const verifiedPhone = (user.phone || '').replace(/\D/g, '');
const requestedPhone = phoneNumber.replace(/\D/g, '');
if (!verifiedPhone || verifiedPhone !== requestedPhone) {
  return NextResponse.json(
    { error: 'Phone number does not match verified session', code: 'PHONE_SESSION_MISMATCH' },
    { status: 403 }
  );
}

The guard runs before any DB work, so a spoofed/anonymous request is rejected before a user row is created. The OTP branch is unchanged.

Tests

Added verify-sms/route.test.js (mirroring the existing upload-avatar/route.test.js mock style): a mismatched phone → 403 PHONE_SESSION_MISMATCH, and an anonymous/phoneless session → 403.

Fixes #131.

The session-based branch of POST /api/auth/verify-sms (useSession + auth
header) validates the JWT and obtains the verified user, but never compares
user.phone to the request-body phoneNumber. Unlike the OTP branch (which
binds the phone via verifyOtp), it then persists the client-supplied
phoneNumber verbatim as the new user's phone_number.

A caller holding any valid Supabase session (their own verified number, or
an anonymous session) could therefore complete registration claiming a phone
number they never verified (phone spoofing).

Enforce that the requested phone matches the JWT-verified phone (compared as
bare digits) and reject anonymous/phoneless sessions with 403. Add regression
tests for the mismatch and anonymous cases.
@ralyodio ralyodio merged commit e6f3664 into profullstack:master Jul 2, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

verify-sms: session signup trusts client phoneNumber without matching the JWT-verified phone (spoofing)

2 participants