Files
zopu-code/docs/auth-proxy.md
2026-08-04 01:38:48 +05:30

3.6 KiB

Auth Proxy — Production Ingress Requirement

The application uses same-origin authentication: the browser and React Router SSR both hit /api/auth/* on the public application domain. This keeps cookies first-party, avoids cross-origin credentials, and gives development and production the same API surface.

Required production route

At the public application domain, route:

Prefix Target
/api/auth/* Convex HTTP site
/* React Router frontend

Caddy

zopu.example.com {
    handle /api/auth/* {
        reverse_proxy https://joyous-cat-297.convex.site {
            header_up Host joyous-cat-297.convex.site
        }
    }

    handle {
        reverse_proxy frontend:3000
    }
}

Dokploy / Traefik

Create a higher-priority path router for /api/auth that forwards to the external Convex site URL. Ensure it:

  • Preserves the original browser Cookie header
  • Passes Set-Cookie responses back (rewrite domain if Convex emits an explicit one)
  • Preserves the original method and body
  • Forwards X-Forwarded-Host and X-Forwarded-Proto
  • Passes the full path unchanged (e.g. /api/auth/convex/token)
  • Does not cache auth responses
  • Allows OAuth callback routes under the same prefix

Convex environment

The Convex deployment SITE_URL must match the public origin users visit, not the Convex site URL:

# Production
npx convex env set SITE_URL 'https://zopu.example.com'

# Local
npx convex env set SITE_URL 'http://100.101.157.28:5173'

# Staging
npx convex env set SITE_URL 'https://zopu-staging.vercel.app'

This value is Better Auth's public baseURL and trustedOrigins. It must be the browser origin because Better Auth writes OAuth state cookies and receives provider callbacks through the same-origin proxy. CONVEX_SITE_URL remains the internal Convex HTTP site URL.

One origin per shared OAuth deployment

GitHub OAuth Apps expose one authorization callback URL. The value must exactly equal <SITE_URL>/api/auth/callback/github, including scheme, host, port, and path. A GitHub authorization request whose redirect_uri differs returns Invalid Redirect URI before the user can grant access.

SITE_URL is also single-valued in a Convex deployment. Therefore, two browser origins that share one deployment—such as Tailscale development and Puter staging—cannot use GitHub OAuth at the same time. Either switch both the Convex SITE_URL and the GitHub OAuth App callback together before testing the other origin, or give each public environment its own Convex deployment and OAuth App.

Never set Better Auth's baseURL to CONVEX_SITE_URL for the browser flow. The browser creates the OAuth state cookie on SITE_URL; a provider callback sent directly to CONVEX_SITE_URL cannot read that cookie and fails with state_mismatch.

Why same-origin

  1. First-party cookies. No SameSite=None, no third-party cookie restrictions.
  2. SSR consistency. The React Router server uses the same auth surface the browser sees; no cross-host credential translation.
  3. No CORS complexity. The browser talks only to its own origin.
  4. The reverse proxy handles the cross-host hop server-side.
  • Better Auth: Trusted Origins
  • apps/web/vite.config.ts — Vite dev proxy (/api/auth → Convex site)
  • packages/auth/src/web/auth-client.tswindow.location.origin
  • apps/web/src/lib/auth.server.ts — SSR token loader via request origin