Today Platform Web — Dev Docs
Architecture

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 (from todayai-labs/today-admin)
  • auth.today.ai: auth service origin resolved by resolveAuthBaseUrl()
  • api.today.ai: API origin resolved by resolveApiBaseUrl()

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.

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 at betterAuthConfig.baseURL
  • apps/web/src/data/user/session-query.ts: session reads call authClient.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 incoming cookie, authorization, content-type, accept, user-agent, accept-language, and origin

/api/token also depends on the browser cookie being available on the app origin:

  • apps/web/src/hooks/use-session.ts: calls fetch('/api/token', { credentials: 'include' })
  • apps/web/src/app/api/token/route.ts: reads request.headers.get('cookie') and request.headers.get('authorization'), then forwards them to ${AUTH_BASE_URL}/api/token

In other words, the current apps/web flow assumes:

  1. The browser has a valid session cookie.
  2. That cookie is sent on requests to today.ai.
  3. The web app forwards that cookie to auth.today.ai through /api/auth/* and /api/token.

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.

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.ts
    • authority is resolveAuthBaseUrl()
    • authorization_endpoint points directly to ${authority}/api/auth/oauth2/authorize
    • token_endpoint is proxied through ${appOrigin}/api/auth/token
    • userinfo_endpoint is proxied through ${appOrigin}/api/auth/oauth2/userinfo
  • src/data/user/session-query.ts: fetches /api/auth/oauth2/userinfo with Authorization: Bearer <access_token>

Admin proxy routes still forward cookies when present:

  • src/app/api/auth/[...all]/route.ts: proxies /api/auth/* to the auth service
  • src/app/api/auth/[...all]/utils.ts: forwards incoming cookie and authorization
  • src/app/api/token/route.ts: forwards incoming cookie and authorization to ${AUTH_BASE_URL}/api/token
  • src/hooks/use-session.ts: calls fetch('/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.

On this page