Authentication flow

How sessions, cookies, and identity are propagated across xcity.one and its sub-products.

The 30-second model

user logs in once on        →   xcity-home sets a host-only
www.xcity.one (GoTrue)           SameSite=Lax session cookie

user visits chat.xcity.one
  app boots →
    fetch('https://www.xcity.one/api/me/litellm-key',
          { credentials: 'include' })
    ↑ browser auto-attaches the cookie because *.xcity.one is same-site
    ↑ xcity-home returns: { key, plan, models, api_base }

  app hits tokenhub.xcity.one/v1/{models,chat/completions} with bearer
  ↑ tokenhub enforces the user's plan whitelist + budget per request

Three BFF endpoints do all the work — sub-products never call GoTrue, Stripe, or LiteLLM admin APIs directly.

Why this shape

  • Single sign-on without OAuth dance. Users log in once at www.xcity.one and their session works across every *.xcity.one sub-product, with no per-product redirect.
  • Sub-products hold no secrets. They never see the user’s password, refresh token, or LiteLLM master key. The worst-case compromise of a sub-product is a leaked short-lived inference key.
  • Centralized policy. Plan whitelists, model gating, and budget enforcement live in one place (xcity-home + LiteLLM) — sub-products can’t drift from policy.

Production vs dev

In production the session cookie is Secure, so cookies will not attach to plain http:// hosts. For local development, allow your dev origin via XCT_CORS_EXTRA_ORIGINS=http://localhost:3000.

Desktop (Electron)

Browser cookie inheritance doesn’t apply to Electron — see Guides: Desktop integration for the OAuth-style flow we use for xct-agent-desktop.

Last updated: