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:

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
Google ❌ 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.


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.