feat(credentials): second auth modes for ServiceNow, Snowflake, Infisical, and Google Ads
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.dothrough 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:
issisACCOUNT.USER.SHA256:<fp>,subisACCOUNT.USER, both uppercased, valid 59 minutes, sent withX-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_accountormyorg-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/loginthrough the newpostToConfiguredTokenEndpointguard. This replaces pasted access tokens that expire. One coordinated token per identity, served for at most a day. AnexpiresInof 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-accountJSON key (adwords scope; the service account is added as a user in Ads). Keyless Google credentials are still refused for Ads. - Shared, additive pieces:
exchangeClientCredentialsGranttakes anegress: 'configuredEndpoint'option. The request type is a discriminated union, sofetchOptionscan't be combined with it.readAccessTokenResponseholds the refusal classification, capped JSON read, token and lifetime checks, and lifetime cap. The grant and the Infisical login both use it.OAuthConfig.optionalCredentialParamslets a tool policy name a mapped param that a credential mode lacks; it is cleared instead of failing.- Token descriptor fields take
requiredForAuthMethods(tokenorclient_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. authStylegainsbearerandsnowflake-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
expiresIn0), 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 inclient-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
authStylevalues. - Keyless subject sweep: it treats a key-pair blob as keyed, so it never blocks keyless creation.
- ServiceNow inline mode:
authStylecannot be set by an inline caller.
- Rolling deploys: old workers fail closed on the new blob types and
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.