Skip to content

feat(credentials): second auth modes for ServiceNow, Snowflake, Infisical, and Google Ads - #9928

Merged
waleedlatif1 merged 14 commits into
stagingfrom
feat/credential-second-modes
Nov 12, 2026
Merged

waleedlatif1 merged 14 commits into
stagingfrom
feat/credential-second-modes

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Adds a second (or third) sign-in mode to integrations that already support saved credentials. Each one is a new authMethod or stored blob type, and every existing stored shape resolves exactly as before.

  • ServiceNow: OAuth client credentials. Sim mints a token at {instance}/oauth_token.do through the configured-endpoint egress guard and shares one coordinated token per client. Tools and attachment upload send it as a Bearer token. Saved username/password logins are unchanged (Basic).
  • Snowflake: key-pair JWT as a third mode, beside programmatic access tokens and keyless workload identity. Sim stores an unencrypted RSA key (2048 to 4096 bits) and signs an RS256 JWT for each run: iss is ACCOUNT.USER.SHA256:<fp>, sub is ACCOUNT.USER, both uppercased, valid 59 minutes, sent with X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT. The account comes from the host's first label. An account whose name has underscores signs as that name from either of its hostnames (myorg-my_account or myorg-my-account): organization names hold only letters and numbers and account names never hold a hyphen, so every hyphen after the first is a replaced underscore. It needs no workload identity, so organization credentials and deployments without a signing key can use it. The credential provider catalog (v2, Copilot, CLI) lists a Snowflake "Connection configuration" field that documents the key pair in every scope; the keyless hint is appended only where keyless is available. Host hints now name .snowflakecomputing.cn, which was already accepted.
  • Infisical: Universal Auth client ID and secret, logged in at {instance}/api/v1/auth/universal-auth/login through the new postToConfiguredTokenEndpoint guard. This replaces pasted access tokens that expire. One coordinated token per identity, served for at most a day. An expiresIn of 0 (an access token TTL of 0) is also served for a day. Revoked tokens and tokens past their use limit are answered with 401 by Infisical, so the existing 401 invalidation logs in again on the next call.
  • Google Ads: accepts the google-service-account JSON key (adwords scope; the service account is added as a user in Ads). Keyless Google credentials are still refused for Ads.
  • Shared, additive pieces:
    • exchangeClientCredentialsGrant takes an egress: 'configuredEndpoint' option. The request type is a discriminated union, so fetchOptions can't be combined with it.
    • readAccessTokenResponse holds the refusal classification, capped JSON read, token and lifetime checks, and lifetime cap. The grant and the Infisical login both use it.
    • OAuthConfig.optionalCredentialParams lets a tool policy name a mapped param that a credential mode lacks; it is cleared instead of failing.
    • Token descriptor fields take requiredForAuthMethods (token or client_credentials), the same vocabulary client-credential descriptors and the v2 catalog already use. ServiceNow's username is ['token']: it shows only in the pasted-token mode, is never required or stored in client-credentials mode, and the catalog says so.
    • authStyle gains bearer and snowflake-key-pair; both are authoritative, so caller or model values are stripped.

Vendor references: ServiceNow client credentials and grant workflow; Snowflake SQL API authentication and account identifiers; Infisical Universal Auth login, attach (TTL ranges) and Universal Auth; Google Ads service accounts and developer token sunset.

Not included: Infisical OIDC keyless. Infisical matches subjects as glob patterns, so Sim cannot pin a credential's subject, and this needs a security decision and a live proof first.

