Repository navigation
Commit 9ab4e6f
authored
feat(credentials): keyless Snowflake, Microsoft app-only and Google keyless, GitHub and Bitbucket tokens, DocuSign JWT (#9869)
* feat(credentials): keyless Snowflake connections through workload identity federation
A Snowflake connection can now trust Sim's OIDC issuer instead of holding a
programmatic access token. Sim signs a short-lived token (audience
snowflakecomputing.com) for the credential's own random subject; the customer
creates a SERVICE user with WORKLOAD_IDENTITY = (TYPE = OIDC ISSUER SUBJECT).
- Save verifies by minting a token and running the identity statement at the
pinned account host through the guarded egress path; Snowflake's refusals
keep the shared codes (invalid_credentials, site_not_found,
provider_unavailable). Subjects bind to their creator like AWS/Databricks.
- Runtime: the resolver returns authStyle 'snowflake-workload-identity' for a
keyless credential, and the tools and selectors send Bearer WIF.OIDC.<token>
with the WORKLOAD_IDENTITY_FEDERATION token type. authStyle and domain are
authoritative on every Snowflake tool, so a caller can no longer choose the
token type or steer the account host of a saved credential.
- PAT credentials resolve and verify exactly as before.
- Connect dialog offers the keyless service user with the CREATE USER
statement; it falls back to the token dialog where keyless is unavailable.
- Docs: Snowflake connections page with keyless setup, troubleshooting, and
the 60-minute Snowflake key cache on signing-key rotation.
* fix(snowflake): run Cortex Analyst SQL with the resolved credential and reopen keyless reconnects as keyless
Cortex Analyst's "run the SQL" went through a nested snowflake_execute_sql
call carrying the already-resolved token and host. With authStyle and domain
now authoritative, the executor cleared both before the nested call, so it
failed for every credential (and a keyless token would have been sent as a
PAT). The generated SQL now runs as one guarded statement request built from
the parent call's credential-derived host, token, and token type, through the
same requestTarget egress the tool transport uses, with redirects refused.
The Snowflake connect dialog opened every reconnect on the programmatic
access token. The workload-identity prepare call now takes the reconnected
credential and reports whether it is keyless (only to an admin of that
credential in the same workspace), and the dialog opens on that mode.
Docs: the 60-minute wait applies before signing with a new key; removing the
old key needs 10 minutes with keyless Snowflake connections.
* fix(snowflake): cancel Cortex Analyst's generated SQL with the run
The generated SQL used to run as a nested tool call, which inherited the run's
abort signal; running it directly dropped that. postProcess now receives the
executor's effective abort signal as an optional fourth argument (the same
signal the tool's own request used), and Cortex Analyst forwards it to the DNS
check, the guarded fetch, and the response read. Other tools' postProcess
hooks are unchanged.
* feat(credentials): saved GitHub personal access tokens and Bitbucket access tokens
GitHub: a saved personal access token credential (fine-grained or classic),
verified against api.github.com/user with the tools' own headers. GitHub's
workflow block authenticates with an inline token rather than OAuth, so it
follows the token-block pattern: github_v3 is the credential-only successor
of github_v2, which stays exactly as it was, marked legacy and hidden from the
toolbar. Every GitHub tool declares the optional saved-token policy; inline
tokens keep working. The GitHub App installation credential (Search) is a
separate service and is untouched: the Assistant still projects GitHub tools
onto the person's connected GitHub account, and Search policy now follows the
visible GitHub block (github_v3).
Bitbucket: a workspace, project, or repository access token as a second
option next to OAuth (the Linear pattern), verified by listing one repository
of the typed workspace on api.bitbucket.org; the slug is validated so it
cannot steer the path. List Workspaces needs a signed-in user, so it now
refuses a saved access token before any request.
Also: isHostNotFound no longer lets an outer error code mask ENOTFOUND on its
cause.
* fix(credentials): close wave 2a review findings on GitHub and Bitbucket tokens
- The GitHub token service is `github-token`, so it no longer shares an id
with the GitHub sign-in provider: parseProvider and getProviderIdFromServiceId
answer for sign-in exactly as before, and a token-only service is never
marked connected from a sign-in account whose id prefixes it.
- Suggested connect rows offer each integration once and count it connected
when any of its services' OAuth or service-account credentials is saved
(GitHub's Search App and saved tokens share one integration).
- Assistant GitHub calls bind through the connected-account policy the
Assistant projects (the person's github-repositories OAuth account) in the
token resolver too, on both the ordinary and managed paths; workflow runs
and saved workspace tokens are unaffected.
- Bitbucket verification reads one repository (`workspace/repository`), the
request Atlassian documents for workspace, project, and repository access
tokens alike; listing a workspace is not documented for repository tokens.
- prepare-workload-identity test uses the central credentials-access mock.
* feat(credentials): DocuSign JWT Grant credentials next to OAuth
A saved DocuSign credential that authenticates with JWT Grant (an
integration key, an impersonated user ID, the app's RSA private key, and an
environment), offered as a second option beside the existing OAuth
connection, which is unchanged.
The minter signs an RS256 assertion (iss = integration key, sub = user,
aud = the environment's authentication host without a scheme, scope
"signature impersonation"), exchanges it at that host's /oauth/token, then
reads /oauth/userinfo on the same host to bind the pinned account (or the
user's default) and returns its eSignature REST base. Both hosts come only
from the stored environment; a base_uri outside that environment's
docusign.net shape is never trusted with the token. GUIDs and the key are
checked before any request, and tokens are re-minted about 15 minutes
before they expire, as DocuSign advises.
DocuSign tools take the account base as an authoritative credential param,
so a caller can never supply it, and DocuSignClient skips its
deployment-host userinfo lookup when one is bound. OAuth tokens resolve
exactly as before.
Save-time failures map to consent_required (the connect modal shows the
exact consent URL for the key and environment) and account_not_found (a
pinned account the user does not belong to), alongside the shared
invalid_credentials and provider_unavailable codes.
* fix(credentials): close wave 2b review findings on DocuSign JWT Grant
- The DocuSign knowledge connector now honours a JWT Grant credential's
bound account. The resolved token's instanceUrl reaches the connector as
credentialInstanceUrl in the sync context (seeded on every path through
syncContextForToken). The connector re-validates it with the same parser
as DocuSignClient, lists and fetches from that account in the
credential's environment, links envelopes to that environment's web app,
and validates without asking the deployment host. A seeded value of the
wrong shape fails closed. OAuth syncs are unchanged.
- RSA keys under 2048 bits are refused as invalid_credentials with copy
that names the size, instead of jose's raw TypeError.
- A short expires_in no longer makes every call re-mint: the cached
lifetime floors at six minutes while staying a minute under the real
expiry.
- The connect modal shows the consent URL as a link with a copy button,
and account_not_found says "pinned account" only when one was entered.
* feat(credentials): Microsoft Entra app-only credentials for Entra ID, SharePoint, Dataverse, and Dynamics 365
A saved Microsoft app credential (OAuth 2.0 client credentials) as an
additional option next to Microsoft OAuth, which is unchanged. One app
registration serves the four services whose tools run without a signed-in
user; Outlook, OneDrive, Excel, Word, Planner, and Teams never offer it and
token resolution refuses it for them (provider mismatch).
- Modes: client secret; certificate (PS256 client assertion with the
certificate's x5t#S256 thumbprint, aud = the tenant token endpoint, 5-minute
lifetime; key/certificate pairing, RSA size, and expiry checked before any
request); keyless federated credential (a Sim workload-identity token for the
credential's own subject, audience api://AzureADTokenExchange, subject bound
to the saving user, refused if the app also accepts an unheld subject).
- The token resource comes from the tool's service, passed from the
server-validated tool policy: Graph for Entra ID and SharePoint, the stored
environment for Dataverse and Dynamics 365. Any other service is refused.
- The environment URL is stored on the credential (public-cloud Dynamics
hosts, https root only). Dataverse tools take credentialType and instanceUrl
as authoritative params; an app credential's request goes only to its own
environment and a block environment that names another is refused. OAuth
requests keep the block environment exactly as before. Dynamics 365 already
matched instanceUrl against the block environment.
- Save check: a Graph token reading the app's own service principal (no
permission needed), plus Dataverse WhoAmI when an environment is set. Entra
AADSTS codes map to actionable copy; runtime failures keep the shared
invalid_credentials / provider_unavailable split. Tokens cache per
credential, resource, and stored-secret version in an LRU.
- Azure AD Reset password refuses app-only credentials: Graph does not
support application permissions for resetPassword.
- Connect dialog with tenant ID, client ID, environment URL, and the
issuer/audience/subject to paste; docs page with per-mode setup.
* fix(credentials): close wave 3 review findings on Microsoft app credentials
- SharePoint selectors (workspace and organization paths) and knowledge
connectors now pass the service they resolve for (the selector's own
serviceId, the connector's declared auth provider), so a Microsoft app
credential browses SharePoint with a Graph token instead of being refused.
The resolution option is renamed from toolService to service; it is only
ever set from server-side policy.
- AADSTS700211 and 700212 are no longer mapped: Microsoft documents only
70021 and 700213 for federated credential mismatches. Other keyless
refusals still name the mismatch.
- DocuSign minter test mocks carry account_name, and the connector
access-token test narrows its tokens with a guard instead of non-null
assertions.
* feat(credentials): keyless Google Cloud credentials via Workload Identity Federation
A keyless mode of the existing Google service account credential
(google-service-account) for Google Cloud APIs. JSON-key credentials, their
connect flow, and their token minting are unchanged.
- Flow: Sim signs a short-lived token for the credential's own subject with
aud = the customer's provider resource name
(https://iam.googleapis.com/projects/<number>/locations/global/
workloadIdentityPools/<pool>/providers/<provider>), exchanges it at
sts.googleapis.com/v1/token (token-exchange, jwt subject token,
cloud-platform scope, audience //iam.googleapis.com/...), then calls
iamcredentials generateAccessToken for the service account with the tool's
scopes and a 1h lifetime. Impersonation rather than direct federated access:
some Cloud APIs reject federated tokens, and the service account keeps the
same identity and roles a JSON key uses.
- Inputs validated against Google's documented formats before any request:
numeric project number, pool/provider IDs (4-32 [a-z0-9-], no gcp-), and a
user-managed *.iam.gserviceaccount.com email, so nothing can steer the STS
audience or IAM URL. Requests never follow redirects.
- Scope: refuses domain-wide delegation (an impersonated user) and any service
but BigQuery and Vertex AI before reading further or contacting Google, with
a new shared unsupported_use code and copy pointing to a JSON key. Workspace
blocks never offer the mode.
- Save check: STS exchange, impersonation, and tokeninfo (no IAM role or
enabled API needed), plus a probe that a subject no credential holds cannot
impersonate the service account. Subject bound to the saving user with the
signed proof; kept across reconnects.
- STS/IAM failures map to actionable copy (provider settings, issuer
reachability, the exact workloadIdentityUser binding) without Google's
descriptions or any token; runtime keeps the invalid_credentials /
provider_unavailable split. Tokens cache per credential, secret version, and
scopes in an LRU.
- Per-credential audiences: audiences.ts gains google without a fixed
audience; the setup response omits audience for it and reports a keyless
reconnect. Connect dialog shows the issuer, audience, subject, and filled-in
gcloud commands (pool, OIDC provider with attribute condition on the
subject, IAM binding); docs section added.
* fix(credentials): close wave 4 review findings on keyless Google Cloud credentials
- Drop the tokeninfo check: a token from generateAccessToken already proves
the provider accepted Sim's token and the impersonation binding, and
tokeninfo puts a live token in a query string.
- Test that saving fails closed when the stray-subject pin probe hits an STS
outage or an unreachable IAM endpoint.
- Pass --project=<project number> on all three gcloud setup commands, drop
the unused Resource Manager API from the docs, and raise the shared
validation error for an empty scope list.
* fix(credentials): keep Snowflake, Microsoft app, and Google keyless subjects unique
The subject-uniqueness scan from #9866 covered only AWS and Databricks. It now
also scans Snowflake, Microsoft app, and Google service account credentials,
so no two live credentials of an owner hold one keyless subject. Their keyed
secrets that carry no config (a Google JSON key, a Snowflake programmatic
access token) hold no subject and no longer read as unreadable. The three
keyless save paths report a newly bound subject so create checks it.
* fix(credentials): close round-1 CI and review findings on alt auth
- Knowledge connector tests expect the service now passed with Google
service-account token requests; a new test proves a JSON-key mint signs the
same request with or without it.
- Re-record the tool-registry-boundary baseline: the keyless Snowflake,
Microsoft app, and Google modules add 5 modules behind credential-service,
which tipped credit-usage past its allowance on top of staging's drift.
- Docs compatibility baseline: record the new code examples in the Google and
Snowflake service account pages.
- Generate the Microsoft certificate test fixture at run time instead of
committing a PEM key and certificate.
- A Microsoft app credential reconnect opens on keyless only when the stored
credential is federated (the prepare step now reports it), like Snowflake;
new connections still default to keyless.
- TSDoc on the Google project number, pool/provider ID, and service account
email validators.
* fix(credentials): offer keyless Google setup only in a Google Cloud context
Reconnecting a keyless Google service account from a Workspace integration
(Gmail, Drive, ...) offered the keyless form, saving a credential that
integration can never use. The dialog now offers keyless only for a Google
Cloud service (new or reconnect) or on the credential's own page; from a
Workspace integration it explains the credential works only with BigQuery and
Vertex AI and does not save. The chat connect control passes its service so
it gets the same gate.
* test(credentials): match the stable part of the subject-in-use refusal1 parent aabee30 commit 9ab4e6f
246 files changed
Lines changed: 8554 additions & 747 deletions
File tree
- apps
- docs
- components/ui
- content
- docs/integrations
- scripts/fixtures
- sim
- app/workspace/[workspaceId]
- home/components
- message-content/components/special-tags
- suggested-actions
- integrations
- [block]
- components/connect-service-account-modal
- w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector
- blocks
- blocks
- connectors/docusign
- ee/access-requests/lib
- executor/utils
- hooks/queries
- oauth
- lib
- api/contracts
- v2
- block-metadata
- credentials
- application
- client-credential-accounts
- minters
- orchestration
- token-service-accounts
- validators
- workload-identity
- integrations
- internal
- docusign
- llm
- knowledge
- application
- connectors
- mothership
- assistant
- generated
- oauth
- permission-groups
- selectors/server
- providers
- tools
- bitbucket
- docusign
- generated
- github
- microsoft_ad
- microsoft_dataverse
- snowflake
- packages/deployment-config/src
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
481 | 481 | | |
482 | 482 | | |
483 | 483 | | |
| 484 | + | |
484 | 485 | | |
485 | 486 | | |
486 | 487 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | | - | |
| 68 | + | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
18 | 20 | | |
19 | 21 | | |
20 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
| 18 | + | |
| 19 | + | |
16 | 20 | | |
17 | 21 | | |
18 | 22 | | |
| |||
0 commit comments