Raxx · internal docs

internal · gated ↑ index

Auth Unification — PM Card Rework

Date: 2026-05-03 UTC Author: PM agent (raxx-pm-bot) Locked direction (Kristerpher 2026-05-03): - Hybrid IDP: Google Workspace for all operator surfaces; passkeys remain primary for Antlers customer surfaces - Programmatic: per-caller scoped service tokens (Velvet D3, already locked in ADR-0037–0041) - Workspace 2FA enforced at Google level for all operator identities - CF Access at edge for every operator surface (outer perimeter, non-negotiable) Architect design doc: docs/architecture/auth-unification.md (landed 2026-05-03) ADR: ADR-0042 (companion to this doc) Parent epics: #907 (Velvet), #146 (console), #651 (support), #81 (SDLC/security hardening)


Cards from TradeMasterAPI repo only (cross-repo noise filtered). Cards are grouped by surface.

1.1 Customer Surface — Antlers (raxx.app)

# Title State Key Labels Summary
#128 raxx.app passkey signup and login screens OPEN blocked, needs-grooming Customer-facing passkey enrollment + login UI. Correct per Direction C.
#111 WebAuthn login endpoint + session issuance (Raptor) OPEN blocked, needs-grooming Backend WebAuthn login. Customer surface, correct.
#113 Passkey / device management UI + endpoints (Antlers + Raptor) OPEN blocked, needs-grooming Customer passkey management. Customer surface, correct.
#112 Email verification service + /api/auth/email/verify endpoint OPEN area:backend-v2 Email verification step after WebAuthn registration. Correct per invariant I3.
#670 feat(support): multi-passkey enrollment + backup codes OPEN groomed, ready-for-dev A+B recovery policy implementation. Customer surface. Correct.
#272 Client-side E2E key derivation from passkey PRF OPEN needs-grooming PRF extension for E2E encryption. Customer surface.
#173 Implement passkey sign-in flow on iOS (AuthenticationServices) OPEN blocked, type:ios iOS passkey. Customer surface. Correct per ADR-0001.
#174 Serve AASA file from Raptor for iOS passkey association OPEN area:security iOS AASA. Customer surface.
#250 Epic: Passkey-keyed end-to-end encryption for customer data OPEN type:epic, blocked PRF E2E encryption epic. Customer surface.
#472 Bind passkey sign-up UI to WebAuthn registration endpoints CLOSED (2026-04-30) ready-for-dev Completed.

1.2 Operator Surface — Console (console.raxx.app)

# Title State Key Labels Summary
#146 Epic: raxx-console — operator admin console OPEN type:epic, area:security Parent epic for console. Encompasses all console auth work.
#620 feat(console): admins-online — sessions, revoke per session OPEN area:security Session management UI in console. Correct.
#300 M11: Token rotation UI — secrets index, TOTP re-elevation, rotate-now flow OPEN area:security TOTP elevation for rotation. CONFLICT — see demote list.
#254 raxx-console: secret rotation dashboard + on-demand rotation UI OPEN area:security Precursor to Velvet. Partially superseded.
#766 Add console-billing-read RBAC role and middleware guard OPEN area:security RBAC role for billing. Correct per RBAC design.
#954 feat(velvet/ui): yaml-driven revocation auth gate (group/role/user) OPEN area:security, sprint:next Velvet revocation auth gate. Correct per Velvet v2.

1.3 Operator Surface — FreeScout / Tickets (tickets.raxx.app)

# Title State Key Labels Summary
#651 Epic: support.raxx.app — public customer support portal OPEN type:epic Parent epic for all FreeScout work.
#707 epic(support): FreeScout productionization — first-customer-email readiness OPEN type:epic, groomed FreeScout production hardiness. No auth conflict.
#695 ops(security): tickets.raxx.app/admin now requires WebAuthn second factor OPEN groomed, type:docs Doc card for WebAuthn-on-CF. CONFLICT — see demote list.
#725 ops(support): FreeScout activity log retention policy OPEN area:security Retention policy. Correct.
#712 feat(support): brand FreeScout outbound email templates OPEN area:devops, blocked Email branding. Not auth-related.

