Skip to content

Auth Architecture

Document: docs/product/auth-architecture.md
Status: Canonical
Last updated: 2026-04-06
Authority: Tim Rignold, RTOpacks Pty Ltd


Auth Flow

User visits my.rtopacks.com.au
  Middleware checks rtp_session cookie
   ┌────┴────┐
   │ No      │ Yes
   │ cookie  │
   ▼         ▼
 /auth    Route handler
   │      (KV validation
   │       at page level)
 Email form
 POST /api/auth/magic-link
   ├── Check access_allowlist (ops-db)
   │   ├── Not listed → "invitation only" message (no email sent)
   │   └── Listed → continue
   ├── Generate 32-byte token → KV (magic:{token}, 15min TTL)
   └── Send via Resend (noreply@rtopacks.com.au)
 User clicks link → GET /auth/verify?token=...
        ├── Validate token in KV (single use — deleted immediately)
        ├── Upsert user in workspace-db
        ├── resolveUserTier(email, userId, workspaceDb, opsDb)
        ├── Build RTPSession with resolved ucca_layer
        ├── Write session to KV (session:{id}, 24hr TTL)
        ├── Log session_started to platform_audit_log
        └── Set-Cookie: rtp_session={id} → redirect to /

Session Model

interface RTPSession {
  session_id: string           // UUID
  user_id: string              // UUID — from workspace-db users table
  email: string
  ucca_layer: 1 | 2 | 3 | 4 | 4.5
  world_id: string             // 'au-vet'
  tenant_id: string            // org identifier
  org_id: string | null
  role_template_id: string | null
  permissions: string[]
  seat_limit_reached: boolean
  identity_source: 'ucca_managed' | 'federated' | 'stepdown'
  impersonation: boolean
  impersonating_layer: number | null
  created_at: number           // epoch ms
  expires_at: number           // epoch ms (24hr from creation)
  last_active: number          // epoch ms (slid on each access)
}

Cookie: rtp_session — HttpOnly, Secure, SameSite=Strict, Max-Age=86400, Domain=my.rtopacks.com.au

KV storage: session:{session_id} in SESSION_KV namespace (293964a187084f10a5eae8b566cff5a4). TTL slides on each access via getSessionFromKV().


Tier Resolution

Resolved once at login, stored in session. Not recalculated per-request.

resolveUserTier(email, userId, workspaceDb, opsDb)
        ├── FEDERATION-HOOK-01 (disabled)
        │   Future: validate UCCA assertion token here
        ├── Look up user in workspace-db → get org_id
        ├── No org_id → L4A (4.5) — unresolved, pending onboarding
        ├── Has org_id → check rto_clients in ops-db
        │   ├── Not a client → L4A (4.5)
        │   └── Active client → check group membership
        │       ├── Admin group → L4 (4)
        │       ├── No groups (first user) → L4 (4) — org owner default
        │       └── Standard group → L4A (4.5)
        └── Return: { ucca_layer, org_id, team_id, group_ids, display_name }

The 4.5 sentinel: L4A is stored as 4.5 (not a string). This allows numeric comparisons: ucca_layer <= 4 correctly includes L1-L4 and excludes L4A. Documented in all KN gating and compliance mode gating locations.


Permissions by Tier

Tier Permissions
L1-L3 Full: qualifications:read, units:read, scope:read/write, audit:read/write, members:manage, billing:read/write, org-settings:manage, compliance:read/write
L4 Org admin: qualifications:read, units:read, scope:read/write, audit:read, members:manage, billing:read, org-settings:manage, compliance:read
L4A Member: qualifications:read, units:read, scope:read, audit:read

Content Gating

KN content (kn_criteria_reasoning column): - L1-L4 (ucca_layer <= 4): KN content included when ?include=kn is passed - L4A (ucca_layer = 4.5): KN content stripped from response

Compliance mode: - L1-L4: full access to compliance mode - L4A: compliance mode option visible but disabled


Allowlist (ONBOARD-01)

All magic link delivery gated by access_allowlist table in ops-db.

Type Behaviour
email Exact email match
domain Any address at that domain

Unlisted emails: no magic link sent, "invitation only" message shown. Response timing identical to allowed path (800ms fixed delay) to prevent timing attacks.

Rejected attempts logged to auth_rejection_log in ops-db.


Worker Secret Dependencies

Secret Worker Purpose
RESEND_API_KEY workspace, internal-api Transactional email (magic links, alerts)

FEDERATION-HOOK-01

Location: apps/workspace/lib/tier-resolution.ts

When UCCA ships B-AUTH-OAUTH-01, this hook will validate a signed assertion token from auth.ucca.online. Until then: - No UCCA domain bypass in tier resolution - admin@rtopacks.com.au resolves via allowlist + org lookup like any other user - The hook is a comment block, not dead code — it marks where future validation will land


Amendment Log

Date Change Authority
2026-04-06 Initial document — Session 44 Tim Rignold