OAuth passthrough
OAuth passthrough
Section titled “OAuth passthrough”One pre-registered OAuth callback serves every ephemeral preview URL. Google, GitHub, Microsoft, and Slack all work out of the box with standard Auth.js apps — no per-PR redirect-URI registration.
The problem
Section titled “The problem”OAuth providers require a pre-registered redirect_uri (exact-match). Previews run on ephemeral hosts — prev-abc.your-workspace.preview.destesi.io, prev-def…, and so on. Registering one per preview is impractical; using a wildcard isn’t supported by most providers.
The solution
Section titled “The solution”Two pieces cooperate:
- The runner sets
AUTH_REDIRECT_PROXY_URLon your app to the central Destesi base ending in/v1/oauth. Auth.js v5 appends/callback/<provider>to it, so theredirect_uriyour app sends to the provider is…/v1/oauth/callback/<provider>— the one URL you pre-register with each provider. The overlay does not rewriteredirect_uriitself (the two OAuth legs have to agree, and the token-exchange leg isn’t a redirect the overlay can intercept). - The overlay proxy (which sits in front of every preview) intercepts outbound 302s to known OAuth authorize endpoints and replaces the
statequery param with a destesi-signed envelope carrying the original app state plus(preview_id, provider, callback_path, nonce, exp).
After the provider authenticates, it returns to the central API, which unwraps the envelope, validates the signature and expiry, and 302s the browser back to the preview’s real /api/auth/callback/<provider> with the original app state restored.
Supported providers
Section titled “Supported providers”| Provider | Authorize host & path |
|---|---|
accounts.google.com/o/oauth2/v2/auth |
|
| GitHub | github.com/login/oauth/authorize |
| Microsoft | login.microsoftonline.com/common/oauth2/v2.0/authorize |
| Slack | slack.com/oauth/v2/authorize |
Matching is host-exact plus path-exact (or a subpath of the listed path). Other providers — and authorize URLs on other paths — pass through unchanged.
App configuration
Section titled “App configuration”For a typical Auth.js v5 / NextAuth app:
- Set
trustHost: truein your Auth.js config so it honors theX-Forwarded-*headers the overlay sets. - Do not set
NEXTAUTH_URLin the preview env. WithtrustHost: true, Auth.js reads the preview’s own forwarded host — settingNEXTAUTH_URLpins one URL and breaks previews. - Your app’s real callback path must follow Auth.js v5 convention:
/api/auth/callback/<provider>. Custom paths are supported via the preview’soauth_jsonfield.
Disabling
Section titled “Disabling”Overlay OAuth interception is disabled if DESTESI_OAUTH_STATE_SECRET is unset at preview startup. The overlay logs a warning and passes OAuth redirects through unchanged — your app receives the original state and must then handle provider callbacks at its own URL (requires per-preview redirect-URI registration, not recommended).
Security notes
Section titled “Security notes”- The signed state envelope is HMAC-signed with
DESTESI_OAUTH_STATE_SECRET; tampering fails validation and the callback is rejected. - Envelopes carry a nonce + expiry so a replay won’t authenticate a different preview.
- The central API verifies the preview still exists and is not
deletedbefore emitting the final redirect. - The app’s original
stateis passed through unchanged (it’s just wrapped), so your app’s own state verification still works end-to-end.