flex_form_integration_connections and refreshed automatically before dispatch when possible.
Routes
Both routes use the
web + auth middleware stack — only logged-in Filament users can connect accounts.
Redirect
{connection} is the FlexFormIntegrationConnection ID (draft or reconnect). The controller:
- Loads the connection’s
driver_keyand resolves the driver - Builds the provider authorize URL (scopes, encrypted OAuth state with one-time nonce, PKCE S256
code_challengewhen the driver enables PKCE — default on forGenericOAuthIntegrationDriver) - Redirects the admin browser to the provider
Callback
The callback route:- Validates encrypted
state+ one-time nonce (and initiating user id) - Exchanges
codefor tokens — includescode_verifierwhen PKCE was used on redirect - Persists tokens via
OAuthTokenManager - Sets connection
statustoconnected, clearslast_error - Redirects back to Integrations → Connections (
?tab=connections— fixed admin URL, never an open redirect)
needs_reauth with last_error text admins can read in Studio.
PKCE
GenericOAuthIntegrationDriver enables PKCE by default (usesPkce(): true):
- Redirect stores a high-entropy
code_verifierin cache with the OAuth nonce - Authorize URL includes
code_challenge+code_challenge_method=S256 - Token exchange posts
code_verifier
usesPkce(): false only for legacy providers that reject PKCE.
OAuthTokenManager
Central service for token lifecycle:- Never log raw tokens
- Refresh uses the driver’s
oauthConfig()token URL and client credentials from global integration config - Missing refresh token → connection marked
reconnect_required; admin must run redirect again
Encrypted storage
FlexFormIntegrationConnection casts credentials as encrypted:array. At rest you store:
APP_KEY — rotating APP_KEY without re-encrypting breaks stored credentials. Plan key rotation with a maintenance window and force reconnect.
Non-secret meta (account ID, hub ID, workspace name) lives in the plain meta JSON column for Studio labels.
Health and testing
- Test connection on a Connection row calls
OAuthTokenManager::accessToken()then the driver’stestConnection()with resolved auth. - Failed refresh surfaces in
last_errorand blocks dispatch until reconnect. - Queue workers use the same manager — no separate token cache that could desync.