80 lines
3.6 KiB
Markdown
80 lines
3.6 KiB
Markdown
# 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
|
|
|
|
```caddy
|
|
zopu.example.com {
|
|
handle /api/auth/* {
|
|
reverse_proxy https://befitting-dalmatian-161.convex.site {
|
|
header_up Host befitting-dalmatian-161.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:
|
|
|
|
```bash
|
|
# 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.cheaptricks.puter.wtf'
|
|
```
|
|
|
|
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.**
|
|
|
|
## Related
|
|
|
|
- [Better Auth: Trusted Origins](https://github.com/better-auth/better-auth/blob/main/docs/content/docs/reference/security.mdx)
|
|
- `apps/web/vite.config.ts` — Vite dev proxy (`/api/auth` → Convex site)
|
|
- `packages/auth/src/web/auth-client.ts` — `window.location.origin`
|
|
- `apps/web/src/lib/auth.server.ts` — SSR token loader via request origin
|