Auth cookie current state
Current-state map of cookie, domain, and auth relationships for `apps/web`. The admin app lives in `todayai-labs/today-admin`; its parallel surface is covered here for context. Cites code paths inline; update this page when the cited paths move.
This page maps the current cookie, domain, and auth relationship for
apps/web — what the app assumes about session cookies, where the proxy
boundary sits, and how OIDC fits in. It cites code paths inline; if a path
moves, update the citation here.
The admin console runs the same patterns from
todayai-labs/today-admin (it
was extracted from this repo in June 2026; see
#749). The
"admin app" sections below describe that parallel surface for context — the code
lives in the sibling repo.
This is a descriptive page, not a target design. Forward-looking proposals (BFF-only, cookie-free admin, etc.) belong in design docs or PRs, not here.
Current domain relationship
today.ai: main web app (apps/web)admin.today.ai: admin app (fromtodayai-labs/today-admin)auth.today.ai: auth service origin resolved byresolveAuthBaseUrl()api.today.ai: API origin resolved byresolveApiBaseUrl()
The same shape is used in dev docs with todayai.dev equivalents. The web repo treats auth and API as separate origins, then adds app-origin proxy routes where needed.
Main web app: session cookie is still foundational
The main web app uses Better Auth against the app origin, not directly against auth.today.ai from the browser:
apps/web/src/lib/better-auth-client.ts:createAuthClient({ baseURL: betterAuthConfig.baseURL, fetchOptions: { credentials: 'include' } })apps/web/src/lib/session-auth-client.ts: lightweight session client also points atbetterAuthConfig.baseURLapps/web/src/data/user/session-query.ts: session reads callauthClient.getSession()
That app-origin auth traffic is proxied server-side to the auth service:
apps/web/src/app/api/auth/[...all]/route.ts: proxies/api/auth/*to${AUTH_BASE_URL}/api/auth/*apps/web/src/app/api/auth/[...all]/utils.ts: forwards incomingcookie,authorization,content-type,accept,user-agent,accept-language, andorigin
/api/token also depends on the browser cookie being available on the app origin:
apps/web/src/hooks/use-session.ts: callsfetch('/api/token', { credentials: 'include' })apps/web/src/app/api/token/route.ts: readsrequest.headers.get('cookie')andrequest.headers.get('authorization'), then forwards them to${AUTH_BASE_URL}/api/token
In other words, the current apps/web flow assumes:
- The browser has a valid session cookie.
- That cookie is sent on requests to
today.ai. - The web app forwards that cookie to
auth.today.aithrough/api/auth/*and/api/token.
Why shared parent-domain cookie is assumed today
The current docs in this repo explicitly describe the shared-cookie assumption:
- Auth interaction architecture: the social login callback lands on the auth origin, and even with a BFF proxy the session must remain visible after redirecting back to the app
- Local development: documents the concrete dev flow where Google redirects to
auth.todayai.dev, the auth server sets a cookie on.todayai.dev, and the browser later sends that cookie on app requests - Environment variables: repeats the same constraint and calls out cookie-domain alignment as required for login to keep working
This means the current web login flow is not purely "same-origin BFF only". The browser still visits the auth origin during social/OIDC redirects, and the repo documentation assumes the resulting session remains usable when the browser returns to the app origin.
Admin app: OIDC bearer flow first, cookie forwarding still exists
The admin app (in todayai-labs/today-admin)
mixes OIDC bearer tokens with the same app-origin proxy pattern. The code
paths below refer to that repo's layout:
src/lib/oidc-config.tsauthorityisresolveAuthBaseUrl()authorization_endpointpoints directly to${authority}/api/auth/oauth2/authorizetoken_endpointis proxied through${appOrigin}/api/auth/tokenuserinfo_endpointis proxied through${appOrigin}/api/auth/oauth2/userinfo
src/data/user/session-query.ts: fetches/api/auth/oauth2/userinfowithAuthorization: Bearer <access_token>
Admin proxy routes still forward cookies when present:
src/app/api/auth/[...all]/route.ts: proxies/api/auth/*to the auth servicesrc/app/api/auth/[...all]/utils.ts: forwards incomingcookieandauthorizationsrc/app/api/token/route.ts: forwards incomingcookieandauthorizationto${AUTH_BASE_URL}/api/tokensrc/hooks/use-session.ts: callsfetch('/api/token', { credentials: 'include', headers: { Authorization: ... } })
So the current admin picture is:
- interactive login starts with OIDC redirects against the auth origin
- user/session reads are bearer-token based
- app-origin proxy routes still preserve cookie forwarding behavior instead of assuming a cookie-free model
Current-state conclusion
In the current web repo, /api/auth/* and /api/token are implemented as thin app-origin proxies to the auth service, and both preserve browser cookies when available. The main web app still depends directly on that session cookie path. The admin app is more bearer-token-oriented, but it has not removed cookie forwarding from the same proxy surface.
Auth interaction architecture
Why this repo's auth server stays headless and delegates login/consent/account-selection screens to `apps/web`, and why those calls go through a same-origin BFF proxy instead of hitting the auth server directly.
Better-Auth quirks
The `oauthProviderClient` plugin auto-injects `oauth_query` into non-GET request bodies. What that means in practice and the workaround for calls that shouldn't progress the OAuth flow.