Skip to main content

Authentication

All connectors share the same sf.auth.* configuration, implemented once in sf-core.

Flows

Flowsf.auth.grant.typeWhen to use
Client Credentialsclient_credentialsServer-to-server with a single integration user (recommended)
JWT Bearerjwt_bearerCertificate-based auth, multiple usernames, no shared secret
Username-PasswordpasswordLegacy orgs only — blocked by default in orgs created Summer '23+

Full setup walkthrough: Salesforce Setup.

Client Credentials details

POST {sf.instance.url}/services/oauth2/token with grant_type=client_credentials. The token endpoint must be the org's My Domain host — the connectors reject login.salesforce.com/test.salesforce.com at validation time. The Connected App needs a "Run As" API-Only integration user.

JWT Bearer details

The connector builds an RS256-signed JWT (iss = consumer key, sub = sf.username, aud = sf.jwt.audience, 3-minute expiry, unique jti) and exchanges it with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer. Key material (sf.jwt.keystore.path) may be:

  • a PEM file containing a PKCS#8 private key (-----BEGIN PRIVATE KEY-----),
  • a JKS keystore, or
  • a PKCS12 keystore (.p12/.pfx)

with sf.jwt.keystore.password for the keystore formats. Use sf.jwt.audience=https://test.salesforce.com for sandbox orgs.

Session lifecycle

  • The access token and instance_url from the token response are cached per task.
  • On any 401 / INVALID_SESSION_ID, the session is invalidated and one transparent re-authentication is attempted before the request is retried; concurrent callers share the refreshed session.
  • The Pub/Sub gRPC channel attaches the session as call metadata (accesstoken, instanceurl, tenantid) on every RPC, so streams pick up refreshed tokens on reconnect.

Secret handling

  • Credentials are declared as Connect PASSWORD-type configs, so they are masked in the REST API and logs.
  • Tokens are never logged; error messages never echo request bodies containing credentials.
  • Use a Connect config provider to keep secrets out of connector JSON:
# worker
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
"sf.consumer.secret": "${file:/secrets/sf.properties:consumer.secret}"