diff --git a/apps/sim/lib/api/contracts/credential-groups.test.ts b/apps/sim/lib/api/contracts/credential-groups.test.ts index 7ca0cd7b232..aca0cf00a4e 100644 --- a/apps/sim/lib/api/contracts/credential-groups.test.ts +++ b/apps/sim/lib/api/contracts/credential-groups.test.ts @@ -5,6 +5,7 @@ import { credentialGroupAccessResponseSchema, credentialGroupEnrollmentDetailSchema, credentialGroupEnrollmentListQuerySchema, + credentialGroupOAuthCallbackQuerySchema, credentialGroupSchema, inviteCredentialGroupEnrollmentsBodySchema, sharedCredentialGroupOAuthCallbackContract, @@ -292,4 +293,22 @@ describe('credential group contracts', () => { }).success ).toBe(false) }) + + it('accepts an Atlassian-sized authorization code', () => { + const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({ + state: `cg_${'a'.repeat(36)}`, + code: 'a'.repeat(4096), + }) + + expect(parsed.success).toBe(true) + }) + + it('still rejects an unbounded authorization code', () => { + const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({ + state: `cg_${'a'.repeat(36)}`, + code: 'a'.repeat(8193), + }) + + expect(parsed.success).toBe(false) + }) }) diff --git a/apps/sim/lib/api/contracts/credential-groups.ts b/apps/sim/lib/api/contracts/credential-groups.ts index b96b71c8afd..9a5ff22c81a 100644 --- a/apps/sim/lib/api/contracts/credential-groups.ts +++ b/apps/sim/lib/api/contracts/credential-groups.ts @@ -1,5 +1,9 @@ import { z } from 'zod' -import { workflowIdSchema, workspaceIdSchema } from '@/lib/api/contracts/primitives' +import { + MAX_OAUTH_CODE_LENGTH, + workflowIdSchema, + workspaceIdSchema, +} from '@/lib/api/contracts/primitives' import { defineRouteContract } from '@/lib/api/contracts/types' import { CREDENTIAL_GROUP_MCP_SERVER_LIMIT, @@ -234,7 +238,7 @@ export const startCredentialGroupMcpOAuthParamsSchema = export const credentialGroupOAuthCallbackQuerySchema = z .object({ state: z.string().min(1, 'OAuth state is required').max(512), - code: z.string().min(1).max(2048).optional(), + code: z.string().min(1).max(MAX_OAUTH_CODE_LENGTH, 'Authorization code is too long').optional(), error: z.string().min(1).max(256).optional(), error_description: z.string().max(1000).optional(), }) diff --git a/apps/sim/lib/api/contracts/oauth-connections.ts b/apps/sim/lib/api/contracts/oauth-connections.ts index 8f7b51e3e79..81ba4376414 100644 --- a/apps/sim/lib/api/contracts/oauth-connections.ts +++ b/apps/sim/lib/api/contracts/oauth-connections.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { workspaceIdSchema } from '@/lib/api/contracts/primitives' +import { MAX_OAUTH_CODE_LENGTH, workspaceIdSchema } from '@/lib/api/contracts/primitives' import type { ContractBody, ContractBodyInput, @@ -223,7 +223,6 @@ export const trelloCallbackContract = defineRouteContract({ }) const MAX_OAUTH_RETURN_URL_LENGTH = 2048 -const MAX_OAUTH_CODE_LENGTH = 8192 const MAX_OAUTH_STATE_LENGTH = 256 const MAX_OAUTH_ERROR_LENGTH = 2048 diff --git a/apps/sim/lib/api/contracts/primitives.ts b/apps/sim/lib/api/contracts/primitives.ts index 82f06553bdf..97e2878e1ff 100644 --- a/apps/sim/lib/api/contracts/primitives.ts +++ b/apps/sim/lib/api/contracts/primitives.ts @@ -237,6 +237,17 @@ export function withMissingFieldMessage( */ export const MAX_ID_LENGTH = 128 +/** + * Bound for an OAuth `code` callback parameter. + * + * Authorization codes have no length ceiling in RFC 6749, and providers differ by + * orders of magnitude: Slack's are tens of characters while Atlassian returns a + * signed JWT that routinely exceeds 2KB. The bound exists to keep an unbounded + * string out of a token exchange, so it is sized above the largest real code + * rather than around any one provider. + */ +export const MAX_OAUTH_CODE_LENGTH = 8192 + /** * Builds a required, non-empty string schema whose message covers **both** * failure modes.