Skip to content

Commit f050dc0

Browse files
waleedlatif1claude
andauthored
fix(credential-groups): raise OAuth code cap so Atlassian callbacks validate (#7422)
* 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 * fix(credential-groups): correct the OAuth code bound's doc comment The constant's comment claimed every callback contract shares the bound, but the Shopify and MCP callback schemas leave `code` unbounded. Describe what the bound is and why it is sized as it is, without asserting an adoption scope that would go stale as contracts are migrated onto it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013e5sXbYST2R4qNzM996GFg --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c77b070 commit f050dc0

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.
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)