Security requirements
Account linking joins identities from separate systems. A flaw in this flow can let one user connect another user's Advertiser account, leak backend credentials, or redirect a browser to an attacker-controlled destination.
Apply these requirements to both supported mechanisms, then follow the mechanism-specific controls below.
Platform requirements
- Call
POST /v1/account/start-connectonly from the Platform backend. - Authenticate the Platform user before starting a connection.
- Derive
platform_user_idfrom the authenticated session instead of accepting an arbitrary browser-supplied value. - Use a Platform return URL approved for the integration.
- Redirect only to the
data.redirect_urlreturned by Onward; do not construct an Advertiser URL in frontend code. - Refresh the active quest chain after the user returns and treat API state as authoritative.
The backend-only boundary prevents an attacker from submitting another user's platform_user_id and linking an account
on that user's behalf.
Advertiser requirements
- Complete authentication or registration before associating an
advertiser_user_idwith the flow. - Keep
advertiser_user_idstable and scoped to the Advertiser system. - Keep API credentials, OAuth client secrets, authorization codes, and connection tokens out of frontend code.
- Use HTTPS for authorization pages, callbacks, token endpoints, and Platform redirects.
- Do not place API keys or API secrets in redirect URLs.
- Avoid recording credentials, authorization codes, or connection tokens in application and analytics logs.
OAuth 2.0 controls
- Register the exact Onward callback URI supplied during onboarding.
- Validate
client_id,redirect_uri, the authorization code, and its expiry at the Advertiser token endpoint. - Make authorization codes short-lived and single-use.
- Return the stable
advertiser_user_idonly after the code has been validated. - Store the OAuth client secret only in backend secret storage.
- Let Onward validate and correlate
state; the Advertiser must return it unchanged from/authorize. - Never send the resulting access token to the Platform or expose it through a browser redirect.
See the OAuth 2.0 Authorization Code Flow for the complete sequence.
Signed token controls
- Treat the Onward connection token as opaque, short-lived, and single-use.
- Preserve it only for the duration of the Advertiser sign-up or sign-in flow.
- Call
POST /v1/users/connect-accountonly from the Advertiser backend. - Never accept an
advertiser_user_idfrom an unauthenticated browser and forward it without verification. - Do not retry a failed request indefinitely: expired or already-used tokens require a new connection flow.
- Do not attempt to decode or modify the token. Onward verifies its integrity, expiry, intended Advertiser, and usage.
Connection tokens typically expire quickly. Integrations must handle expiry rather than depend on an exact lifetime.
See the Signed Token Callback Flow for the complete sequence.
Redirect handling
The Platform return URL is captured at the start of the flow and validated before it is returned to the browser. Treat success and error query parameters as display information, not as authorization evidence.
- Show a clear success or failure state without rendering raw provider errors.
- Do not include secrets or tokens when forwarding telemetry from the result page.
- Handle duplicate or already-connected outcomes without creating a second identity mapping.
- On ambiguous failures, refresh the quest-chain state before offering the user another attempt.
Production checklist
- Platform and Advertiser calls originate from authenticated backend services.
- Every redirect and callback uses HTTPS outside local development.
- OAuth callback URIs match the registered value exactly.
- Secrets and tokens are excluded from URLs, logs, analytics, and error reports.
- Authorization codes and signed tokens cannot be reused.
- User identifiers come from authenticated server-side state.
- Expired and already-consumed flows restart safely.
- The Platform verifies the final state through the Onward API.