Skip to content

feat(auth): per-org MFA + dispatcher/MCP gate (ADR-0069 D3) - #2395

Merged
os-zhuang merged 1 commit into
mainfrom
auth/adr-0069-mfa-tails
Jun 27, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
auth/adr-0069-mfa-tails

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

ADR-0069 (tracking #2281, follow-up #2375): the two remaining tails that make enforced MFA total.

What

  • Per-org sys_organization.require_mfa — an org can require MFA above the global floor. computeAuthGate now treats the active org's require_mfa as an effective MFA requirement even when global mfa_required is off. isAuthGateActive() stays cheap (synchronous) via a 60s-TTL "any org requires MFA" cache with a lazy background refresh — a brand-new per-org requirement activates the gate on the next request, with no per-request org query when the feature is off.
  • Dispatcher / MCP gate — the auth-policy gate now also runs in the runtime dispatcher (after resolveExecutionContext), so MCP / GraphQL / embedded data paths enforce PASSWORD_EXPIRED / MFA_REQUIRED consistently with the REST seam (feat(auth): password expiry via the session-validation gate (ADR-0069 D1, P1) #2388). Reuses the shared core evaluateAuthGate + allow-list. Previously only the REST surface (the Console) was gated.

Default-off / additive; ADR-0049 (enforcement ships with the setting).

Verification

  • Unit: plugin-auth 195 + runtime 444 + core 299 + platform-objects 63 + service-settings 129 green; full build incl. strict DTS green. New tests: per-org computeAuthGate (active-org require_mfa → MFA_REQUIRED with global off; not-requiring org → pass; cache trips isAuthGateActive).
  • Live dogfood:
    • Per-org: global MFA off; an org with require_mfa=true → its member's data request is 403 MFA_REQUIRED, while an admin with no org is 200 (correct scoping).
    • Dispatcher: global MFA on → POST /api/v1/mcp returns 403 { error: { details: { code: 'MFA_REQUIRED' } } } (the dispatcher seam fires, matching the REST gate).

ADR-0069 enforced MFA — now complete across all surfaces

✅ global + per-org enforcement · ✅ REST (Console) gate (#2388) · ✅ dispatcher/MCP gate (this PR) · ✅ Console remediation UI (objectstack-ai/objectui#2037). Remaining on #2375 is P2/P3 only.

🤖 Generated with Claude Code

Completes enforced MFA with the two remaining tails.

- platform-objects: sys_organization.require_mfa (per-org tightening above the
  global floor).
- plugin-auth: computeAuthGate treats the active org's require_mfa as an
  effective MFA requirement even when global mfaRequired is off; isAuthGateActive
  consults a 60s-TTL cached "any org requires MFA" flag (lazy background refresh)
  so the cheap sync check stays honest without per-request org queries.
- runtime: enforceAuthGate runs in the dispatcher after resolveExecutionContext,
  gating MCP/GraphQL/embedded data paths the same way the REST seam gates the
  Console — reusing the shared core evaluateAuthGate + allow-list.

Default-off / additive; ADR-0049.

Verified live (dogfood): with GLOBAL mfa off, an org member whose org has
require_mfa=true is gated 403 MFA_REQUIRED while an admin with no org is not
(per-org scoping). With global mfa on, POST /api/v1/mcp returns 403 with
details.code=MFA_REQUIRED (the dispatcher seam). Unit: plugin-auth 195 +
runtime 444 + core 299 + platform-objects 63 + service-settings 129 green;
full build incl. strict DTS green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 27, 2026 5:21pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jun 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/runtime.

23 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/cloud-artifact-api.mdx (via packages/runtime)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/plugin-auth, @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/concepts/packages.mdx (via @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/runtime)
  • content/docs/concepts/setup-app.mdx (via @objectstack/platform-objects)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/api-reference.mdx (via @objectstack/runtime)
  • content/docs/guides/auth-sso.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/authentication.mdx (via @objectstack/plugin-auth, @objectstack/runtime)
  • content/docs/guides/cloud-deployment.mdx (via @objectstack/runtime)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/runtime)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/runtime)
  • content/docs/guides/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/guides/kernel-services.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/packages.mdx (via @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/runtime)
  • content/docs/guides/plugin-chatbot-integration.mdx (via @objectstack/runtime)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/production-readiness.mdx (via @objectstack/plugin-auth, @objectstack/runtime)
  • content/docs/guides/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

This branch was successfully deployed

1 active deployment
Preview — 97a24292 Deployed Jun 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant