OAuth and sessions

Sign in with X uses OAuth 2.0 PKCE on the API server. The browser never stores X access tokens in localStorage — only an httpOnly session cookie.

Flow

Browser                    API server                 X
   |  GET /api/auth/x/start  |                        |
   |------------------------>|  redirect authorize    |
   |<------------------------|------------------------>|
   |  callback ?code=…       |  token exchange        |
   |------------------------>|  store tokens in DB    |
   |  Set-Cookie ix_session  |                        |
   |<------------------------|                        |
  1. GET /api/auth/x/start — creates PKCE verifier/challenge, stores state in oauth_states, redirects to X authorize URL.

  2. User approves on X.

  3. GET /api/auth/x/callback — validates state, exchanges code, upserts x_sessions, sets cookie, redirects to FRONTEND_URL.

  4. GET /api/auth/me — returns { signedIn, user? } from cookie session.

  5. POST /api/auth/logout — clears session row + cookie.

Environment

Variable

Purpose

X_CLIENT_ID

OAuth app client id

X_CLIENT_SECRET

Client secret

X_REDIRECT_URI

Must match X developer portal (e.g. https://liveandletdev.com/api/auth/x/callback)

FRONTEND_URL

Post-login redirect (e.g. https://liveandletdev.com)

IFIXEDX_SESSION_SECRET

HMAC signing secret (≥16 chars; required in production)

Without IFIXEDX_SESSION_SECRET in production, sign-in cannot complete (signing throws).

Token storage

Table/collection: x_sessions in ifixedx_local (RxDB on server uses SQLite or in-memory depending on deployment — see server DB bootstrap in server/index.ts).

Tokens are used server-side for:

  • GET /api/x/me

  • GET /api/x/home-timeline

  • GET /api/x/recent-search

Client behavior

  • fetch uses credentials: "include" for API calls

  • Account page shows sign-in / sign-out

  • Live timeline requires signed-in session

Production checklist

See OAuth in production.