Authentication in Preview Environments
Preview environments are full deployments of your application — which means any authentication your app uses will behave exactly as it does in production, with a few things to be aware of. This guide covers what works out of the box, what requires setup, and the recommended patterns for each auth approach.
JWT and token-based auth
Stateless authentication (JWT, API keys, Bearer tokens) has no dependency on the URL your app is served from. If your app validates tokens using a secret or public key, set the relevant env var in .previewops.yaml and it works as-is:
env:
JWT_SECRET: your-preview-jwt-secret
No additional setup is needed.
OAuth and social login
OAuth requires your auth provider (Google, GitHub, Auth0, Okta, etc.) to have a list of pre-approved redirect URIs — the callback URLs your app is allowed to redirect users to after login.
In preview environments, every PR gets a different URL. For example:
https://prev-myorg-myrepo-pr42-abc123.run.apphttps://prev-myorg-myrepo-pr55-abc123.run.app
These URLs are unpredictable until the deploy happens. You cannot pre-register them one by one.
Option 1 — Disable OAuth in preview mode
The simplest and most common approach. Guard the OAuth flow behind an environment check and drop the user into a pre-authenticated state instead:
// Skip OAuth and use a mock session in preview environments
if (process.env.NODE_ENV === 'preview') {
req.session.user = { id: 'preview-user', role: 'admin' };
return next();
}
// ... normal OAuth flow
Set NODE_ENV: preview in your .previewops.yaml to activate this path.
Option 2 — Use wildcard redirect URIs
Some auth providers support wildcard patterns for redirect URIs. If your provider supports them, you can register a pattern that matches all your preview URLs:
| Provider | Wildcard support | Pattern example |
|---|---|---|
| Auth0 | ✅ Yes | https://*.fly.dev/auth/callback |
| Okta | ✅ Yes | https://*.run.app/auth/callback |
| ❌ No | Must register exact URLs | |
| GitHub OAuth | ❌ No | Must register exact URLs |
| Microsoft Entra | ⚠️ Limited | Supports wildcards in some configurations |
Check your provider's documentation for the exact syntax and any restrictions.
Option 3 — Use a stable preview callback URL
If you always deploy to the same PR number (for a long-lived feature branch, for example), the preview URL will be consistent between redeploys. You can set your auth redirect URI env var to that known URL in .previewops.yaml and register it once in your auth provider. The exact env var name depends on your auth library (e.g. NEXTAUTH_URL, AUTH0_REDIRECT_URI, OAUTH_CALLBACK_URL):
env:
NEXTAUTH_URL: https://prev-myorg-myrepo-pr99.fly.dev
This only works for stable, known URLs — it is not practical for ephemeral per-PR previews.
Session cookies
If your app issues session cookies, there are two things to check:
Cookie domain — if your app explicitly sets Domain=myapp.com on the cookie, the browser will reject it on a preview URL like prev-myorg-myrepo-pr42.fly.dev. Remove the Domain attribute (or scope it dynamically based on the request host) so the cookie is set for whatever domain the app is currently served from:
// ✅ Works on any domain including previews
res.cookie('session', token, { httpOnly: true, secure: true });
// ❌ Won't work on preview URLs
res.cookie('session', token, { domain: 'myapp.com', httpOnly: true, secure: true });
Secure flag — the Secure flag requires HTTPS. See the HTTPS section below for which providers serve HTTPS by default.
HTTPS and providers
Most OAuth providers and browser security features require HTTPS. Preview URL protocols are determined by your chosen provider:
| Protocol | Providers |
|---|---|
| HTTPS by default | Cloud Run, Fly.io, Render, Railway, AWS ECS, Azure Container Apps |
| HTTP by default | Hetzner, DigitalOcean, AWS Lightsail, Docker SSH |
If you use an HTTP-only provider and your app requires HTTPS (e.g., for OAuth callbacks or Secure cookies), add a reverse proxy such as Caddy or nginx in front of your container to handle TLS termination. See your provider's guide in providers/ for infrastructure setup.
Recommended pattern
For most teams, the recommended approach is to disable auth in preview mode and seed a known user into the session. This lets reviewers, designers, and stakeholders open the preview URL without needing a login — which is usually what you want from a preview environment anyway.
# .previewops.yaml
env:
NODE_ENV: preview
PREVIEW_BYPASS_AUTH: true
// Middleware — bypass auth when PREVIEW_BYPASS_AUTH is set
if (process.env.PREVIEW_BYPASS_AUTH === 'true') {
req.user = { id: 'preview', name: 'Preview User', role: 'admin' };
return next();
}
// ... normal auth check
If your preview needs to test the auth flow itself (e.g., the login page is part of what you are reviewing), use the wildcard redirect URI approach if your provider supports it, or pre-register a specific preview URL with your auth provider.