From 51b040a1ccfc6f1a6c9fddae5d7eec2bf70d81a6 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 11 Jul 2026 11:27:02 -0700 Subject: [PATCH] fix(chat-otp): re-check authType before minting deployment auth cookie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PUT verify no longer trusts a stale authType at cookie-mint time — it now re-checks the chat is still email-auth before issuing the cookie, matching the existing POST guard and the public-file OTP route. --- .../api/chat/[identifier]/otp/route.test.ts | 37 +++++++++++++++++++ .../app/api/chat/[identifier]/otp/route.ts | 4 ++ 2 files changed, 41 insertions(+) diff --git a/apps/sim/app/api/chat/[identifier]/otp/route.test.ts b/apps/sim/app/api/chat/[identifier]/otp/route.test.ts index a860df8eb28..6d32eb61f6b 100644 --- a/apps/sim/app/api/chat/[identifier]/otp/route.test.ts +++ b/apps/sim/app/api/chat/[identifier]/otp/route.test.ts @@ -529,6 +529,43 @@ describe('Chat OTP API Route', () => { }) }) + describe('PUT - Verify OTP (authType re-check)', () => { + beforeEach(() => { + mockGetStorageMethod.mockReturnValue('redis') + mockRedisGet.mockResolvedValue(`${mockOTP}:0`) + }) + + it('rejects verification when the chat has switched away from email auth', async () => { + mockDbSelect.mockImplementationOnce(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'password', + password: 'encrypted-password', + }, + ]), + }), + }), + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockCreateErrorResponse).toHaveBeenCalledWith( + 'This chat does not use email authentication', + 400 + ) + expect(mockRedisGet).not.toHaveBeenCalled() + expect(mockSetChatAuthCookie).not.toHaveBeenCalled() + }) + }) + describe('PUT - Verify OTP (Database path)', () => { beforeEach(() => { mockGetStorageMethod.mockReturnValue('database') diff --git a/apps/sim/app/api/chat/[identifier]/otp/route.ts b/apps/sim/app/api/chat/[identifier]/otp/route.ts index aeb1b69f450..248fcfa84ab 100644 --- a/apps/sim/app/api/chat/[identifier]/otp/route.ts +++ b/apps/sim/app/api/chat/[identifier]/otp/route.ts @@ -174,6 +174,10 @@ export const PUT = withRouteHandler( const deployment = deploymentResult[0] + if (deployment.authType !== 'email') { + return createErrorResponse('This chat does not use email authentication', 400) + } + const storedValue = await getOTP('chat', deployment.id, email) if (!storedValue) { return createErrorResponse('No verification code found, request a new one', 400)