Skip to content

Commit 303066e

Browse files
waleedlatif1claude
andcommitted
fix(credential-groups): raise OAuth code cap so Atlassian callbacks validate
The managed credential-group callback capped the OAuth `code` query param at 2048 characters. That bound was sized when the flow only supported Slack, whose authorization codes are short, and was never revisited as more providers were added. Providers that return a signed JWT as the authorization code exceed it, so the callback rejected the exchange with a validation error before it ran. Share a single MAX_OAUTH_CODE_LENGTH (8192) from the contract primitives and use it on both callback paths, so the two contracts cannot drift apart again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013e5sXbYST2R4qNzM996GFg
1 parent 0db6256 commit 303066e

4 files changed

Lines changed: 37 additions & 4 deletions

File tree

‎apps/sim/lib/api/contracts/credential-groups.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
credentialGroupAccessResponseSchema,
66
credentialGroupEnrollmentDetailSchema,
77
credentialGroupEnrollmentListQuerySchema,
8+
credentialGroupOAuthCallbackQuerySchema,
89
credentialGroupSchema,
910
inviteCredentialGroupEnrollmentsBodySchema,
1011
sharedCredentialGroupOAuthCallbackContract,
@@ -292,4 +293,22 @@ describe('credential group contracts', () => {
292293
}).success
293294
).toBe(false)
294295
})
296+
297+
it('accepts an Atlassian-sized authorization code', () => {
298+
const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({
299+
state: `cg_${'a'.repeat(36)}`,
300+
code: 'a'.repeat(4096),
301+
})
302+
303+
expect(parsed.success).toBe(true)
304+
})
305+
306+
it('still rejects an unbounded authorization code', () => {
307+
const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({
308+
state: `cg_${'a'.repeat(36)}`,
309+
code: 'a'.repeat(8193),
310+
})
311+
312+
expect(parsed.success).toBe(false)
313+
})
295314
})

‎apps/sim/lib/api/contracts/credential-groups.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { z } from 'zod'
2-
import { workflowIdSchema, workspaceIdSchema } from '@/lib/api/contracts/primitives'
2+
import {
3+
MAX_OAUTH_CODE_LENGTH,
4+
workflowIdSchema,
5+
workspaceIdSchema,
6+
} from '@/lib/api/contracts/primitives'
37
import { defineRouteContract } from '@/lib/api/contracts/types'
48
import {
59
CREDENTIAL_GROUP_MCP_SERVER_LIMIT,
@@ -234,7 +238,7 @@ export const startCredentialGroupMcpOAuthParamsSchema =
234238
export const credentialGroupOAuthCallbackQuerySchema = z
235239
.object({
236240
state: z.string().min(1, 'OAuth state is required').max(512),
237-
code: z.string().min(1).max(2048).optional(),
241+
code: z.string().min(1).max(MAX_OAUTH_CODE_LENGTH, 'Authorization code is too long').optional(),
238242
error: z.string().min(1).max(256).optional(),
239243
error_description: z.string().max(1000).optional(),
240244
})

‎apps/sim/lib/api/contracts/oauth-connections.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { z } from 'zod'
2-
import { workspaceIdSchema } from '@/lib/api/contracts/primitives'
2+
import { MAX_OAUTH_CODE_LENGTH, workspaceIdSchema } from '@/lib/api/contracts/primitives'
33
import type {
44
ContractBody,
55
ContractBodyInput,
@@ -223,7 +223,6 @@ export const trelloCallbackContract = defineRouteContract({
223223
})
224224

225225
const MAX_OAUTH_RETURN_URL_LENGTH = 2048
226-
const MAX_OAUTH_CODE_LENGTH = 8192
227226
const MAX_OAUTH_STATE_LENGTH = 256
228227
const MAX_OAUTH_ERROR_LENGTH = 2048
229228

‎apps/sim/lib/api/contracts/primitives.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,17 @@ export function withMissingFieldMessage<TSchema extends z.ZodString>(
237237
*/
238238
export const MAX_ID_LENGTH = 128
239239

240+
/**
241+
* Bound for an OAuth `code` callback parameter, shared by every callback contract.
242+
*
243+
* Authorization codes have no length ceiling in RFC 6749, and providers differ by
244+
* orders of magnitude: Slack's are tens of characters while Atlassian returns a
245+
* signed JWT that routinely exceeds 2KB. The bound exists to keep an unbounded
246+
* string out of a token exchange, so it is sized above the largest real code
247+
* rather than around any one provider.
248+
*/
249+
export const MAX_OAUTH_CODE_LENGTH = 8192
250+
240251
/**
241252
* Builds a required, non-empty string schema whose message covers **both**
242253
* failure modes.

0 commit comments

Comments
 (0)