Raxx · internal docs

internal · gated ↑ index

ADR-0043 — Auth Unification: RBAC Reconciliation

Status: Accepted Date: 2026-05-03 Deciders: Kristerpher (product owner), software-architect Scope: CF Access group taxonomy + app-layer RBAC integration for all operator surfaces Design doc: docs/architecture/auth-unification-rbac-reconciliation.md Does NOT supersede: ADR-0042 (sibling ADR; extends it) Extends: ADR-0020, ADR-0042 Refs: Epic #146 (console operator admin console); Velvet v2 epic #907; NV10 (#954); DQ-4 resolved 2026-05-03 ~08:00 UTC


Context

ADR-0042 established Google Workspace as the IDP for all operator surfaces via CF Access Zero Trust. It left open DQ-4: the CF Access group taxonomy. The prior proposal in auth-unification.md §5.3 defined four flat CF Access groups (ops, superadmin, developer, auditor). Kristerpher rejected that taxonomy as incompatible with the fine-grained <app>-<resource>-<level> model specified in rbac-design.md and ADR-0020.

The reconciliation question: CF Access JWTs carry group claims at the network edge, but Raxx's canonical authz namespace uses fine-grained role names internal to each app. The two layers must be connected without duplicating policy state or tying Workspace group management to every app's release cycle.


Decision

D1: CF Access groups encode operator function, not app-level permissions

The CF Access group taxonomy uses four team-function groups — raxx-platform-admins, raxx-support, raxx-devops, raxx-break-glass — not fine-grained role names. These groups answer the question "is this person a Raxx operator of a given function?" at the network edge. They do not answer "can this person perform console:secrets:rotate?" That question is answered entirely by the app-layer.

D2: App-layer RBAC is authoritative; JWT groups claim is advisory

The CF JWT groups claim is read by the app layer for two advisory purposes only: 1. raxx-break-glass in claims triggers a mandatory audit alert before the passkey step. 2. Coarse surface filtering (UI optimization before role resolution completes).

The authoritative permission decision is always the user_groups → group_roles → role_permissions → role_inheritance DAG walk from rbac-design.md §7. A permission check that relies solely on the JWT groups claim and skips the DAG walk is a security defect.

D3: <app>-<resource>-<level> naming is the canonical role namespace

All Raxx roles follow the pattern from rbac-design.md §3.1. CF Access group names and Workspace group names never appear in permission checks. The mapping is:

Workspace group → CF Access Group → [advisory JWT claim for edge routing]
Workspace group → Raxx internal group (same name, different system) → roles → permissions

The Workspace group name and the Raxx internal group name may share the same string (e.g., both are raxx-platform-admins) but are independently managed systems.

D4: No auto-provisioning from CF JWT group claims

An operator whose CF JWT carries raxx-platform-admins but who has no row in user_groups for the Console is permitted past the CF edge but receives 403 at the first authenticated route. CF Access group membership is a necessary but not sufficient condition for app-layer access. This upholds ADR-0042 D5 (no auto-provisioning via Google OAuth).

D5: Velvet NV10 yaml gate uses Raxx internal group/role names exclusively

The NV10 yaml-driven revocation auth gate references group (Raxx internal group name), role (Raxx role name), or single-user (admin_id UUID). It never references CF Access group names or Workspace group emails.

D6: single-user gate is an exception to I2, permitted only for break-glass scenarios

ADR-0020's invariant (no direct permissions on users) is upheld for all routine access. The single-user option in NV10 is permitted exclusively for emergency break-glass gate configurations. Every use must generate a mandatory audit event. Routine use of single-user gates is prohibited.


Consequences

Positive

Negative / Risks

Neutral


Alternatives Considered

Alternative A: CF Access groups mirror the full <app>-<resource>-<level> namespace

Rejected. Requires ~20 Workspace groups; ties group management to every role addition or rename; exposes Raxx's internal permission taxonomy to an external system (violates I7 from the reconciliation doc); breaks SAML readiness (IdP would need to know role names, not just team-function group names). See design doc §5 Option 2 for full analysis.

Alternative B: CF Access as network gate only; no group information in JWT

Rejected as suboptimal, not as incorrect. Option 1 (design doc §5) is operationally correct but wastes the information that CF Access can carry. The raxx-break-glass alerting use-case (D2 above) is valuable and requires the JWT groups claim. Without it, break-glass access generates no early warning until after passkey authentication completes.

Alternative C: SCIM bridge for near-real-time group sync

Not rejected — this is a future upgrade path. At current team size, 1-hour eventual consistency on group membership changes is acceptable. SCIM can be added when the team grows and real-time revocation latency matters. The group taxonomy and policy design are compatible with SCIM; no architectural change is needed to adopt it later.


References