1.4 CF Access / Infrastructure Auth

# Title State Key Labels Summary
#453 feat: sync Founders waitlist to CF Access policy on raxx.app OPEN blocked, type:security Pre-GA customer allowlist sync. Customer surface; no conflict with hybrid IDP.
#692 ops: add raxx-agent-vault-access service token to CF Access policies OPEN type:infrastructure Service token enrollment. Correct, applies to programmatic callers.
#948 feat(velvet/adapters): CF User API + Access service-token adapters OPEN sprint:next, needs-grooming Velvet CF adapter. Correct per Velvet v2.
#568 reliability: configure TOTP/passkey MFA on CF Access CLOSED (2026-04-30) groomed Implemented. TOTP/passkey MFA on Class 3/4 apps. Superseded by hybrid IDP direction — CF Access TOTP is transitional; Google Workspace 2FA is target.
#393 feat(console): Mode A rotation for CF Access service tokens CLOSED (2026-04-27) groomed Completed.

1.5 Velvet / Programmatic Auth

# Title State Key Labels Summary
#907 epic(velvet): centralized token + rotation service OPEN type:epic, groomed Parent Velvet epic. Per-caller service tokens (D3 locked). Correct.
#912 feat(velvet/auth): service-token auth middleware + rotation-authz matrix OPEN groomed, sprint:next M1 auth middleware. Correct per D3.
#908 feat(velvet): scaffold Heroku app pair + Postgres add-on OPEN groomed, sprint:next Velvet scaffold. Correct.

1.6 RBAC / Audit / Identity Infrastructure

# Title State Key Labels Summary
#334 ops: contingent authorization for break-glass credentials OPEN area:security Break-glass authz. Correct per ADR-0031 and hybrid model.
#500 Epic: Workflow UUID tracing — replay + support transparency OPEN type:epic, area:security Audit/tracing epic. Correct.
#515 feat(trace): SC-12 Ed25519 subsystem signing OPEN area:security Audit integrity signing. Correct.
#115 Retention job + audit-log tamper-evidence digest OPEN area:security Audit retention. Correct per ADR-0003.
#114 GDPR DSR endpoints: export, erase, rectify, retention OPEN blocked, area:security GDPR endpoints. Correct.

2. Demote List

Demote A — #695: "ops(security): tickets.raxx.app/admin now requires WebAuthn second factor + extend to console + vault"

Why: This card documents the state where CF Access uses email-OTP + WebAuthn as the MFA layer at CF Access. The locked hybrid direction replaces this: CF Access moves to Google Workspace OIDC as the IDP. Workspace 2FA (not CF Access–native WebAuthn prompts) is the 2FA mechanism. The "extend to console + vault" follow-up in item 2 of this card is now superseded by the architect's Phase 1 (#NV01: CF Access Google Workspace OIDC binding).

The doc content in #695 remains factually correct as a historical record of the transitional state (PR #633), but the active "extend" action item conflicts with the new direction.

Action: Add priority:low label. Post supersede comment (below). Do NOT close — the card's body documents the transitional state which is still live until Phase 1 is complete.

Demote B — #300: "M11: Token rotation UI — secrets index, TOTP re-elevation, rotate-now flow"

