Skip to content

Commit b60771c

Browse files
authored
fix(auth): require sms code strings (#205)
1 parent ac88557 commit b60771c

2 files changed

Lines changed: 36 additions & 2 deletions

File tree

src/app/api/auth/verify-sms/route.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ export async function POST(request, { params } = {}) {
184184
}
185185
} else {
186186
// Original OTP verification flow
187-
if (!/^\d{6}$/.test(verificationCode)) {
187+
if (typeof verificationCode !== 'string' || !/^\d{6}$/.test(verificationCode)) {
188188
logger.error( 'Invalid verification code format', {
189189
codeLength: verificationCode?.length,
190-
codePattern: verificationCode?.replace(/\d/g, 'X')
190+
codePattern: typeof verificationCode === 'string' ? verificationCode.replace(/\d/g, 'X') : null
191191
});
192192
return NextResponse.json(
193193
{

src/app/api/auth/verify-sms/route.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
55
const mocks = vi.hoisted(() => ({
66
getUser: vi.fn(),
77
serverClient: vi.fn(),
8+
verifyOtp: vi.fn(),
89
createClient: vi.fn(() => ({}))
910
}));
1011

@@ -121,3 +122,36 @@ describe('verify-sms session phone binding', () => {
121122
expect(res.status).toBe(403);
122123
});
123124
});
125+
126+
describe('verify-sms OTP validation', () => {
127+
beforeEach(() => {
128+
vi.resetModules();
129+
vi.clearAllMocks();
130+
process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://example.supabase.co';
131+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = 'anon-key';
132+
process.env.SUPABASE_SERVICE_ROLE_KEY = 'service-role-key';
133+
mocks.serverClient.mockResolvedValue({
134+
auth: {
135+
getUser: mocks.getUser,
136+
verifyOtp: mocks.verifyOtp
137+
}
138+
});
139+
});
140+
141+
it('rejects numeric verification codes before OTP verification', async () => {
142+
const { POST } = await import('./route.js');
143+
const res = await POST(new Request('https://example.com/api/auth/verify-sms', {
144+
method: 'POST',
145+
headers: { 'content-type': 'application/json' },
146+
body: JSON.stringify({
147+
phoneNumber: '+15559999999',
148+
verificationCode: 123456
149+
})
150+
}));
151+
const body = await res.json();
152+
153+
expect(res.status).toBe(400);
154+
expect(body.error).toBe('Verification code must be 6 digits');
155+
expect(mocks.verifyOtp).not.toHaveBeenCalled();
156+
});
157+
});

0 commit comments

Comments
 (0)