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

feat(auth): per-org MFA + dispatcher/MCP gate (ADR-0069 D3)#2395
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

vercelBot commented Jun 27, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 27, 2026 5:21pm

Request Review

@github-actionsgithub-actionsBot 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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang