Skip to content

feat(credentials): add v2 credential lifecycle APIs - #6664

Open
TheodoreSpeaks wants to merge 6 commits into
stagingfrom
feat/credential-v2-api
Open

feat(credentials): add v2 credential lifecycle APIs#6664
TheodoreSpeaks wants to merge 6 commits into
stagingfrom
feat/credential-v2-api

Conversation

@TheodoreSpeaks

@TheodoreSpeaks TheodoreSpeaks commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Problem:

The V2 API could list stored credentials, but clients could not discover the complete set of credential methods, create service-account credentials, start or reconnect OAuth credentials safely, or disconnect credentials. OAuth still has to cross into an authenticated browser session, and the old browser entrypoint accepted mutable target parameters instead of an API-created connection intent.

Solution:

  • Put the complete lifecycle under /api/v2/credentials.
  • Discover all 52 OAuth services (53 provider IDs) and 23 service-account providers, including caller-specific availability and the exact fields needed to connect each one.
  • Verify and store service-account credentials through the existing credential table and encrypted secret pipeline; no new table or migration.
  • Create short-lived, user-bound OAuth connection drafts and return a browser URL. The browser requires login, reauthorizes the draft, and finishes at /oauth/credential-connected.
  • Support OAuth reconnection by credential ID while preserving the existing display name.
  • Disconnect OAuth or service-account credentials with credential-admin authorization and clear stored workflow, deployment, paused-run, knowledge-connector, and webhook references.
  • Keep all responses on the standard V2 { data } / { data, nextCursor } / { error } envelopes.

API shapes:

GET /api/v2/credentials?workspaceId={workspaceId}

{
  "data": [
    {
      "id": "credential-id",
      "type": "oauth",
      "displayName": "Work Gmail",
      "description": null,
      "providerId": "google-email",
      "accountId": "provider-account-id",
      "hasServiceAccountKey": false,
      "role": "admin",
      "createdAt": "2026-08-13T18:00:00.000Z",
      "updatedAt": "2026-08-13T18:00:00.000Z"
    }
  ],
  "nextCursor": null
}

GET /api/v2/credentials/providers?workspaceId={workspaceId}

OAuth entry:

{
  "type": "oauth",
  "serviceId": "salesforce",
  "name": "Salesforce",
  "description": "Connect to Salesforce CRM data and operations.",
  "providerFamily": "salesforce",
  "available": true,
  "supportsReconnect": true,
  "authorizationOptions": [
    { "providerId": "salesforce", "label": "Production" },
    { "providerId": "salesforce-sandbox", "label": "Sandbox" }
  ]
}

Service-account entry:

{
  "type": "service_account",
  "serviceId": "zoom-service-account",
  "providerId": "zoom-service-account",
  "name": "Zoom server-to-server app",
  "description": "Connect Zoom with a server-to-server app.",
  "providerFamily": "zoom",
  "available": true,
  "docsUrl": "https://docs.sim.ai/integrations/zoom-service-account",
  "requiresClientGeneratedCredentialId": false,
  "fields": [
    { "id": "clientId", "label": "Client ID", "placeholder": "Paste the client ID", "required": true, "secret": false, "multiline": false },
    { "id": "clientSecret", "label": "Client secret", "placeholder": "Paste the client secret", "required": true, "secret": true, "multiline": false },
    { "id": "orgId", "label": "Account ID", "placeholder": "Paste the account ID", "required": true, "secret": false, "multiline": false }
  ]
}

The endpoint returns { "data": [oauthEntry, serviceAccountEntry], "nextCursor": null }.

POST /api/v2/credentials

Creates a service-account credential. displayName is optional because providers may derive it from the verified account identity.

{
  "workspaceId": "workspace-id",
  "type": "service_account",
  "providerId": "zoom-service-account",
  "displayName": "Zoom automation",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "orgId": "YOUR_ACCOUNT_ID"
}

Returns 201 { "data": credential } for a new credential or 200 { "data": credential } for an accepted replay. Secret fields are write-only and never returned.

POST /api/v2/credentials/connections

New OAuth credential:

{
  "workspaceId": "workspace-id",
  "providerId": "google-email",
  "displayName": "Work Gmail"
}

Reconnect an existing OAuth credential:

{
  "workspaceId": "workspace-id",
  "credentialId": "credential-id"
}
{
  "data": {
    "authorizationUrl": "https://www.sim.ai/api/auth/oauth2/authorize?draftId=draft-id",
    "expiresAt": "2026-08-13T18:30:00.000Z"
  }
}

This write requires a personal API key because the draft is bound to the human who must sign in in the browser. Workspace API keys can still list credentials and providers.

DELETE /api/v2/credentials/{credentialId}?workspaceId={workspaceId}

{
  "data": {
    "id": "credential-id",
    "deleted": true
  }
}

Validation:

  • bun run lint
  • bunx turbo run type-check --filter=sim --filter=@sim/auth
  • focused V2, application, OAuth, service-account, deletion, and visibility tests: 87 passed
  • bun run check:audits: 26 passed
  • bun run check:api-validation:strict
  • bun run check:openapi
  • OpenAPI regenerated and current: 7 documents, 139 operations, 141 contracts, 265 JSON Schema examples, and 113 runtime examples validated

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 13, 2026 8:49pm

Request Review

@TheodoreSpeaks
TheodoreSpeaks marked this pull request as ready for review August 13, 2026 18:17
@cursor

cursor Bot commented Aug 13, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Expands the public API for credential creation, OAuth token rebinding, and disconnect with admin checks and write-only secret handling—security-sensitive paths with a large documented surface area.

Overview
Adds the full v2 credential lifecycle beyond list: provider discovery, verified service-account create, OAuth connect/reconnect via short-lived drafts, and disconnect with reference cleanup. OpenAPI and shared 403 docs now include CREDENTIAL_ADMIN_ACCESS_REQUIRED.

New routes: GET /api/v2/credentials/providers (full catalog with availability and field metadata), POST /api/v2/credentials (service-account create, 200/201 on replay), POST /api/v2/credentials/connections (personal key only — returns authorizationUrl + expiresAt), and DELETE /api/v2/credentials/{credentialId} (credential-admin, clears workflow/deployment/webhook refs). List/create paths use shared toV2Credential presentation and workspace-scoped error concealment.

OAuth browser path: GET /api/auth/oauth2/authorize accepts draftId alone (mutually exclusive with providerId/workspaceId query params). Drafts are resolved through launchCredentialConnection for the session principal; success/failure lands on /oauth/credential-connected. Custom providers (Trello, Instagram, Shopify) still redirect to their authorize URLs. Plain connect draft upserts use setWhere so a reconnect intent is not overwritten when refreshing a plain connect.

Contracts add provider schemas, connection body unions, service-account body validation against SERVICE_ACCOUNT_REQUIRED_FIELDS, and provider list is registered as a non-paged full-set list in pagination tests.

Reviewed by Cursor Bugbot for commit cf680e6. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds the V2 credential lifecycle APIs and secures browser OAuth handoff with short-lived, user-bound connection drafts.

  • Adds provider discovery, service-account creation, OAuth connect/reconnect, credential listing, and disconnection endpoints.
  • Preserves active OAuth draft identifiers while rejecting attempts to rebind an active draft to a different target.
  • Treats reconnect display names as mutable presentation so renamed credentials can retry the same draft.
  • Extends contracts, OpenAPI documents, authorization behavior, cleanup orchestration, and regression coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/credentials/connect-draft.ts Creates user-bound expiring drafts, preserves active draft IDs, CAS-guards immutable targets, and allows renamed reconnect retries.
apps/sim/lib/credentials/application/create-credential-connection.ts Resolves new versus reconnect intent and passes the correct display-name identity semantics into draft creation.
apps/sim/app/api/auth/oauth2/authorize/route.ts Reauthorizes active drafts against the authenticated browser user before launching OAuth.
apps/sim/lib/credentials/connect-draft.test.ts Covers stable draft IDs, mutable reconnect names, and conflicting connection intents.
apps/sim/lib/credentials/deletion.ts Coordinates credential disconnection with cleanup of stored references.
apps/sim/lib/api/contracts/v2/credentials.ts Defines the V2 credential lifecycle request and response contracts.

Sequence Diagram

sequenceDiagram
  participant Client
  participant V2 as V2 Credentials API
  participant Drafts as OAuth Draft Store
  participant Browser as Authenticated Browser
  participant OAuth as OAuth Provider
  Client->>V2: POST /credentials/connections
  V2->>Drafts: Create or refresh immutable intent
  Drafts-->>V2: draftId and expiry
  V2-->>Client: authorizationUrl
  Client->>Browser: Open authorizationUrl
  Browser->>Drafts: Load user-bound active draft
  Drafts-->>Browser: Authorized connection target
  Browser->>OAuth: Complete authorization
  OAuth-->>Browser: OAuth callback
  Browser->>V2: Materialize or reconnect credential
  V2-->>Browser: /oauth/credential-connected
Loading

Reviews (6): Last reviewed commit: "fix(credentials): stabilize oauth draft ..." | Re-trigger Greptile

Comment thread apps/sim/lib/credentials/connect-draft.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/credentials/connect-draft.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e4b09dc. Configure here.

@TheodoreSpeaks TheodoreSpeaks changed the title feat(credentials): add v2 OAuth connection APIs feat(credentials): add v2 credential lifecycle APIs Aug 13, 2026
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/credentials/deletion.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 7fcf26f. Configure here.

Comment thread apps/sim/lib/credentials/connect-draft.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit cf680e6. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant