Repository navigation
Conversation
Serve GET, POST, PUT and DELETE on /organizations/{organization_id}/connected_accounts/{slug}.
An organization holds one account per provider with user_id null, apart from any its members
hold. Import and update name the acting member in user_id, who must be an active member.
Accounts now report established_by_user_id, cleared when that user is deleted. Deleting an
organization deletes its accounts and its members' accounts scoped to it. The connectedAccounts
seed takes owner: organization.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Describe the organization-owned rules in prose, as the other behavior sections do. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An overlapping import otherwise misses the other's write and both answer 201. Cover the members' scoped accounts in the organization-delete cascade test, and say plainly that ending a membership does not clear credentials, unlike production. Co-Authored-By: Claude Opus 5.5 <[email protected]>
As CLAUDE.md asks, and cite production's invalidation when the establishing member leaves. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Serve POST /data-integrations/{slug}/credentials. It returns the account's token as a
credential, or active: false with not_installed or needs_reauthorization. With
connection_owner: organization it vends the organization's account. Naming an organization
the user is not an active member of returns 403 for either owner, and a malformed body
returns the spec's 400.
/credentials and /token share the token logic. /token is otherwise unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…olves A request that leaves organization_id out still resolves a user's only account for the provider, and that account may be scoped to an organization the user has left. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
[Critical impact] The PR appears safe to merge, with one remaining non-blocking error-response mismatch. Findings
Fix with agent prompt### Issue 1
src/workos/routes/data-integrations.ts:undefined-95
Invalid JSON sent to `/credentials` bypasses `badRequest()`. For example, a body of `{` returns `{ message: 'Problems parsing JSON', code: 'invalid_request_body' }`, rather than the documented `{ message, error: 'Bad Request' }`. Convert parsing failures to this route's error shape and add an invalid-JSON test.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryAdds credential retrieval for user- and organization-owned connected accounts.
|
| // The spec's credential vend, for user- and organization-owned accounts alike. | ||
| app.post('/data-integrations/:slug/credentials', async (c) => { | ||
| const slug = c.req.param('slug'); | ||
| const owner = resolveOwner(await parseJsonBody(c)); |
There was a problem hiding this comment.
Invalid JSON gets different fields
Invalid JSON sent to /credentials bypasses badRequest(). For example, a body of { returns { message: 'Problems parsing JSON', code: 'invalid_request_body' }, rather than the documented { message, error: 'Bad Request' }. Convert parsing failures to this route's error shape and add an invalid-JSON test.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/workos/routes/data-integrations.ts
Line: 95
Comment:
**Invalid JSON gets different fields**
Invalid JSON sent to `/credentials` bypasses `badRequest()`. For example, a body of `{` returns `{ message: 'Problems parsing JSON', code: 'invalid_request_body' }`, rather than the documented `{ message, error: 'Bad Request' }`. Convert parsing failures to this route's error shape and add an invalid-JSON test.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
We're following the convention of other existing endpoints here. Leaving for now.
There was a problem hiding this comment.
Understood. Keeping parseJsonBody behavior consistent with the existing endpoints is reasonable; I’ll treat this as intentional and won’t press the finding.
…0 row The 400 with "error": "Bad Request" answers a missing or invalid field; a body that is not JSON gets the emulator-wide invalid_request_body, as on every route. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stacks on #177.
Summary
POST /data-integrations/{slug}/credentials. It returns the account's token as acredential. Without a usable account it returns{ active: false, error }withnot_installedorneeds_reauthorization.connection_owner: organizationvends the organization's account and requiresorganization_id.user, the default, vends the user's own.{ message, error: 'Bad Request' }./credentialsand/tokenshare the token logic (vendAccessToken).Behaviour
/credentialswith a malformed body400,"error": "Bad Request"/credentialsnaming an organization the user is not an active member of403/credentialsresolving a personal account scoped to an organization the user is not an active member of403/credentialswith no account for the owner200,{ "active": false, "error": "not_installed" }Verification
Full suite: 1918 pass, 0 fail.
bun run typecheck,bun run lint,bun run fmt:checkandbun run gen:supportedare clean. New tests indata-integrations.spec.tscover both owners, the reauthorization and disconnect states, both 403s, each malformed body, and the user-owned path through the Node SDK'screateDataIntegrationCredential.Resolves part of #176