Security
OAuth redirect URIs
Register every callback URL Envoy exposes for SSO and connector OAuth, forward the public origin correctly, and diagnose mismatches.
OAuth redirect URIs
Envoy exposes OAuth callbacks in two places: SSO sign-in (Microsoft Entra ID and Google) and connector account connection for catalog integrations. Each provider must allow the exact callback URL Envoy sends during authorization and token exchange.
SSO callbacks
The sign-in flow lives at /api/auth/oauth/:provider with the callback at /api/auth/oauth/:provider/callback (src/server/api/auth.ts). Register one callback per provider and public Envoy origin:
https://envoy.example.com/api/auth/oauth/microsoft/callback
https://envoy.example.com/api/auth/oauth/google/callbackPaths and provider names are lowercase. Scheme, host, port, and path must match exactly.
The provider config keys in envoy-server.yaml:
auth:
providers:
microsoft:
client_id: ${MICROSOFT_CLIENT_ID}
client_secret: ${MICROSOFT_CLIENT_SECRET}
tenant_id: common
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}client_id and client_secret are required per provider; tenant_id is optional and restricts Microsoft sign-in to a tenant. A provider appears on the sign-in page only when configured.
How Envoy builds the redirect URI
For both the authorization request and the token exchange, getOAuthRedirectUri derives the origin from the incoming request:
x-forwarded-protoheader when present, otherwise the request scheme;x-forwarded-hostheader when present, otherwise the request host;- path
/api/auth/oauth/<provider>/callback.
The authorization start request and the callback must resolve to the same public origin.
Connector OAuth callback
Connecting an account for a catalog integration uses a separate browser return URL (src/server/api/cardamom-connect.ts). Unless the request supplies its own redirect_uri, Envoy builds it from the resolved public origin:
https://envoy.example.com/connectors/connect-callbackThe public origin is resolved in this order: the ENVOY_PUBLIC_ORIGIN environment variable, then saffron.public_origin in server config, then http://<server.host>:<server.port> (with 0.0.0.0 mapped to 127.0.0.1). Behind a proxy or Docker port map, set ENVOY_PUBLIC_ORIGIN to the browser-facing URL so connect flows return to the right place.
Reverse-proxy configuration
Terminate TLS at an approved proxy and forward the original values:
X-Forwarded-Proto: https
X-Forwarded-Host: envoy.example.comDo not forward an untrusted client-supplied host unchanged. Configure the proxy with an allowlisted public host and replace inbound forwarding headers. If the application is exposed on a non-default port, include it in both the forwarded host and the registered redirect URI.
Provider setup
For Microsoft Entra ID: create a web application registration, register the Microsoft callback URI, configure client ID and secret, and set tenant_id when access should be tenant-restricted.
For Google: create an OAuth web client, register the Google callback URI, configure client ID and secret, and configure the consent screen.
Store client secrets through deployment secret management — never in documentation, browser code, or task YAML.
Multiple environments
Register each environment separately:
https://envoy-dev.example.com/api/auth/oauth/google/callback
https://envoy.example.com/api/auth/oauth/google/callbackPrefer separate OAuth clients for development and production so credentials, consent, and revocation are isolated.
Verify safely
- Open the public HTTPS login page and start sign-in for the provider.
- Inspect the provider's
redirect_uriquery parameter and confirm it matches an allowlisted URI exactly. - Complete sign-in and verify the callback returns to the expected origin.
- Repeat through every production proxy or hostname.
Troubleshooting
redirect_uri_mismatch— compare the URI character for character against the provider registration.- Internal HTTP host appears in the URI — the proxy is not forwarding
X-Forwarded-Proto/X-Forwarded-Host. - Wrong tenant — verify the Microsoft
tenant_idand account policy. - Callback loops to login — verify the browser sign-in state, HTTPS, proxy headers, and clock synchronization.
- Connect flow returns to the wrong host — set
ENVOY_PUBLIC_ORIGIN.
OAuth state and PKCE protections do not compensate for accepting an attacker-controlled forwarded host. Validate proxy trust boundaries.
Related
- Hardening, RBAC, and secrets — the broader auth model
- Server configuration — the
auth.providerskeys - Catalog connectors and connected accounts — the connect flow this callback serves