# ADR-0072: OSHUN Customer Auth and Session Model

**Status**: Accepted **Date**: 2026-03-27 **Authors**: OSHUN Platform
Engineering, OSHUN Web Engineering, OSHUN Mobile Engineering **Reviewers**:
Security, Product, Tara, Veritas, Nyx, Arete, Nisaba leads **Supersedes**:
`docs/adr/ADR-0016-shared-identity-and-cross-domain-session-model.md`
**Superseded by**: N/A

## Context and Problem Statement

OSHUN V1 already decided that customer identity and sessions must be shared
across domains, but the current implementation is still fragmented at the exact
surface boundaries this task is meant to settle.

Today, the repo has all of these contradictions at once:

- web middleware protects customer routes with `oshun-session` / `__session`
  cookies
- the web `AuthProvider` still hydrates access and refresh tokens from
  `localStorage`
- the installable PWA is the same `apps/oshun/web` runtime, but the auth model
  is not written down as "same as web"
- mobile has no canonical customer auth runtime yet and still uses memory-backed
  profile/session-adjacent state
- the BFF currently authorizes only bearer-style dev tokens rather than the
  mixed cookie-plus-bearer boundary the product actually needs
- the shared auth package does not publish the full customer-session contract,
  and older docs still omit `nisaba`

That ambiguity blocks the next tasks in the backlog:

- sign-up / sign-in / sign-out flows
- refresh, expiration, and re-auth behavior
- device/session management
- onboarding continuity and cross-device preference sync

Without one canonical model, each of those tasks would keep re-deciding
transport, storage, expiry, and re-entry rules.

## Decision Drivers

- **One-account continuity**: a customer signs in once and keeps continuity
  across Tara, Veritas, Nyx, Arete, and Nisaba.
- **Surface fit**: web/PWA and mobile need one shared model, but not identical
  transport mechanics.
- **Security posture**: refresh tokens need rotation, revocation, and device
  scoping.
- **Implementation leverage**: the shared auth package must publish the model in
  code so web, mobile, and BFF work can converge on it.
- **Operational clarity**: session expiry, revocation, and step-up events need
  one shared taxonomy.

## Considered Options

### Option 1: Keep separate auth/session models per surface

**Description**: let web, PWA, and mobile each keep their own transport and
storage model, then align only loosely at the API layer.

**Pros**:

- Lowest short-term migration cost.

**Cons**:

- Preserves the current contradictions.
- Makes cross-device/session management much harder.
- Keeps sign-in, logout, and expiry behavior inconsistent.

### Option 2: Force every surface onto one cookie-only browser model

**Description**: make mobile mirror web/PWA by depending on cookie-backed
session state everywhere.

**Pros**:

- Simplifies one branch of BFF handling.

**Cons**:

- Poor fit for native mobile networking and secure storage.
- Makes offline/bootstrap behavior awkward on mobile.
- Treats install/runtime differences as if they do not matter.

### Option 3: One shared customer session authority with surface-specific transport rules (Chosen)

**Description**: define one customer identity and session authority for all
domains and surfaces, but explicitly split the transport/storage boundary by
surface: cookie-backed same-origin BFF sessions for web/PWA, secure bearer-token
flows for mobile.

**Pros**:

- Matches real runtime constraints without fragmenting the identity model.
- Gives follow-on tasks one normative contract.
- Keeps security rules strong and explicit.

**Cons**:

- Requires web, mobile, and BFF implementation cleanup in follow-on tasks.
- Requires older auth docs and helper code to be brought into conformance.

## Decision Outcome

**Chosen option**: Option 3.

### Normative Identity Rule

OSHUN customers have one canonical identity and one customer-session authority
across:

- `web`
- `pwa`
- `mobile`

That identity applies across all customer domains:

- `tara`
- `veritas`
- `nyx`
- `arete`
- `nisaba`

Domain surfaces do not own independent customer identities or refresh-token
families.

### Normative Session Artifacts

Every authenticated customer session is defined by the same core artifacts:

- `oshun_user_id` / token `sub`
- `session_id` / token `sid`
- short-lived access token
- rotating refresh token family
- server-owned per-device session registry record

The canonical access-token target lifetime is 10-15 minutes.

Refresh tokens are long-lived, device-scoped, rotating, and server-revocable.

### Surface Transport Rule

The transport boundary is canonical by surface:

- `web`: same-origin cookie-backed customer session to the OSHUN web/BFF runtime
- `pwa`: same as web, because the installable PWA is the same browser-origin
  runtime
- `mobile`: bearer access token to the BFF/API layer