Why: This card specifies TOTP re-elevation as the step-up factor in the Console rotation UI. The hybrid auth direction makes Google Workspace 2FA the MFA mechanism for operators. "TOTP re-elevation" at the app layer inside the Console is a duplicate layer on top of Workspace 2FA. The Velvet revocation auth gate (#954) already covers the yaml-driven step-up confirmation for rotation — its auth gate is configurable (group/role/user) and does not specify TOTP as the factor.

Conflict specifics: The auth.py TOTP flow in the Console is called out as a potential removal target in architect's DQ-2 (open question). The card as written mandates building TOTP into the rotation UI — this is premature and may need to be removed if DQ-2 resolves to "Workspace 2FA only."

Action: Add priority:low label. Post supersede comment flagging the DQ-2 dependency. Do NOT close — the non-TOTP parts of the card (secrets index, rotate-now flow) are still valid and are subsumed into Velvet v2 cards.

Demote C — #254: "raxx-console: secret rotation dashboard + on-demand rotation UI"

Why: This card was the V1 precursor card for token rotation UI. It is substantially superseded by the Velvet epic (#907) and the Velvet v2 card slate (#908–#918, #948, #954). The Velvet cards are finer-grained, better groomed, and tied to the current architecture. #254 is not closed but it represents older, coarser scope.

Action: Add priority:low label. Post a note pointing to #907 as the active work. Do NOT close.


3. New Cards to Propose

These cards cover the architect's Phase 1–3 work that is NOT yet represented by any existing open card. Do NOT file yet — Kristerpher reviews this slate first.

NV01 — CF Access: bind Google Workspace OIDC IDP + group-policy migration

Phase: 1 (sprint:current — unblocks every downstream Phase 1 card) Parent epic: #146 (console) + #81 (security hardening) Surface: CF Access (all gated apps) Summary: Create Google OAuth app in Cloud Console (scoped to openid email profile + Workspace Admin SDK). Configure CF Zero Trust Google Workspace OIDC IDP. Create four CF Access groups (cf-ops, cf-superadmin, cf-developer, cf-auditor). Replace one-time-pin policies with group-membership policies on console, vault, FreeScout, and internal-docs. 48-hour dual-IDP validation window before removing one-time-pin. Terraform codified. Depends on: DQ-4 (group taxonomy decision) from architect's auth-unification.md §12. Risk: Group taxonomy locked before this card starts or CF group recreation breaks all CF JWTs.

NV02 — FreeScout: install + configure OAuth Login module (Google IDP)

Phase: 1 (sprint:current — FreeScout license key in vault is the gate) Parent epic: #651 (support epic), #707 (FreeScout productionization) Surface: FreeScout (tickets.raxx.app) Summary: Install FreeScout OAuth Login module (Tagras). Create separate Google OAuth2 app in Cloud Console. Store credentials in Infisical under /freescout/google-oauth. Configure: @raxx.app domain only, auto-create users OFF. Test: Kristerpher Google login matches existing FreeScout user. CF Access outer gate remains. Depends on: NV01 (CF Access IDP swap), FreeScout license key must be in vault at /freescout/license-key. Risk: FreeScout module version compatibility with current FreeScout install.

NV03 — Console: "Sign in with Google" fallback path + google_sub migration

Phase: 1 (sprint:next) Parent epic: #146 (console epic) Surface: Console (console.raxx.app) Summary: Add google_sub + google_email columns to console_admins (migration 0NNN_console_google_oidc.sql). New /auth/google/callback route with PKCE, JWKS validation, hd claim check, google_sub bind-on-first-login. Audit events: auth.google_bind, auth.google_login, auth.google_unbind. Feature-flagged CONSOLE_GOOGLE_AUTH=1. Passkey remains primary. Google is fallback only. Depends on: NV01 (CF Access IDP), DQ-1 decision (passkey primary vs Google-only). Risk: DQ-1 unresolved means scope may shrink (passkey primary + fallback) or expand (Google-only + remove TOTP flow). Card assumes passkey-primary recommendation accepted.

NV04 — Vault: Infisical Google OIDC SSO configuration

Phase: 1 (sprint:next) Parent epic: #907 (Velvet), #249 (secrets vault) Surface: Infisical (vault.raxx.app) Summary: Configure Infisical org SSO to use Google OIDC. Map @raxx.app hosted domain. Test with Kristerpher's Google account. CF Access outer gate (Class 3, MFA non-relaxable) remains unchanged. Document in docs/ops/runbooks/operator-login.md. Depends on: NV01 (CF Access IDP swap must complete first to establish the Google identity layer). Risk: Infisical OIDC config is org-level; misconfiguration locks out all vault access. Rollback: disable OIDC in Infisical admin (local auth remains as fallback until OIDC is confirmed working).

NV05 — Velvet admin UI: Google OIDC from day one (Phase 2, NV12 sub-card)

Phase: 2 (sprint:next, tied to Velvet admin UI build milestone) Parent epic: #907 (Velvet) Surface: Velvet admin UI Summary: Velvet admin UI ships with Google OIDC as the only auth path (no password path, no separate TOTP). OIDC library (PKCE, short-lived ID tokens, no token persistence). Validate hd == raxx.app. Map sub + email to velvet_admin_id in Velvet Postgres (schema: velvet_admins table per architect's §7.2). Read CF JWT groups claim for RBAC. Per-caller service tokens for programmatic access (already in #912). Depends on: NV01 (CF Access IDP), #908 (Velvet scaffold deployed), #912 (service-token middleware). Risk: Must coordinate with Velvet admin UI Milestone definition (NV12 milestone number not yet filed as a card).

NV06 — Antlers: optional Google OAuth for customers (Phase 3, post-MBT-v1)

Phase: 3 (do not start before MBT-v1 ships) Parent epic: #78 (Exploration Platform) Surface: Antlers (raxx.app) Summary: Add Google OAuth as an optional second sign-in path for customers. Passkeys remain primary and required. Google is additive — no customer is forced to use it. Separate ADR required before implementation. DQ-3 must be answered (hard requirement: no customer forced to Google). Depends on: ADR filed post-MBT-v1; NV01 for CF Access Google identity foundation; DQ-3 resolution. Risk: Introducing Google OAuth on the customer surface after passkey launch creates a weaker fallback path. ADR must explicitly address the security regression risk.

NV07 — JWT claims / role taxonomy doc

Phase: 1 (sprint:current — blocks NV01 through NV05) Parent epic: #146 (console) Surface: Cross-cutting Summary: Document the CF Access JWT groups claim taxonomy (cf-ops, cf-superadmin, cf-developer, cf-auditor), how each app layer reads and validates the CF JWT, and the RBAC mapping from groups claim to app-local roles (per rbac-design.md). This is the spec that all app-layer auth implementations in NV01–NV05 must reference. Lives in docs/architecture/jwt-claims.md. Depends on: DQ-4 decision (group taxonomy). Architect's auth-unification.md §5.3 is the source draft. Risk: If filed as a standalone doc, risk of drift from the CF Access config. Must be kept in sync via PR review policy.

NV08 — Audit log schema for operator logins

Phase: 1 (sprint:next) Parent epic: #500 (workflow tracing), #146 (console) Surface: Console + Velvet Summary: Define and implement audit log entries for Google OAuth events across all operator surfaces. Console: auth.google_bind, auth.google_login, auth.google_unbind (spec in architect's §7.1). Velvet: equivalent schema when NV05 ships. Retention: 2 years (ADR-0003). Verify Cloudflare Access login events are captured (CF already logs these; ensure they are exported or retrievable). Depends on: NV03 (Console Google OAuth route), NV05 (Velvet admin UI). Risk: Scope creep — this card is about the schema and implementation, not a full audit dashboard. Audit dashboard is a separate card (#620 partially covers session management).

NV09 — Workspace 2FA enforcement check (operator action, not a code card)

Phase: 1 (sprint:current) Parent epic: #81 (SDLC / security hardening) Surface: Google Workspace admin Summary: Operator action: Kristerpher enables "Require 2-Step Verification" in Google Workspace Admin Console for all users in the raxx.app domain. Verify 2FA enforcement policy is in place before NV01 (CF Access IDP swap) goes live. Document the enforcement state in docs/ops/runbooks/operator-login.md. This is a one-time infra action, not a code change — but it must be tracked and confirmed before Phase 1 can close. Depends on: Nothing. But NV01 depends on this. Risk: Enabling Workspace 2FA before operators are enrolled locks them out. Verify Kristerpher's 2FA enrollment before toggling the policy.


4. Contradiction / Flag for Kristerpher

Flag 1 — #695 transitional state vs. Phase 1 CF Access swap

Issue #695 documents the current state where CF Access uses auth_method: swk (WebAuthn at CF Access) for tickets.raxx.app. The architect's Phase 1 replaces the CF Access IDP with Google Workspace OIDC, which changes the MFA mechanism from CF-native WebAuthn prompts to Workspace-enforced 2FA. These are not compatible as simultaneous goals: running Google Workspace OIDC through CF Access means CF is no longer prompting for WebAuthn — it trusts Workspace to enforce the second factor.

The transition window needs a documented handshake: during the 48-hour dual-IDP window (both one-time-pin and Google Workspace enabled in CF Zero Trust), which policy wins? If Workspace 2FA is already enrolled before the swap, the Class 3 surfaces (FreeScout, vault) retain their "strong MFA always" requirement via Workspace — this is functionally equivalent to the current auth_method: swk gate, but enforced by a different mechanism. This is architecturally sound per ADR-0031 D3 (the requirement is "strong MFA always" — the mechanism is not specified). The PM read: no contradiction, but the transition handshake must be explicit in the NV01 card ACs.

Flag 2 — DQ-1 affects #300 demote scope

DQ-1 (passkey primary vs Google-only for Console) directly affects how much of #300's TOTP elevation flow is retained. If DQ-2 also resolves to "Workspace 2FA only" (remove Console TOTP), the #300 demote becomes a near-close rather than a priority:low. Recommend Kristerpher answers DQ-1 and DQ-2 before the NV03 card is claimed.

Flag 3 — #695 "extend to console + vault" follow-up conflicts with NV01 timing

Item 2 in #695's body says "extend to console + vault" should be filed as separate sub-cards. That work is now NV01 (CF Access IDP swap) and NV04 (Infisical OIDC). The demote comment will point to NV01 and NV04 as the canonical replacements once they are filed.

Flag 4 — #453 (Founders waitlist → CF Access policy) timing

453 syncs the Founders waitlist to CF Access as a one-time-pin allowlist. After NV01 completes, the CF Access policy moves to group-membership-based (Workspace groups). The Founders waitlist sync pattern assumes email-OTP identity — a Google Workspace identity requires a different mechanism (the founder would need a @raxx.app Workspace account or an external Google account allowed by domain policy). This is a pre-GA customer surface concern, not an operator concern, but #453 should be reviewed after NV01 to confirm the sync mechanism still works. Flagging here; no demote at this stage.


5. Sprint Placement Summary

Card Phase Proposed Sprint
NV09 — Workspace 2FA enforcement check (operator action) 1 sprint:current
NV01 — CF Access Google Workspace OIDC binding 1 sprint:current
NV02 — FreeScout OAuth Login module install 1 sprint:current
NV07 — JWT claims / role taxonomy doc 1 sprint:current
NV03 — Console "Sign in with Google" fallback 1 sprint:next
NV04 — Vault Infisical Google OIDC SSO 1 sprint:next
NV08 — Audit log schema for operator logins 1 sprint:next
NV05 — Velvet admin UI Google OIDC (NV12 sub-card) 2 sprint:next (when NV12 is scoped)
NV06 — Antlers optional Google OAuth for customers 3 future (post-MBT-v1)

6. Open Decisions Required Before Filing

These are all from the architect's auth-unification.md §12. No new cards can be claimed until these are resolved:

ID Question Blocks
DQ-1 Console: passkey primary + Google fallback vs Google-only? NV03 scope
DQ-2 Console TOTP: retain for SEV-1 step-up vs remove (Workspace 2FA only)? #300 demote scope, NV03
DQ-3 Customers: hard requirement "no customer forced to Google"? NV06 scope
DQ-4 CF Access group taxonomy: approve ops, superadmin, developer, auditor? NV01, NV07

End of rework doc. See docs/architecture/auth-unification.md for the architect's full design. See ADR-0042 for the companion decision record.