Deployment
Browser origins, HTTPS development, subdomains, proxies, and one backend serving two frontends.
Deployment
Every topology uses the same CSRF client contract: bootstrap JSON with credentials, keep the token in memory, echo it on mutations, and accept replacement response headers. Session, refresh, challenge, and CSRF cookies are HttpOnly. You do not need a readable cookie or a CSRF domain override.
One backend, app and admin on sibling subdomains
For app.example.com, admin.example.com, and api.example.com, use:
goauth.WithBrowser(goauth.BrowserConfig{
APIURL: "https://api.example.com",
AllowedOrigins: []string{
"https://app.example.com",
"https://admin.example.com",
},
Cookies: goauth.CookieConfig{
Name: "goauth_session",
RefreshName: "goauth_refresh",
ChallengeName: "_2fa_challenge",
AdminName: "goauth_admin_session",
AdminRefreshName: "goauth_admin_refresh",
AdminChallengeName: "_goauth_admin_2fa_challenge",
Domain: "",
Path: "/",
SameSite: http.SameSiteLaxMode,
Secure: goauth.SecureAlways(),
},
})Import net/http. These are different origins but the same site when their scheme matches. CORS permits the listed frontends to read the bootstrap JSON and rotation header. Set the frontend API base to https://api.example.com and send credentials: "include". Keep Domain empty: cookies go to the API, which is enough for both clients to authenticate.
App and admin sessions are independent even when both frontends call this API host directly. The app uses the default context. The panel automatically sends X-GoAuth-Session: admin on every request, including CSRF bootstrap. The API selects separate session, refresh, challenge, CSRF, and browser binding cookies. Logging in or out of one frontend leaves the other's current session intact. Revoking all sessions and other account-wide security operations still affect both.
For the Vite panel, build with VITE_API_URL=https://api.example.com and host its static dist/ output at https://admin.example.com, with SPA fallback to index.html. This environment value is the request URL prefix; /auth/admin/login becomes https://api.example.com/auth/admin/login. It is public build-time configuration, not a secret. Changing it requires rebuilding the panel. No frontend reverse proxy is required.
Same-origin frontend proxy
Use a relative API base in the client, such as "" when /auth is proxied, or /api when the proxy strips that prefix. The frontend host owns the cookies. The backend still requires the original frontend origin in AllowedOrigins. The same JSON bootstrap works; no browser-specific CSRF flags are needed.
This is an optional deployment alternative. Preserve Origin, X-GoAuth-Session, and all Set-Cookie headers, and avoid caching authentication responses. For client IP recording and rate limiting, trust only your actual proxy addresses.
Different sites
For a panel at https://panel.acme.com calling https://api.example.com, configure:
goauth.WithBrowser(goauth.BrowserConfig{
APIURL: "https://api.example.com",
AllowedOrigins: []string{"https://panel.acme.com"},
Cookies: goauth.CookieConfig{
SameSite: http.SameSiteNoneMode,
Secure: goauth.SecureAlways(),
},
})Cookie domains cannot span unrelated sites. JSON already supplies the CSRF token, so there is no extra token-delivery option. Browser privacy settings can still block the credential cookies; SameSite=None does not override third-party-cookie blocking. Prefer a same-origin proxy when you need reliable support across those browser policies.
HTTP development
goauth.WithApp(goauth.AppConfig{
Name: "My app",
BaseURL: "http://localhost:3000",
Environment: goauth.EnvironmentDev,
Database: goauth.DatabaseConfig{DB: db, Driver: goauth.DriverPostgres},
}),
goauth.WithBrowser(goauth.BrowserConfig{
APIURL: "http://localhost:8080",
AllowedOrigins: []string{
"http://localhost:3000",
"http://localhost:5173",
},
Cookies: goauth.CookieConfig{
SameSite: http.SameSiteLaxMode,
Secure: goauth.SecureNever(),
},
}),These are options inside NewConfig, alongside your secret and other required settings. Secure: nil would also derive false for this HTTP development API. Point both frontends at http://localhost:8080; use VITE_API_URL=http://localhost:8080 for the panel. Bare localhost on different ports works because the two contexts use different cookie names, even though ports do not isolate cookies. Use the same hostname consistently. Genuine cross-site development needs HTTPS and secure SameSite=None cookies. Insecure SameSite=None is rejected even in development because browsers reject that combination.
Outside EnvironmentDev, API and frontend origins must use HTTPS and cookies must be secure. SecurityConfig.AllowHTTPURLs controls email links separately and does not enable HTTP cookies.
Bootstrap and troubleshoot
- Always send credentials to bootstrap and API calls. A bootstrap success does not prove a browser accepted cookies; the first mutation checks both channels.
csrf_headers_missingmeans neither origin header was available. Preserve browser origin through proxies; non-browser HTTP clients must send an allowed origin too.- Token missing/mismatch/invalid errors can bootstrap and retry once. Repeated failure usually means cookies are blocked, scoped incorrectly, or sent to a different host.
- A 401 remains an authentication failure; a 500 remains a backend failure. Neither is a reason to replay arbitrary writes.
- All API replicas must share the same root secret, database, browser configuration, and public deployment policy.
See full browser configuration, client wrapper, and migration instructions.
Redis deployment
For complete Redis configuration, version-aware rollout, isolated counter capacity, ACL/TLS, failure behavior and rollback, see Redis deployment.