diff --git a/src/app/api/auth/verify-sms/route.js b/src/app/api/auth/verify-sms/route.js index abe732b..3997a4e 100644 --- a/src/app/api/auth/verify-sms/route.js +++ b/src/app/api/auth/verify-sms/route.js @@ -180,10 +180,10 @@ export async function POST(request, { params } = {}) { } } else { // Original OTP verification flow - if (!/^\d{6}$/.test(verificationCode)) { + if (typeof verificationCode !== 'string' || !/^\d{6}$/.test(verificationCode)) { logger.error( 'Invalid verification code format', { codeLength: verificationCode?.length, - codePattern: verificationCode?.replace(/\d/g, 'X') + codePattern: typeof verificationCode === 'string' ? verificationCode.replace(/\d/g, 'X') : null }); return NextResponse.json( { diff --git a/src/app/api/auth/verify-sms/route.test.js b/src/app/api/auth/verify-sms/route.test.js index 50c3cd2..50226f0 100644 --- a/src/app/api/auth/verify-sms/route.test.js +++ b/src/app/api/auth/verify-sms/route.test.js @@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ getUser: vi.fn(), serverClient: vi.fn(), + verifyOtp: vi.fn(), createClient: vi.fn(() => ({})) })); @@ -106,3 +107,36 @@ describe('verify-sms session phone binding', () => { expect(res.status).toBe(403); }); }); + +describe('verify-sms OTP validation', () => { + beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://example.supabase.co'; + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = 'anon-key'; + process.env.SUPABASE_SERVICE_ROLE_KEY = 'service-role-key'; + mocks.serverClient.mockResolvedValue({ + auth: { + getUser: mocks.getUser, + verifyOtp: mocks.verifyOtp + } + }); + }); + + it('rejects numeric verification codes before OTP verification', async () => { + const { POST } = await import('./route.js'); + const res = await POST(new Request('https://example.com/api/auth/verify-sms', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + phoneNumber: '+15559999999', + verificationCode: 123456 + }) + })); + const body = await res.json(); + + expect(res.status).toBe(400); + expect(body.error).toBe('Verification code must be 6 digits'); + expect(mocks.verifyOtp).not.toHaveBeenCalled(); + }); +});