Product update (if relevant)

  • ServiceNow and Infisical can connect without storing a person's password or a token that expires.
  • Snowflake can connect with a key pair wherever keyless workload identity isn't available.
  • Google Ads reporting can run as a service account.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Unit tests were written test-first for each grant and minter. They cover request shape through the egress guard, lifetime caps (including Infisical's expiresIn 0), refused and invalid responses, unsafe instance URLs, missing secrets (no request sent), and abort. The configured-endpoint guard's own cases (egress refused, unresolvable or transiently failing host, abort) are tested in client-credentials-grant.test.ts.
  • Snowflake JWT claims and fingerprint, locator accounts, underscore accounts from both hostnames, and key-shape refusals are tested.
  • Catalog contract tests check that the Snowflake key-pair field is listed for workspaces that can sign, workspaces that cannot, and organization scope, and that ServiceNow's username carries requiredForAuthMethods: ['token'].
  • Executor tests check that a ServiceNow login sends Basic as before, a minted token is sent as Bearer, and a caller-supplied username is never paired with the saved password.
  • Revert proofs: removing the user uppercasing, the optional-param clearing, or the zero-lifetime handling turns the matching tests red. The hyphen-host test was red before the account-name fix, and the catalog tests were red before the catalog changes.
  • Targeted Vitest passes. Generated-artifact and boundary checks pass: tool-metadata, deployment-config, openapi, cli-api, mcp-operations, egress-boundary, api-validation:strict.
  • Reviewers, please focus on:
    • Rolling deploys: old workers fail closed on the new blob types and authStyle values.
    • Keyless subject sweep: it treats a key-pair blob as keyed, so it never blocks keyless creation.
    • ServiceNow inline mode: authStyle cannot be set by an inline caller.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Connect dialogs: ServiceNow and Infisical gain an auth method picker; Snowflake gains a "Key pair (RSA private key)" option.

@vercel

vercel Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
docs Ready Ready Preview Nov 12, 2026 3:38pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues remain.

Summary

Adds more saved-credential sign-in options:

  • Snowflake connections can sign in with an RSA key pair.
  • ServiceNow connections can use OAuth clients across its tools.
  • Infisical connections can sign in with Universal Auth credentials.
  • Google Ads can connect with a Google service-account key.

The three supplied, unnumbered previous threads are addressed: Snowflake reconnects no longer guess an unknown mode, grant tests use the shared response helper, and internal uploads expire rejected coordinated tokens.

Review used source checks. Tests and visual capture were not run; dependencies are not installed.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Saved credential] --> B{Sign-in mode}
  B -->|ServiceNow or Infisical client credentials| C[Shared token]
  B -->|Snowflake key pair| D[Sign JWT for this run]
  B -->|Google JSON key| E[Request Google access token]
  C --> F[Executor fills credential fields]
  D --> F
  E --> F
  F --> G[Provider request]
  G -->|Rejected shared token| H[Expire coordinated token]
Loading

Reviews (6) · Last reviewed commit: "fix(credentials): expire a coordinated t..." · Reviewed by Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 56 files

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 force-pushed the feat/credential-second-modes branch from c0c4650 to e3e7242 Compare November 12, 2026 12:38
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 57 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread apps/sim/lib/oauth/oauth.ts Resolved
@waleedlatif1
waleedlatif1 force-pushed the feat/credential-second-modes branch from e3e7242 to d0abd40 Compare November 12, 2026 13:26
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 57 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 force-pushed the feat/credential-second-modes branch from d0abd40 to 17775e5 Compare November 12, 2026 14:12
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 57 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 force-pushed the feat/credential-second-modes branch from 17775e5 to 5f723df Compare November 12, 2026 14:54
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 57 files

Confidence score: 3/5

  • In apps/sim/tools/index.ts, invalidation uses the default digest tag, so credentials configured with a custom tag may remain valid on other workers after a 401. Pass the credential’s digest tag through the invalidation call.
  • In apps/sim/tools/index.ts, internal 403s can invalidate the coordinated provider token even when they’re just local permission denials. Restrict invalidation to actual provider-auth failures.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/index.ts">

<violation number="1" location="apps/sim/tools/index.ts:2984">
P2: This invalidates the coordinated provider token for every internal 403, including Sim's own file-access or per-record permission refusals. After the token is older than one minute, an unrelated local denial forces all workers to mint a replacement and can repeatedly hammer the token endpoint; only invalidate the coordinated cache after confirming that the response came from the provider.</violation>

<violation number="2" location="apps/sim/tools/index.ts:3102">
P2: This invalidation uses the default token digest tag, so coordinated credentials that resolve with a custom tag, such as Vanta, are not expired across workers after a 401. Pass the credential’s digest tag through the refusal path or use a provider-specific invalidation callback so other workers cannot keep serving the rejected token.</violation>
</file>

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Turn on auto-fix | Re-trigger cubic

