Skip to content

v9.10.0 — role-scoped read enforcement + per-user identity foundation

Choose a tag to compare

@saurabhjain1592 saurabhjain1592 released this 17 Jul 10:53
873ac47

[9.10.0] - 2026-07-17 (per-user identity & role-scoped authorization for fleet deployments)

Closes the fleet broken-access-control epic: in a fleet where every developer's plugin authenticates with one shared org:license credential, that credential granted attribution, not authorization — any holder could read the entire tenant's audit trail, decisions, overrides, per-user cost, and execution history. This release resolves the caller into a validated, non-forgeable {identity, role} on the fleet plane and role-scopes every cross-user read. Migrations core/143 + enterprise/135 (existence-guarded, with down migrations). Behavior change: a shared-credential caller with no per-user token now reads zero rows from the governed read tools — see Changed.

Security

  • Role-scoped audit / decision / override reads (Enterprise). (Enterprise) The presenting fix: the fleet read tools (search_audit_events, list_recent_decisions, list_overrides) and the equivalent API reads are now scoped server-side (SQL WHERE, never post-fetch) — a developer role reads only their own user_email rows, an admin/owner reads the full tenant. The census is exhaustive: audit/decision/override, plus the whole-tenant compliance/evidence exports (admin-gated, since a per-user export is meaningless) and the adjacent cost/usage and replay/execution surfaces — a different data domain reachable with the same credential; both stamp a shared license identity, so a non-admin is denied (403) rather than given a vacuous filter, with cross-org isolation on every by-id route.
  • Shared synthesized identities fail closed (Enterprise). (Enterprise) Every shared synthesized identity the platform mints (mcp-client:*, the reserved @axonflow.local / @axonflow.internal service domains, the evaluator identity) is matched against a single canonicalized census predicate at the read-scope boundary and empties the scope, so a read can never resolve to a multi-developer pool. A validated per-user identity reads its own rows normally; near-miss customer domains are untouched.

Added

  • Per-user identity foundation for the fleet plane (Enterprise). (Enterprise) authenticateMCPServerRequest resolves a validated, non-forgeable {identity, role} — replacing the forgeable X-User-Email-only identity and the hardcoded role: unknown — on a pluggable validator seam shared by both provisioning backends below.
  • Per-user token provisioning — the two validator backends for fleet identity (Enterprise). (Enterprise) Part of the fleet-identity epic: fleets that authenticate every developer's plugin with one shared org:license credential get attribution, not authorization. This delivers per-user credentials on two paths, both producing a validated, non-forgeable {identity, role} for the fleet plane's pluggable validator. Path A (AxonFlow-managed): an admin-plane API (/api/v1/admin/organizations/{org_id}/user-tokens) mints, rotates, and revokes per-user HS256 tokens with an admin-assigned role. Because the role travels as a JWT claim, mint/rotate/revoke require a valid X-Admin-API-Key even in deployment modes where admin auth is otherwise optional — a developer can never mint themselves role: admin. Tokens carry a required exp (default 30d, capped 1y) and a jti; revocation is a server-side deny-list (enterprise migration 135) consulted on every validation, fail-closed. Path B (IdP-issued OIDC): a per-tenant OIDC configuration on sso_configurations (core migration 143, CRUD now gated on sso:configure) drives a new asymmetric verifier that validates JumpCloud/Okta/Azure-AD tokens against the IdP JWKS — RS256 only (alg:none and HS256 algorithm-confusion rejected before any key is consulted), iss/aud/exp/nbf enforced, JWKS cached with kid-miss refetch for signing-key rotation, and the role resolved from the SCIM-synced directory rather than any token claim so IdP misconfiguration cannot escalate privilege. The SSO-config mutation endpoints, which rewrite an org's authentication trust anchors, are now enforced behind sso:configure (previously declared but unenforced). Migrations core/143 + enterprise/135 are existence-guarded with down migrations. Fleet-scale provisioning for both paths, including end-to-end JumpCloud OIDC, is documented in docs/enterprise/per-user-token-provisioning.md.
  • Advisory-plane server / tool identity fields. (Community) The check_policy / MCP check-input schema and DecisionTarget gain distinct server and tool identity fields (previously one caller-supplied string was duplicated into both), surfaced in the portal audit view. Additive and backward-compatible (omitempty). This is one self-contained addition from the separate audit-identity epic that landed ahead of this release; the remainder of that epic follows in a later release.
  • Client support for per-user tokens. The Claude Code (1.10.0), Cursor (1.6.0), Codex (1.6.0), and OpenClaw (2.7.0) plugins now send the per-user token as X-User-Token on every governed surface (unconfigured behavior unchanged); the LiteLLM integration (1.0.4) fails closed on a platform policy rejection even under fail_open, while still honoring fail_open on a genuine engine outage. /health advertises the new recommended plugin versions.

Fixed

  • Token-machinery robustness (Enterprise). (Enterprise) Per-user token revocation on the request hot path is short-TTL cached (invalidated by the per-user mass-revoke watermark, fail-closed on any checker error); fleet-validator registration moved to a deterministic startup step with a warning when a token is presented but no validator is registered.

Changed

  • Behavior change — fleet read tools require a per-user (or admin) identity (Enterprise). (Enterprise) The MCP read tools and equivalent API reads are role-scoped: a caller presenting only the shared org:license credential with no per-user token resolves to a shared identity and reads zero rows, where it previously read the whole tenant trail. Provision per-user tokens (admin-minted or IdP/OIDC) or call with an admin-role token to restore reads. Single-operator and Community-mode deployments are unaffected.