- Integration driver — defines scopes, token URLs, feed fields, and dispatch logic.
- Connection — a saved OAuth account (tokens + metadata) admins authorize once and reuse across forms.
Why two tiers?
Admins connect once under Integrations → Connections (hub tab), then pick that connection on each form feed — no re-pasting refresh tokens per form.
Auth from a Connection
Indispatch() and testConnection(), resolve credentials from the linked connection instead of inline global config:
- Client ID / Client secret (app registration)
- Optional default scopes
- Connection picker on the form feed (required)
Driver responsibilities
OAuth drivers extend the sameIntegrationDriver contract with:
getOAuthConfig()— authorize URL, token URL, scopes, redirect URI,uses_pkce(never includesclient_secret)oauthClientId()/oauthClientSecret()— read credentials for token exchange / storage onlyusesPkce()— defaulttrue(S256); override only for legacy providersexchangeOAuthCode($code, $connectionId, $codeVerifier = null)— token exchange- Tokens refresh via
OAuthTokenManager(mutex +needs_reauth)
Studio UX
- Admin registers the OAuth app (client ID/secret) under Integrations.
- Clicks Connect account → redirect flow → new row in Connections.
- On a form feed, selects Connection: Marketing Hub ([email protected]).
- Field mapping and conditional rules behave like any other feed.
Connections are panel-wide. Scope them to tenants in multi-tenant apps via custom connection models — see Custom models.