Comment thread apps/sim/tools/index.ts Resolved
Comment thread apps/sim/tools/index.ts Resolved
Google Ads now offers the google-service-account credential: a service
account added as a user of the Ads account signs in as itself with the
adwords scope (developer tokens were retired). Keyless Google credentials
stay refused because Google documents no Workload Identity path for Ads.
Infisical credentials gain a Universal Auth mode: the machine identity's
client ID and secret are stored instead of a pasted access token, and Sim
logs in at the credential's instance through the configured-endpoint egress
guard whenever its token expires. One coordinated token serves every
credential for an identity, so logins don't multiply per worker. Pasted
access-token credentials are unchanged.

Adds postToConfiguredTokenEndpoint, the guarded token request for hosts
taken from a validated credential field.
ServiceNow credentials gain an OAuth client credentials mode beside the
username and password login. The client ID and secret are stored, and Sim
mints a token at the instance's /oauth_token.do through the
configured-endpoint egress guard, shared across workers. The minted token
resolves with authStyle 'bearer', which the ServiceNow tools and the
attachment upload operation send as a bearer token. Saved logins store and
resolve exactly as before.

Shared changes, each additive: exchangeClientCredentialsGrant takes an
egress option for configured hosts; a tool policy can name mapped params a
credential mode lacks (optionalCredentialParams), which are cleared rather
than failing; a token descriptor field can belong to the pasted-token mode
only; a token check's account id may be absent for a sign-in mode.
Snowflake connections gain a key-pair mode beside keyless workload
identity and programmatic access tokens. Sim stores the user's unencrypted
RSA private key and signs a JWT for every run (iss ACCOUNT.USER.SHA256:<fp>,
sub ACCOUNT.USER, uppercased, 59-minute lifetime) that tools and selectors
send with the KEYPAIR_JWT token type. The account comes from the host the
way Snowflake's drivers derive it. Key pairs need no workload identity, so
organization connections and deployments without a signing key can use
them; the connect dialog now always offers them. Token and keyless
credentials store and resolve exactly as before.

Host hints now name .snowflakecomputing.cn, which the validator, tools,
and selectors already accept.
serviceNowAuthorization is now the only caller, so the export was unused.
…s hostnames

Snowflake also serves an account whose name has underscores at a hostname
with hyphens in their place. Organization names hold only letters and
numbers and account names never hold a hyphen, so every hyphen after the
first in an org-account host label is a replaced underscore and is
restored before the key-pair JWT names the account. The derivation moves
next to normalizeSnowflakeHost and its suffix list. The connection secret
type is renamed for the three modes, and the modal shares one saved-
connection submit hook.
…ical login

readAccessTokenResponse holds the refusal classification, capped JSON read,
token and lifetime checks, and lifetime cap that exchangeClientCredentialsGrant
and the Infisical Universal Auth login both need. An Infisical expiresIn of 0
(an access token TTL of 0) is served for the daily cap instead of failing as an
outage. egress and fetchOptions become a discriminated union, and the
configured-endpoint guard's own cases are tested where it lives.
The hidden credential params become a constant like Zendesk's, the upload
schema accepts only the bearer authStyle, and serviceNowAuthorization takes an
optional username so the upload client no longer fakes one. A pasted-token-only
descriptor field stays hidden until that mode is chosen and is never required
in client-credentials mode.
…o the catalog scopes it to the password sign-in
…n organization reconnect's mode unchosen

Key-pair validation now sends its signed JWT through the same guarded egress
path as keyless, so an account host that resolves to a private address is
refused as host_not_allowed. An organization credential's stored mode is not
reported, so its reconnect starts with no mode chosen instead of defaulting to a
programmatic access token. The Infisical and ServiceNow grant tests use the
shared jsonResponse helper.
… the key-pair header test

The key-pair private key textarea hides its value off focus, matching the
Google JSON key field, and the new getSnowflakeHeaders case passes the
oauthCredential its params type requires.
@waleedlatif1
waleedlatif1 force-pushed the feat/credential-second-modes branch from 5f723df to 80980da Compare November 12, 2026 15:31
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 57 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit 21e32d7 into staging Nov 12, 2026
83 checks passed
@waleedlatif1
waleedlatif1 deleted the feat/credential-second-modes branch November 12, 2026 17:01

This branch was successfully deployed

1 active deployment
Preview — 80980daa Deployed Nov 12, 2026 by vercel[bot]
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