-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
Description
Bug report
- I confirm this is a bug with Supabase, not with my own application.
- I confirm I have searched the Docs, GitHub Discussions, and Discord.
Bug Description
When using auth.updateUser({ phone }) to send a phone change OTP, the 6-digit code sent via SMS does not match the token hash stored in auth.one_time_tokens, causing verification to always fail with 403: Token has expired or is invalid.
- If helpful, broader context is that users create an account with email initially. I want to enable them to subsequently sign in with a phone number and receive OTP via SMS.
- Also worth mentioning, it did randomly work twice yesterday during testing (with intermittent failures in between). I haven't been able to get any additional successes since then.
Important
Update: Tested with Supabase's "Test Phone Numbers and OTPs" feature:
Added test phone number with pre-registered OTP code
Verification succeeded immediately
This confirms our verification implementation is correct
The bug is specifically in Supabase's OTP generation/sending pipeline when using real SMS
Steps to Reproduce
- Call
supabase.auth.updateUser({ phone: "[REDACTED]" }) - Receive SMS with code (e.g.,
[6-digit-code]) - Call
supabase.auth.verifyOtp({ phone: "[REDACTED]", token: "[6-digit-code]", type: "phone_change" }) - Result:
AuthApiError: Token has expired or is invalidwithcode: 'otp_expired'
Evidence
Token in database:
SELECT * FROM auth.one_time_tokens WHERE id = '[token-id]';- relates_to: "[REDACTED]" ✓
token_type: "phone_change_token"✓created_at: "2025-11-25 08:10:07"✓token_hash: "8379b9c0a87dab57cc7ade11499cec5535c288105ff53fcf454b30cf"
SMS sent by Supabase (via Twilio):
- Message SID:
[message-sid] - Body:
"[REDACTED]: Your code is [6-digit-code]" - Sent at:
2025-11-25 08:10:07 PST(matches token creation)
Code verification:
SELECT crypt('[6-digit-code]', token_hash) = token_hash AS code_matches;
-- Returns: falseServer logs:
- Verification attempted within 33ms of token creation (not expired)
- Phone format matches:
[REDACTED]=relates_to✓ - Type matches:
phone_change=phone_change_token✓ - Error:
AuthApiError: Token has expired or is invalidwithcode: 'otp_expired' - Request ID:
9a3fabc5932ceb2a-SJC
Expected Behavior
The OTP code sent in SMS should match the token hash stored in auth.one_time_tokens, allowing verification to succeed.
Actual Behavior
The SMS code does not match the stored token hash, causing verification to always fail immediately (not due to expiration).
Environment
- Supabase JS:
@supabase/[email protected] - Supabase SSR:
@supabase/[email protected] - SMS Provider: Twilio (configured in Supabase dashboard)
- SMS Template:
"[REDACTED]: Your code is {{.Code }}"
Additional Context
This happens consistently with every OTP attempt. The code is being used immediately (within seconds), so expiration is not the issue. The mismatch between the SMS code and token hash suggests a bug in Supabase's OTP generation/sending pipeline.