The BFF is the stable trust boundary between shells and downstream domain/data
services.

### Storage Rule

- `web` and `pwa`
  - refresh/session cookie lives in `HttpOnly`, `Secure`, same-origin cookies
  - access token is ephemeral runtime state only
  - long-lived customer auth tokens must not be persisted in `localStorage`
- `mobile`
  - refresh token and customer session metadata live only in secure encrypted
    device storage
  - access token may be cached only in secure storage and memory, never in plain
    AsyncStorage

### Session State Taxonomy

Customer session state is canonicalized as:

- `active`
- `refresh-required`
- `reauth-required`
- `revoked`
- `expired`

This taxonomy is the shared contract for web, mobile, BFF, telemetry, and
device-management surfaces.

### Assurance Rule

The canonical customer assurance levels are:

- `anonymous`
- `standard`
- `step-up`

`step-up` means the customer must satisfy additional verification before a
sensitive action proceeds, but it does not create a second identity model.

### Re-Authentication Rule

Re-authentication is required only for explicit policy events, not because the
customer switched domains. The canonical re-auth triggers are:

- refresh token expired
- session revoked or refresh reuse detected
- step-up required for a sensitive action
- suspicious account or device activity
- sensitive account-change confirmation

### BFF Boundary Rule

The BFF must converge on this contract:

- web/PWA requests are authenticated through same-origin cookie session
  validation and any required access-token exchange/introspection happens behind
  that boundary
- mobile requests are authenticated through bearer access tokens
- downstream domain adapters do not own refresh tokens and do not become the
  customer session authority

### Customer Entry and Continuity Rule

The customer auth/session model is continuous across:

- public marketing/app entry
- `/welcome`
- `/onboarding`
- authenticated shell routes
- domain entry and cross-domain switching

Onboarding is customer-profile capture inside the same session model, not a
parallel authentication model.

### Device and Session Management Rule

The canonical session registry is per device/session. Customers must be able to:

- list active sessions/devices
- identify the current session
- revoke a single session
- revoke all sessions
- see enough metadata to understand device and surface context

That metadata includes, at minimum, session id, timestamps, device identity, and
surface/runtime context.

### Shared Package Rule

`libs/oshun/auth` is the canonical shell-facing customer auth contract package.

It must publish:

- shared customer auth/session types
- surface-specific policy publication
- shared session state and assurance taxonomy
- session/device metadata contract
- secure storage helpers that preserve canonical session metadata

### Explicit Non-Conformance Note

As of this ADR's publication, the current implementation is knowingly out of
conformance in several places:

- web client auth still uses `localStorage`
- mobile customer auth runtime is not fully implemented
- BFF auth middleware is still dev-token oriented

Those mismatches are intentional follow-on scope for:

- `V1-CSH-042`
- `V1-CSH-043`
- `V1-CSH-044`

This ADR settles the contract those tasks must implement.

## Architecture Implications

- the web customer shell must converge from localStorage token hydration to the
  cookie-plus-memory model
- the installable PWA must inherit the exact same auth/session contract as web
- mobile must add a real secure-session runtime rather than memory-only customer
  auth behavior
- BFF middleware must support cookie-backed web/PWA auth and bearer mobile auth
  under one shared customer-session authority
- device/session management surfaces must be backed by the canonical session id
  and per-device registry model

## Acceptance Criteria

`V1-CSH-041` is complete only when all criteria below are true:

1. A canonical ADR supersedes the older cross-domain session ADR.
2. The shared auth package publishes the customer auth/session model in code.
3. Shared auth types include the session-state, assurance, surface, transport,
   storage, and device/session metadata contract.
4. Secure auth session persistence no longer drops canonical session metadata.
5. Documentation explicitly records current non-conforming implementations for
   follow-on tasks.

## Related Decisions

- `docs/adr/ADR-0014-oshun-web-and-pwa-strategy.md`
- `docs/adr/ADR-0016-shared-identity-and-cross-domain-session-model.md`
- `docs/adr/ADR-0062-oshun-mobile-shell-relationship-model.md`
- `docs/adr/ADR-0063-oshun-customer-admin-shell-relationship-model.md`

## References

- `apps/oshun/web/src/middleware.ts`
- `apps/oshun/web/src/lib/auth-context.tsx`
- `apps/oshun/mobile/src/network/oshun-bff-client.ts`
- `apps/oshun/mobile/src/profile/store.ts`
- `apps/oshun/bff/src/middleware/authz.ts`
- `libs/oshun/auth/src/types.ts`
- `libs/oshun/auth/src/customer-auth-model.ts`
- `libs/oshun/auth/src/session-store.ts`
