# Shell: Auth and session

Source: `apps/oshun/web/src/proxy.ts` (`PUBLIC_PATHS` at proxy.ts:14; Next 16 —
there is no `middleware.ts`), `apps/oshun/web/src/lib/auth-context.tsx`,
`apps/oshun/web/src/lib/providers.tsx` (`AuthProvider`),
`apps/oshun/bff/src/routes/...auth*`, `@oshun-web/isis-entitlements`
(`AAA_ONLY_STUDIO_ROUTES`, `resolveStudioBoundary`)

Where auth lives in the customer surface, what guards what, and how a user moves
between anonymous / signed-in / role-gated states.

## Auth states

- [ ] **Anonymous** — no session cookie; can view PUBLIC_PATHS only
- [ ] **Signed-in** — valid session; standard user; access to customer surface
- [ ] **Signed-in + AAA tier** — access to AAA_ONLY_STUDIO_ROUTES
- [ ] **`/operator/*`** — session-gated only; the proxy has no operator-role
      gate (a signed-in non-operator is not blocked at the proxy)
- [ ] **`/(workspace)/*`** — session-gated only; the proxy has no tenant /
      workspace-member gate
- [ ] **Session expired** — `proxy.ts` redirects to `/welcome` with
      `?redirect=...&expired=1` preserving the deep link

## Public paths (no auth required)

From `proxy.ts`'s `PUBLIC_PATHS` (proxy.ts:14):

- [ ] `/opengraph-image`, `/twitter-image`
- [ ] `/welcome`, `/welcome/domains`, `/welcome/download`
- [ ] `/welcome/opengraph-image`, `/welcome/twitter-image`
- [ ] `/legal/privacy`, `/legal/terms`, `/legal/cookies`,
      `/legal/accessibility`, `/legal/ccpa`, `/legal/dpa`
- [ ] `/landing`, `/v3/landing`, `/legal/lilith`, `/legal/lilith/privacy`,
      `/status`
- [ ] `/lilith` (design system showcase), `/system`, `/system/edge-states`,
      `/system/emails`, `/system/og`, `/system/verify`
- [ ] `/studio/generation/living-scene` (customer-tier Living Scene)
- [ ] `/studio/compose`
- [ ] `/v2`, `/v2/wiki`, `/v2/glossary`, `/v2/roadmap` (V2 public shell +
      Sophia-backed knowledge surfaces)

`PUBLIC_PATHS` is 30 exact entries in total. And `PUBLIC_PREFIXES` (15): `/_next`,
`/api`, `/icons`, `/images`, `/fonts`, `/screenshots`, `/scene`,
`/studio/compose`, `/og`, `/.well-known`, `/manifest.json`, `/robots.txt`,
`/sitemap.xml`, `/favicon`, `/sw.js`.

For each entry above, verify in dev:

- [ ] Anonymous user can load the route (200, not 401)
- [ ] No information that would require auth leaks into the response

## Sign-in flow

- [ ] **Entry: `/welcome`** — primary sign-in CTA
- [ ] **Magic link** — email entry → email sent → click in mail → return to
      original `?redirect=` route (welcome page reads `searchParams.redirect`,
      welcome/page.tsx:72)
- [ ] **OAuth provider(s)** — each available provider button works (Google,
      Apple, …)
- [ ] **Password reset** — request → email → reset form → sign-in
- [ ] **Sign-up flow** — name/email/password → email verification → onboarding
- [ ] **Error: invalid credentials** — clear message; no email enumeration
- [ ] **Error: rate-limited** — clear message; backoff communicated
- [ ] **Error: provider down** — graceful fallback to alt provider or email link

## Sign-in success

- [ ] **`?redirect=` parameter** honored on first nav after sign-in
- [ ] **Default landing** — `/` (home) when no `?redirect=`
- [ ] **Session cookie** set with `HttpOnly`, `SameSite=Lax`, `Secure` (in prod)
- [ ] **Auth state** propagates to all open tabs (BroadcastChannel)

## Session refresh

- [ ] **Silent refresh** — happens before token expiry; user not interrupted
- [ ] **Refresh failure** → user signed out cleanly; redirect to sign-in
- [ ] **Returning after long idle** — session restored if refresh token valid

## Sign-out

- [ ] **Profile menu → Sign out** — confirms in dialog; clears session; clears
      IndexedDB / local data
- [ ] **Sign-out everywhere** — option to invalidate all sessions
- [ ] **SW caches** — auth-scoped caches cleared on sign-out
- [ ] **PWA relaunch target** cleared on sign-out

## AAA-only studio routes

From `@oshun-web/isis-entitlements`.

- [ ] **Non-AAA user on AAA route** — gated UI with upgrade CTA; URL preserved
- [ ] **AAA upgrade flow** at `/aaa-upgrade` — entry from gated routes
- [ ] **Post-upgrade** — return to originally-attempted route

## Role-based gates

- [ ] **Operator routes** (`/operator/*`) — session-gated only in the proxy; no
      operator-role gate (a role gate appears absent — possible gap vs intended
      scope)
- [ ] **Workspace routes** (`/(workspace)/*`) — session-gated only in the proxy;
      no tenant / workspace-member gate
- [ ] **Studio routes** (`/studio/*`) — base entitlement varies per area; the
      proxy enforces the §24.11 AAA-only Studio Isis boundary
      (`AAA_ONLY_STUDIO_ROUTES`, `resolveStudioBoundary`)
- [ ] **403 / 404 UI** — clean denial; explains why; no info leak

## Cross-tab sync

- [ ] **Sign-in in one tab** — other tabs update auth state within 1 s
- [ ] **Sign-out in one tab** — other tabs sign out
- [ ] **Token refresh** — coordinated; not duplicated per-tab

## Cookie consent gate

`CookieConsentBanner.tsx` — analytics/marketing cookies only.

- [ ] **First visit** — banner visible at bottom
- [ ] **Essential cookies** — set regardless of consent (session, csrf)
- [ ] **Analytics cookies** — set only after accept
- [ ] **Reject** — only essential cookies present
- [ ] **Consent persisted** — banner doesn't reappear
- [ ] **Re-open consent** from profile/settings → Privacy

## Cross-references

- [02-routing-layouts.md](./02-routing-layouts.md) — proxy composition
- [03-pwa-behavior.md](./03-pwa-behavior.md) — `/api/auth/*` is network-only
- Feature spec: [`V1/features.md`](../../V1/features.md#iris-memory-and-identity)
- Architecture: [`V1/ARCHITECTURE.md`](../../V1/ARCHITECTURE.md#iris)

## Open questions / known gaps

- [ ] List of OAuth providers actually configured in V1
- [ ] Document the exact AAA entitlement check for each `AAA_ONLY_STUDIO_ROUTES`
      entry
- [ ] Confirm tenant-admin auth lives in `apps/oshun/tenant-admin` only, not in
      this app
