diff --git a/apps/sim/app/api/auth/oauth/token/route.test.ts b/apps/sim/app/api/auth/oauth/token/route.test.ts index c5032fc32..6fc18000f 100644 --- a/apps/sim/app/api/auth/oauth/token/route.test.ts +++ b/apps/sim/app/api/auth/oauth/token/route.test.ts @@ -10,6 +10,7 @@ describe('OAuth Token API Routes', () => { const mockGetUserId = vi.fn() const mockGetCredential = vi.fn() const mockRefreshTokenIfNeeded = vi.fn() + const mockGetOAuthToken = vi.fn() const mockAuthorizeCredentialUse = vi.fn() const mockCheckHybridAuth = vi.fn() @@ -29,6 +30,7 @@ describe('OAuth Token API Routes', () => { getUserId: mockGetUserId, getCredential: mockGetCredential, refreshTokenIfNeeded: mockRefreshTokenIfNeeded, + getOAuthToken: mockGetOAuthToken, })) vi.doMock('@sim/logger', () => ({ @@ -230,6 +232,140 @@ describe('OAuth Token API Routes', () => { expect(response.status).toBe(401) expect(data).toHaveProperty('error', 'Failed to refresh access token') }) + + describe('credentialAccountUserId + providerId path', () => { + it('should reject unauthenticated requests', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: false, + error: 'Authentication required', + }) + + const req = createMockRequest('POST', { + credentialAccountUserId: 'target-user-id', + providerId: 'google', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(401) + expect(data).toHaveProperty('error', 'User not authenticated') + expect(mockGetOAuthToken).not.toHaveBeenCalled() + }) + + it('should reject API key authentication', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: true, + authType: 'api_key', + userId: 'test-user-id', + }) + + const req = createMockRequest('POST', { + credentialAccountUserId: 'test-user-id', + providerId: 'google', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(401) + expect(data).toHaveProperty('error', 'User not authenticated') + expect(mockGetOAuthToken).not.toHaveBeenCalled() + }) + + it('should reject internal JWT authentication', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: true, + authType: 'internal_jwt', + userId: 'test-user-id', + }) + + const req = createMockRequest('POST', { + credentialAccountUserId: 'test-user-id', + providerId: 'google', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(401) + expect(data).toHaveProperty('error', 'User not authenticated') + expect(mockGetOAuthToken).not.toHaveBeenCalled() + }) + + it('should reject requests for other users credentials', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: true, + authType: 'session', + userId: 'attacker-user-id', + }) + + const req = createMockRequest('POST', { + credentialAccountUserId: 'victim-user-id', + providerId: 'google', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(403) + expect(data).toHaveProperty('error', 'Unauthorized') + expect(mockGetOAuthToken).not.toHaveBeenCalled() + }) + + it('should allow session-authenticated users to access their own credentials', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: true, + authType: 'session', + userId: 'test-user-id', + }) + mockGetOAuthToken.mockResolvedValueOnce('valid-access-token') + + const req = createMockRequest('POST', { + credentialAccountUserId: 'test-user-id', + providerId: 'google', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(200) + expect(data).toHaveProperty('accessToken', 'valid-access-token') + expect(mockGetOAuthToken).toHaveBeenCalledWith('test-user-id', 'google') + }) + + it('should return 404 when credential not found for user', async () => { + mockCheckHybridAuth.mockResolvedValueOnce({ + success: true, + authType: 'session', + userId: 'test-user-id', + }) + mockGetOAuthToken.mockResolvedValueOnce(null) + + const req = createMockRequest('POST', { + credentialAccountUserId: 'test-user-id', + providerId: 'nonexistent-provider', + }) + + const { POST } = await import('@/app/api/auth/oauth/token/route') + + const response = await POST(req) + const data = await response.json() + + expect(response.status).toBe(404) + expect(data.error).toContain('No credential found') + }) + }) }) /** diff --git a/apps/sim/app/api/auth/oauth/token/route.ts b/apps/sim/app/api/auth/oauth/token/route.ts index f5c8d7b61..7c7d1f463 100644 --- a/apps/sim/app/api/auth/oauth/token/route.ts +++ b/apps/sim/app/api/auth/oauth/token/route.ts @@ -71,6 +71,22 @@ export async function POST(request: NextRequest) { providerId, }) + const auth = await checkHybridAuth(request, { requireWorkflowId: false }) + if (!auth.success || auth.authType !== 'session' || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized request for credentialAccountUserId path`, { + success: auth.success, + authType: auth.authType, + }) + return NextResponse.json({ error: 'User not authenticated' }, { status: 401 }) + } + + if (auth.userId !== credentialAccountUserId) { + logger.warn( + `[${requestId}] User ${auth.userId} attempted to access credentials for ${credentialAccountUserId}` + ) + return NextResponse.json({ error: 'Unauthorized' }, { status: 403 }) + } + try { const accessToken = await getOAuthToken(credentialAccountUserId, providerId) if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/add-comment/route.ts b/apps/sim/app/api/tools/asana/add-comment/route.ts index b6ef38d94..2fbd49937 100644 --- a/apps/sim/app/api/tools/asana/add-comment/route.ts +++ b/apps/sim/app/api/tools/asana/add-comment/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaAddCommentAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, taskGid, text } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/create-task/route.ts b/apps/sim/app/api/tools/asana/create-task/route.ts index 41cd67329..7d82ff3e4 100644 --- a/apps/sim/app/api/tools/asana/create-task/route.ts +++ b/apps/sim/app/api/tools/asana/create-task/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaCreateTaskAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, workspace, name, notes, assignee, due_on } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/get-projects/route.ts b/apps/sim/app/api/tools/asana/get-projects/route.ts index c57fae722..10513a4c8 100644 --- a/apps/sim/app/api/tools/asana/get-projects/route.ts +++ b/apps/sim/app/api/tools/asana/get-projects/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaGetProjectsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, workspace } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/get-task/route.ts b/apps/sim/app/api/tools/asana/get-task/route.ts index d60902fec..1a2a9a81f 100644 --- a/apps/sim/app/api/tools/asana/get-task/route.ts +++ b/apps/sim/app/api/tools/asana/get-task/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaGetTaskAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, taskGid, workspace, project, limit } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/search-tasks/route.ts b/apps/sim/app/api/tools/asana/search-tasks/route.ts index d9b7e8288..a9446bd11 100644 --- a/apps/sim/app/api/tools/asana/search-tasks/route.ts +++ b/apps/sim/app/api/tools/asana/search-tasks/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaSearchTasksAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, workspace, text, assignee, projects, completed } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/asana/update-task/route.ts b/apps/sim/app/api/tools/asana/update-task/route.ts index 3bc242a29..8d990cf58 100644 --- a/apps/sim/app/api/tools/asana/update-task/route.ts +++ b/apps/sim/app/api/tools/asana/update-task/route.ts @@ -1,13 +1,19 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' export const dynamic = 'force-dynamic' const logger = createLogger('AsanaUpdateTaskAPI') -export async function PUT(request: Request) { +export async function PUT(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { accessToken, taskGid, name, notes, assignee, completed, due_on } = await request.json() if (!accessToken) { diff --git a/apps/sim/app/api/tools/confluence/attachment/route.ts b/apps/sim/app/api/tools/confluence/attachment/route.ts index 7b55dc719..f0265fe1f 100644 --- a/apps/sim/app/api/tools/confluence/attachment/route.ts +++ b/apps/sim/app/api/tools/confluence/attachment/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceAttachmentAPI') export const dynamic = 'force-dynamic' // Delete an attachment -export async function DELETE(request: Request) { +export async function DELETE(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, cloudId: providedCloudId, attachmentId } = await request.json() if (!domain) { diff --git a/apps/sim/app/api/tools/confluence/attachments/route.ts b/apps/sim/app/api/tools/confluence/attachments/route.ts index 6154f3e08..cba790a10 100644 --- a/apps/sim/app/api/tools/confluence/attachments/route.ts +++ b/apps/sim/app/api/tools/confluence/attachments/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceAttachmentsAPI') export const dynamic = 'force-dynamic' // List attachments on a page -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { searchParams } = new URL(request.url) const domain = searchParams.get('domain') const accessToken = searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/confluence/comment/route.ts b/apps/sim/app/api/tools/confluence/comment/route.ts index c94ac85e9..3486f975a 100644 --- a/apps/sim/app/api/tools/confluence/comment/route.ts +++ b/apps/sim/app/api/tools/confluence/comment/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -46,8 +47,13 @@ const deleteCommentSchema = z ) // Update a comment -export async function PUT(request: Request) { +export async function PUT(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = putCommentSchema.safeParse(body) @@ -128,8 +134,13 @@ export async function PUT(request: Request) { } // Delete a comment -export async function DELETE(request: Request) { +export async function DELETE(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = deleteCommentSchema.safeParse(body) diff --git a/apps/sim/app/api/tools/confluence/comments/route.ts b/apps/sim/app/api/tools/confluence/comments/route.ts index eac22a2b2..1c50aa0c0 100644 --- a/apps/sim/app/api/tools/confluence/comments/route.ts +++ b/apps/sim/app/api/tools/confluence/comments/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceCommentsAPI') export const dynamic = 'force-dynamic' // Create a comment -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, cloudId: providedCloudId, pageId, comment } = await request.json() if (!domain) { @@ -86,8 +92,13 @@ export async function POST(request: Request) { } // List comments on a page -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { searchParams } = new URL(request.url) const domain = searchParams.get('domain') const accessToken = searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/confluence/create-page/route.ts b/apps/sim/app/api/tools/confluence/create-page/route.ts index 218b4ff61..c11dbbc7f 100644 --- a/apps/sim/app/api/tools/confluence/create-page/route.ts +++ b/apps/sim/app/api/tools/confluence/create-page/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -7,8 +8,13 @@ const logger = createLogger('ConfluenceCreatePageAPI') export const dynamic = 'force-dynamic' -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, diff --git a/apps/sim/app/api/tools/confluence/labels/route.ts b/apps/sim/app/api/tools/confluence/labels/route.ts index 557c542d1..d008c3d55 100644 --- a/apps/sim/app/api/tools/confluence/labels/route.ts +++ b/apps/sim/app/api/tools/confluence/labels/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceLabelsAPI') export const dynamic = 'force-dynamic' // Add a label to a page -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, @@ -87,8 +93,13 @@ export async function POST(request: Request) { } // List labels on a page -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { searchParams } = new URL(request.url) const domain = searchParams.get('domain') const accessToken = searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/confluence/page/route.ts b/apps/sim/app/api/tools/confluence/page/route.ts index 685eefffd..9cc83914e 100644 --- a/apps/sim/app/api/tools/confluence/page/route.ts +++ b/apps/sim/app/api/tools/confluence/page/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -73,8 +74,13 @@ const deletePageSchema = z } ) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = postPageSchema.safeParse(body) @@ -144,8 +150,13 @@ export async function POST(request: Request) { } } -export async function PUT(request: Request) { +export async function PUT(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = putPageSchema.safeParse(body) @@ -248,8 +259,13 @@ export async function PUT(request: Request) { } } -export async function DELETE(request: Request) { +export async function DELETE(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = deletePageSchema.safeParse(body) diff --git a/apps/sim/app/api/tools/confluence/pages/route.ts b/apps/sim/app/api/tools/confluence/pages/route.ts index e83198ffe..e225bf34e 100644 --- a/apps/sim/app/api/tools/confluence/pages/route.ts +++ b/apps/sim/app/api/tools/confluence/pages/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluencePagesAPI') export const dynamic = 'force-dynamic' // List pages or search pages -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, diff --git a/apps/sim/app/api/tools/confluence/search/route.ts b/apps/sim/app/api/tools/confluence/search/route.ts index 3782aace3..adeedb392 100644 --- a/apps/sim/app/api/tools/confluence/search/route.ts +++ b/apps/sim/app/api/tools/confluence/search/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -7,8 +8,13 @@ export const dynamic = 'force-dynamic' const logger = createLogger('Confluence Search') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, diff --git a/apps/sim/app/api/tools/confluence/space/route.ts b/apps/sim/app/api/tools/confluence/space/route.ts index bda98ce6b..a8e0186f7 100644 --- a/apps/sim/app/api/tools/confluence/space/route.ts +++ b/apps/sim/app/api/tools/confluence/space/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceSpaceAPI') export const dynamic = 'force-dynamic' // Get a specific space -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { searchParams } = new URL(request.url) const domain = searchParams.get('domain') const accessToken = searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/confluence/spaces/route.ts b/apps/sim/app/api/tools/confluence/spaces/route.ts index 6d66aae09..53daafe0e 100644 --- a/apps/sim/app/api/tools/confluence/spaces/route.ts +++ b/apps/sim/app/api/tools/confluence/spaces/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId } from '@/lib/core/security/input-validation' import { getConfluenceCloudId } from '@/tools/confluence/utils' @@ -8,8 +9,13 @@ const logger = createLogger('ConfluenceSpacesAPI') export const dynamic = 'force-dynamic' // List all spaces -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { searchParams } = new URL(request.url) const domain = searchParams.get('domain') const accessToken = searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts index 7487b6ee5..f6be92f3f 100644 --- a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts +++ b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' @@ -11,6 +12,11 @@ export const dynamic = 'force-dynamic' export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const { domain, accessToken, cloudId: providedCloudId, pageId, file, fileName, comment } = body diff --git a/apps/sim/app/api/tools/discord/channels/route.ts b/apps/sim/app/api/tools/discord/channels/route.ts index 23b33dd76..f20735af7 100644 --- a/apps/sim/app/api/tools/discord/channels/route.ts +++ b/apps/sim/app/api/tools/discord/channels/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateNumericId } from '@/lib/core/security/input-validation' interface DiscordChannel { @@ -13,7 +14,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('DiscordChannelsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { botToken, serverId, channelId } = await request.json() diff --git a/apps/sim/app/api/tools/discord/servers/route.ts b/apps/sim/app/api/tools/discord/servers/route.ts index c589ad4b2..490bc4d0a 100644 --- a/apps/sim/app/api/tools/discord/servers/route.ts +++ b/apps/sim/app/api/tools/discord/servers/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateNumericId } from '@/lib/core/security/input-validation' interface DiscordServer { @@ -12,7 +13,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('DiscordServersAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { botToken, serverId } = await request.json() diff --git a/apps/sim/app/api/tools/drive/file/route.ts b/apps/sim/app/api/tools/drive/file/route.ts index 931253b04..1acd0c292 100644 --- a/apps/sim/app/api/tools/drive/file/route.ts +++ b/apps/sim/app/api/tools/drive/file/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' @@ -15,6 +16,11 @@ export async function GET(request: NextRequest) { const requestId = generateRequestId() logger.info(`[${requestId}] Google Drive file request received`) + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { searchParams } = new URL(request.url) const credentialId = searchParams.get('credentialId') diff --git a/apps/sim/app/api/tools/drive/files/route.ts b/apps/sim/app/api/tools/drive/files/route.ts index 5584fe392..89b2e4936 100644 --- a/apps/sim/app/api/tools/drive/files/route.ts +++ b/apps/sim/app/api/tools/drive/files/route.ts @@ -1,7 +1,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' -import { getSession } from '@/lib/auth' import { authorizeCredentialUse } from '@/lib/auth/credential-access' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' @@ -73,14 +73,12 @@ export async function GET(request: NextRequest) { const requestId = generateRequestId() logger.info(`[${requestId}] Google Drive files request received`) + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { - const session = await getSession() - - if (!session?.user?.id) { - logger.warn(`[${requestId}] Unauthenticated request rejected`) - return NextResponse.json({ error: 'User not authenticated' }, { status: 401 }) - } - const { searchParams } = new URL(request.url) const credentialId = searchParams.get('credentialId') const mimeType = searchParams.get('mimeType') diff --git a/apps/sim/app/api/tools/dynamodb/delete/route.ts b/apps/sim/app/api/tools/dynamodb/delete/route.ts index 89dc3ee3e..0002787f3 100644 --- a/apps/sim/app/api/tools/dynamodb/delete/route.ts +++ b/apps/sim/app/api/tools/dynamodb/delete/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, deleteItem } from '@/app/api/tools/dynamodb/utils' const DeleteSchema = z.object({ @@ -13,8 +14,13 @@ const DeleteSchema = z.object({ conditionExpression: z.string().optional(), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = DeleteSchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/get/route.ts b/apps/sim/app/api/tools/dynamodb/get/route.ts index 82564db6e..851f1cb07 100644 --- a/apps/sim/app/api/tools/dynamodb/get/route.ts +++ b/apps/sim/app/api/tools/dynamodb/get/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, getItem } from '@/app/api/tools/dynamodb/utils' const GetSchema = z.object({ @@ -19,8 +20,13 @@ const GetSchema = z.object({ }), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = GetSchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/introspect/route.ts b/apps/sim/app/api/tools/dynamodb/introspect/route.ts index 55db21aea..6e55bde87 100644 --- a/apps/sim/app/api/tools/dynamodb/introspect/route.ts +++ b/apps/sim/app/api/tools/dynamodb/introspect/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRawDynamoDBClient, describeTable, listTables } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBIntrospectAPI') @@ -17,6 +18,11 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const params = IntrospectSchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/put/route.ts b/apps/sim/app/api/tools/dynamodb/put/route.ts index 3516db91d..cb001c587 100644 --- a/apps/sim/app/api/tools/dynamodb/put/route.ts +++ b/apps/sim/app/api/tools/dynamodb/put/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, putItem } from '@/app/api/tools/dynamodb/utils' const PutSchema = z.object({ @@ -12,8 +13,13 @@ const PutSchema = z.object({ }), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = PutSchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/query/route.ts b/apps/sim/app/api/tools/dynamodb/query/route.ts index 16a0481d9..06945c100 100644 --- a/apps/sim/app/api/tools/dynamodb/query/route.ts +++ b/apps/sim/app/api/tools/dynamodb/query/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, queryItems } from '@/app/api/tools/dynamodb/utils' const QuerySchema = z.object({ @@ -15,8 +16,13 @@ const QuerySchema = z.object({ limit: z.number().positive().optional(), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = QuerySchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/scan/route.ts b/apps/sim/app/api/tools/dynamodb/scan/route.ts index 5b3b4ab93..c083faad5 100644 --- a/apps/sim/app/api/tools/dynamodb/scan/route.ts +++ b/apps/sim/app/api/tools/dynamodb/scan/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, scanItems } from '@/app/api/tools/dynamodb/utils' const ScanSchema = z.object({ @@ -14,8 +15,13 @@ const ScanSchema = z.object({ limit: z.number().positive().optional(), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = ScanSchema.parse(body) diff --git a/apps/sim/app/api/tools/dynamodb/update/route.ts b/apps/sim/app/api/tools/dynamodb/update/route.ts index 70c3fb728..07abcc256 100644 --- a/apps/sim/app/api/tools/dynamodb/update/route.ts +++ b/apps/sim/app/api/tools/dynamodb/update/route.ts @@ -1,5 +1,6 @@ -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createDynamoDBClient, updateItem } from '@/app/api/tools/dynamodb/utils' const UpdateSchema = z.object({ @@ -16,8 +17,13 @@ const UpdateSchema = z.object({ conditionExpression: z.string().optional(), }) -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validatedData = UpdateSchema.parse(body) diff --git a/apps/sim/app/api/tools/google_sheets/sheets/route.ts b/apps/sim/app/api/tools/google_sheets/sheets/route.ts index 6a2977e01..6eb9c9fc8 100644 --- a/apps/sim/app/api/tools/google_sheets/sheets/route.ts +++ b/apps/sim/app/api/tools/google_sheets/sheets/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' @@ -29,6 +30,11 @@ export async function GET(request: NextRequest) { const requestId = generateRequestId() logger.info(`[${requestId}] Google Sheets sheets request received`) + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { searchParams } = new URL(request.url) const credentialId = searchParams.get('credentialId') diff --git a/apps/sim/app/api/tools/jira/issue/route.ts b/apps/sim/app/api/tools/jira/issue/route.ts index d77d98ffe..3c837de04 100644 --- a/apps/sim/app/api/tools/jira/issue/route.ts +++ b/apps/sim/app/api/tools/jira/issue/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId } from '@/tools/jira/utils' @@ -7,8 +8,13 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JiraIssueAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, issueId, cloudId: providedCloudId } = await request.json() if (!domain) { logger.error('Missing domain in request') diff --git a/apps/sim/app/api/tools/jira/issues/route.ts b/apps/sim/app/api/tools/jira/issues/route.ts index cc5f7a4cc..dfebb1d9e 100644 --- a/apps/sim/app/api/tools/jira/issues/route.ts +++ b/apps/sim/app/api/tools/jira/issues/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId } from '@/tools/jira/utils' @@ -26,8 +27,13 @@ const validateRequiredParams = (domain: string | null, accessToken: string | nul return null } -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, issueKeys = [], cloudId: providedCloudId } = await request.json() const validationError = validateRequiredParams(domain || null, accessToken || null) @@ -101,8 +107,13 @@ export async function POST(request: Request) { } } -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const url = new URL(request.url) const domain = url.searchParams.get('domain')?.trim() const accessToken = url.searchParams.get('accessToken') diff --git a/apps/sim/app/api/tools/jira/projects/route.ts b/apps/sim/app/api/tools/jira/projects/route.ts index e85e4cc94..994320cb4 100644 --- a/apps/sim/app/api/tools/jira/projects/route.ts +++ b/apps/sim/app/api/tools/jira/projects/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId } from '@/tools/jira/utils' @@ -7,8 +8,13 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JiraProjectsAPI') -export async function GET(request: Request) { +export async function GET(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const url = new URL(request.url) const domain = url.searchParams.get('domain')?.trim() const accessToken = url.searchParams.get('accessToken') @@ -98,8 +104,13 @@ export async function GET(request: Request) { } } -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, projectId, cloudId: providedCloudId } = await request.json() if (!domain) { diff --git a/apps/sim/app/api/tools/jira/update/route.ts b/apps/sim/app/api/tools/jira/update/route.ts index b1e67f953..d4ad86af6 100644 --- a/apps/sim/app/api/tools/jira/update/route.ts +++ b/apps/sim/app/api/tools/jira/update/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId } from '@/tools/jira/utils' @@ -21,8 +22,13 @@ const jiraUpdateSchema = z.object({ cloudId: z.string().optional(), }) -export async function PUT(request: Request) { +export async function PUT(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const body = await request.json() const validation = jiraUpdateSchema.safeParse(body) diff --git a/apps/sim/app/api/tools/jira/write/route.ts b/apps/sim/app/api/tools/jira/write/route.ts index c80434ceb..61ec34e01 100644 --- a/apps/sim/app/api/tools/jira/write/route.ts +++ b/apps/sim/app/api/tools/jira/write/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId } from '@/tools/jira/utils' @@ -7,8 +8,13 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JiraWriteAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { try { + const auth = await checkSessionOrInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + const { domain, accessToken, diff --git a/apps/sim/app/api/tools/jsm/approvals/route.ts b/apps/sim/app/api/tools/jsm/approvals/route.ts index 896e5bab9..08e51725a 100644 --- a/apps/sim/app/api/tools/jsm/approvals/route.ts +++ b/apps/sim/app/api/tools/jsm/approvals/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateEnum, @@ -15,7 +16,12 @@ const logger = createLogger('JsmApprovalsAPI') const VALID_ACTIONS = ['get', 'answer'] as const const VALID_DECISIONS = ['approve', 'decline'] as const -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/comment/route.ts b/apps/sim/app/api/tools/jsm/comment/route.ts index b93b90ecc..ab2e3b1e5 100644 --- a/apps/sim/app/api/tools/jsm/comment/route.ts +++ b/apps/sim/app/api/tools/jsm/comment/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmCommentAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { domain, diff --git a/apps/sim/app/api/tools/jsm/comments/route.ts b/apps/sim/app/api/tools/jsm/comments/route.ts index 429857f7c..a2ca2c47d 100644 --- a/apps/sim/app/api/tools/jsm/comments/route.ts +++ b/apps/sim/app/api/tools/jsm/comments/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmCommentsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/customers/route.ts b/apps/sim/app/api/tools/jsm/customers/route.ts index de735337d..f05d39187 100644 --- a/apps/sim/app/api/tools/jsm/customers/route.ts +++ b/apps/sim/app/api/tools/jsm/customers/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmCustomersAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/organization/route.ts b/apps/sim/app/api/tools/jsm/organization/route.ts index 168a76be8..c0fc93203 100644 --- a/apps/sim/app/api/tools/jsm/organization/route.ts +++ b/apps/sim/app/api/tools/jsm/organization/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateEnum, @@ -13,7 +14,12 @@ const logger = createLogger('JsmOrganizationAPI') const VALID_ACTIONS = ['create', 'add_to_service_desk'] as const -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/organizations/route.ts b/apps/sim/app/api/tools/jsm/organizations/route.ts index b6b0f0400..79971e27d 100644 --- a/apps/sim/app/api/tools/jsm/organizations/route.ts +++ b/apps/sim/app/api/tools/jsm/organizations/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmOrganizationsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { domain, accessToken, cloudId: cloudIdParam, serviceDeskId, start, limit } = body diff --git a/apps/sim/app/api/tools/jsm/participants/route.ts b/apps/sim/app/api/tools/jsm/participants/route.ts index 649edd29b..93a0293d1 100644 --- a/apps/sim/app/api/tools/jsm/participants/route.ts +++ b/apps/sim/app/api/tools/jsm/participants/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateEnum, validateJiraCloudId, @@ -13,7 +14,12 @@ const logger = createLogger('JsmParticipantsAPI') const VALID_ACTIONS = ['get', 'add'] as const -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/queues/route.ts b/apps/sim/app/api/tools/jsm/queues/route.ts index 691de5d9c..2921008ef 100644 --- a/apps/sim/app/api/tools/jsm/queues/route.ts +++ b/apps/sim/app/api/tools/jsm/queues/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmQueuesAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/request/route.ts b/apps/sim/app/api/tools/jsm/request/route.ts index 86f18b5be..92e5e9f4c 100644 --- a/apps/sim/app/api/tools/jsm/request/route.ts +++ b/apps/sim/app/api/tools/jsm/request/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId, @@ -11,7 +12,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmRequestAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/requests/route.ts b/apps/sim/app/api/tools/jsm/requests/route.ts index e12ea98fe..f2f0dc0e7 100644 --- a/apps/sim/app/api/tools/jsm/requests/route.ts +++ b/apps/sim/app/api/tools/jsm/requests/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmRequestsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { diff --git a/apps/sim/app/api/tools/jsm/requesttypes/route.ts b/apps/sim/app/api/tools/jsm/requesttypes/route.ts index d7e9bdb28..8591f116b 100644 --- a/apps/sim/app/api/tools/jsm/requesttypes/route.ts +++ b/apps/sim/app/api/tools/jsm/requesttypes/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmRequestTypesAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { domain, accessToken, cloudId: cloudIdParam, serviceDeskId, start, limit } = body diff --git a/apps/sim/app/api/tools/jsm/servicedesks/route.ts b/apps/sim/app/api/tools/jsm/servicedesks/route.ts index bab91d5bb..607508a61 100644 --- a/apps/sim/app/api/tools/jsm/servicedesks/route.ts +++ b/apps/sim/app/api/tools/jsm/servicedesks/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmServiceDesksAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { domain, accessToken, cloudId: cloudIdParam, start, limit } = body diff --git a/apps/sim/app/api/tools/jsm/sla/route.ts b/apps/sim/app/api/tools/jsm/sla/route.ts index ac826afcf..dc414ac83 100644 --- a/apps/sim/app/api/tools/jsm/sla/route.ts +++ b/apps/sim/app/api/tools/jsm/sla/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmSlaAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { domain, accessToken, cloudId: cloudIdParam, issueIdOrKey, start, limit } = body diff --git a/apps/sim/app/api/tools/jsm/transition/route.ts b/apps/sim/app/api/tools/jsm/transition/route.ts index 5d17a7ca4..45a9e3a5c 100644 --- a/apps/sim/app/api/tools/jsm/transition/route.ts +++ b/apps/sim/app/api/tools/jsm/transition/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId, @@ -11,7 +12,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmTransitionAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const { domain, diff --git a/apps/sim/app/api/tools/jsm/transitions/route.ts b/apps/sim/app/api/tools/jsm/transitions/route.ts index a9242177b..5d5f2e260 100644 --- a/apps/sim/app/api/tools/jsm/transitions/route.ts +++ b/apps/sim/app/api/tools/jsm/transitions/route.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' -import { NextResponse } from 'next/server' +import { type NextRequest, NextResponse } from 'next/server' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils' @@ -7,7 +8,12 @@ export const dynamic = 'force-dynamic' const logger = createLogger('JsmTransitionsAPI') -export async function POST(request: Request) { +export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const { domain, accessToken, cloudId: cloudIdParam, issueIdOrKey } = body diff --git a/apps/sim/app/api/tools/mongodb/delete/route.ts b/apps/sim/app/api/tools/mongodb/delete/route.ts index b63467725..95dcf328c 100644 --- a/apps/sim/app/api/tools/mongodb/delete/route.ts +++ b/apps/sim/app/api/tools/mongodb/delete/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBDeleteAPI') @@ -40,6 +41,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB delete attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = DeleteSchema.parse(body) diff --git a/apps/sim/app/api/tools/mongodb/execute/route.ts b/apps/sim/app/api/tools/mongodb/execute/route.ts index afae95975..666d4a450 100644 --- a/apps/sim/app/api/tools/mongodb/execute/route.ts +++ b/apps/sim/app/api/tools/mongodb/execute/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, sanitizeCollectionName, validatePipeline } from '../utils' const logger = createLogger('MongoDBExecuteAPI') @@ -32,6 +33,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB execute attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = ExecuteSchema.parse(body) diff --git a/apps/sim/app/api/tools/mongodb/insert/route.ts b/apps/sim/app/api/tools/mongodb/insert/route.ts index fd350ef3e..f7feafd61 100644 --- a/apps/sim/app/api/tools/mongodb/insert/route.ts +++ b/apps/sim/app/api/tools/mongodb/insert/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, sanitizeCollectionName } from '../utils' const logger = createLogger('MongoDBInsertAPI') @@ -37,6 +38,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB insert attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = InsertSchema.parse(body) diff --git a/apps/sim/app/api/tools/mongodb/introspect/route.ts b/apps/sim/app/api/tools/mongodb/introspect/route.ts index a095c1386..67f281553 100644 --- a/apps/sim/app/api/tools/mongodb/introspect/route.ts +++ b/apps/sim/app/api/tools/mongodb/introspect/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, executeIntrospect } from '../utils' const logger = createLogger('MongoDBIntrospectAPI') @@ -20,6 +21,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB introspect attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = IntrospectSchema.parse(body) diff --git a/apps/sim/app/api/tools/mongodb/query/route.ts b/apps/sim/app/api/tools/mongodb/query/route.ts index ae8276dea..06533e3a8 100644 --- a/apps/sim/app/api/tools/mongodb/query/route.ts +++ b/apps/sim/app/api/tools/mongodb/query/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBQueryAPI') @@ -49,6 +50,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB query attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = QuerySchema.parse(body) diff --git a/apps/sim/app/api/tools/mongodb/update/route.ts b/apps/sim/app/api/tools/mongodb/update/route.ts index ac24d5539..e6c0f867f 100644 --- a/apps/sim/app/api/tools/mongodb/update/route.ts +++ b/apps/sim/app/api/tools/mongodb/update/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBUpdateAPI') @@ -59,6 +60,12 @@ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) let client = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized MongoDB update attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = UpdateSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/create/route.ts b/apps/sim/app/api/tools/neo4j/create/route.ts index 3fb66142a..c599b9c13 100644 --- a/apps/sim/app/api/tools/neo4j/create/route.ts +++ b/apps/sim/app/api/tools/neo4j/create/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { convertNeo4jTypesToJSON, createNeo4jDriver, @@ -26,6 +27,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j create attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = CreateSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/delete/route.ts b/apps/sim/app/api/tools/neo4j/delete/route.ts index e010fe8b6..7a4ed7b31 100644 --- a/apps/sim/app/api/tools/neo4j/delete/route.ts +++ b/apps/sim/app/api/tools/neo4j/delete/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createNeo4jDriver, validateCypherQuery } from '@/app/api/tools/neo4j/utils' const logger = createLogger('Neo4jDeleteAPI') @@ -23,6 +24,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j delete attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = DeleteSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/execute/route.ts b/apps/sim/app/api/tools/neo4j/execute/route.ts index 79d98975f..df4dc43b5 100644 --- a/apps/sim/app/api/tools/neo4j/execute/route.ts +++ b/apps/sim/app/api/tools/neo4j/execute/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { convertNeo4jTypesToJSON, createNeo4jDriver, @@ -26,6 +27,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j execute attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = ExecuteSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/introspect/route.ts b/apps/sim/app/api/tools/neo4j/introspect/route.ts index 0543afb02..f463e1532 100644 --- a/apps/sim/app/api/tools/neo4j/introspect/route.ts +++ b/apps/sim/app/api/tools/neo4j/introspect/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createNeo4jDriver } from '@/app/api/tools/neo4j/utils' import type { Neo4jNodeSchema, Neo4jRelationshipSchema } from '@/tools/neo4j/types' @@ -21,6 +22,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j introspect attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = IntrospectSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/merge/route.ts b/apps/sim/app/api/tools/neo4j/merge/route.ts index 28f00a7e0..839b98f38 100644 --- a/apps/sim/app/api/tools/neo4j/merge/route.ts +++ b/apps/sim/app/api/tools/neo4j/merge/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { convertNeo4jTypesToJSON, createNeo4jDriver, @@ -26,6 +27,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j merge attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = MergeSchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/query/route.ts b/apps/sim/app/api/tools/neo4j/query/route.ts index 84dd3cb51..1bb22ab98 100644 --- a/apps/sim/app/api/tools/neo4j/query/route.ts +++ b/apps/sim/app/api/tools/neo4j/query/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { convertNeo4jTypesToJSON, createNeo4jDriver, @@ -26,6 +27,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j query attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = QuerySchema.parse(body) diff --git a/apps/sim/app/api/tools/neo4j/update/route.ts b/apps/sim/app/api/tools/neo4j/update/route.ts index e5f2bfb76..828d6ef6f 100644 --- a/apps/sim/app/api/tools/neo4j/update/route.ts +++ b/apps/sim/app/api/tools/neo4j/update/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { convertNeo4jTypesToJSON, createNeo4jDriver, @@ -26,6 +27,12 @@ export async function POST(request: NextRequest) { let driver = null let session = null + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + logger.warn(`[${requestId}] Unauthorized Neo4j update attempt`) + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = UpdateSchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/delete/route.ts b/apps/sim/app/api/tools/rds/delete/route.ts index e30979666..8db9e2e1d 100644 --- a/apps/sim/app/api/tools/rds/delete/route.ts +++ b/apps/sim/app/api/tools/rds/delete/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeDelete } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSDeleteAPI') @@ -22,6 +23,11 @@ const DeleteSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = DeleteSchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/execute/route.ts b/apps/sim/app/api/tools/rds/execute/route.ts index 9510d4088..8c88edb0f 100644 --- a/apps/sim/app/api/tools/rds/execute/route.ts +++ b/apps/sim/app/api/tools/rds/execute/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeStatement } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSExecuteAPI') @@ -19,6 +20,11 @@ const ExecuteSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = ExecuteSchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/insert/route.ts b/apps/sim/app/api/tools/rds/insert/route.ts index 6f766d423..783d80821 100644 --- a/apps/sim/app/api/tools/rds/insert/route.ts +++ b/apps/sim/app/api/tools/rds/insert/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeInsert } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSInsertAPI') @@ -22,6 +23,11 @@ const InsertSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = InsertSchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/introspect/route.ts b/apps/sim/app/api/tools/rds/introspect/route.ts index 8eb46ed64..ea96f05b2 100644 --- a/apps/sim/app/api/tools/rds/introspect/route.ts +++ b/apps/sim/app/api/tools/rds/introspect/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeIntrospect, type RdsEngine } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSIntrospectAPI') @@ -20,6 +21,11 @@ const IntrospectSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = IntrospectSchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/query/route.ts b/apps/sim/app/api/tools/rds/query/route.ts index 81d972d47..6caf743c3 100644 --- a/apps/sim/app/api/tools/rds/query/route.ts +++ b/apps/sim/app/api/tools/rds/query/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeStatement, validateQuery } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSQueryAPI') @@ -19,6 +20,11 @@ const QuerySchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = QuerySchema.parse(body) diff --git a/apps/sim/app/api/tools/rds/update/route.ts b/apps/sim/app/api/tools/rds/update/route.ts index 9648574e2..d973d04d8 100644 --- a/apps/sim/app/api/tools/rds/update/route.ts +++ b/apps/sim/app/api/tools/rds/update/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createRdsClient, executeUpdate } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSUpdateAPI') @@ -25,6 +26,11 @@ const UpdateSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = UpdateSchema.parse(body) diff --git a/apps/sim/app/api/tools/sqs/send/route.ts b/apps/sim/app/api/tools/sqs/send/route.ts index c738adf9e..2c3e643e3 100644 --- a/apps/sim/app/api/tools/sqs/send/route.ts +++ b/apps/sim/app/api/tools/sqs/send/route.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'crypto' import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { createSqsClient, sendMessage } from '../utils' const logger = createLogger('SQSSendMessageAPI') @@ -21,6 +22,11 @@ const SendMessageSchema = z.object({ export async function POST(request: NextRequest) { const requestId = randomUUID().slice(0, 8) + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + try { const body = await request.json() const params = SendMessageSchema.parse(body) diff --git a/apps/sim/app/api/tools/stagehand/agent/route.ts b/apps/sim/app/api/tools/stagehand/agent/route.ts index ee5ffe6e2..f8cddf143 100644 --- a/apps/sim/app/api/tools/stagehand/agent/route.ts +++ b/apps/sim/app/api/tools/stagehand/agent/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { env } from '@/lib/core/config/env' import { isSensitiveKey, REDACTED_MARKER } from '@/lib/core/security/redaction' import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils' @@ -91,6 +92,11 @@ function substituteVariables(text: string, variables: Record | u } export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + let stagehand: StagehandType | null = null try { diff --git a/apps/sim/app/api/tools/stagehand/extract/route.ts b/apps/sim/app/api/tools/stagehand/extract/route.ts index 18f3e408b..b663f575d 100644 --- a/apps/sim/app/api/tools/stagehand/extract/route.ts +++ b/apps/sim/app/api/tools/stagehand/extract/route.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { checkInternalAuth } from '@/lib/auth/hybrid' import { env } from '@/lib/core/config/env' import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils' @@ -22,6 +23,11 @@ const requestSchema = z.object({ }) export async function POST(request: NextRequest) { + const auth = await checkInternalAuth(request) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + let stagehand: StagehandType | null = null try {