Skip to content

fix(auth): admin-gate self-service SSO provider registration + default-role JIT (ADR-0024 / cloud#551) - #2363

Merged
os-zhuang merged 2 commits into
mainfrom
adr-0024-sso-admin-gate
Jun 27, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
adr-0024-sso-admin-gate

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

Part of the cloud#551 Unified Identity V1 (ADR-0024) E2E. Local browser testing surfaced a real gap that static review had missed.

@better-auth/sso's POST /sso/register only enforces org-admin when body.organizationId is supplied (index.mjs: if (ctx.body.organizationId) { … hasOrgAdminRole … }). A global (org-less) provider passes on nothing but a valid session — so any authenticated env member can register an env-wide external IdP, a JIT-provisioning / login-routing vector. This violates ADR-0024's "registerSSOProvider is admin-only".

What

  • A3 — admin gate: a before-hook on /sso/register requiring the caller to be a platform admin OR an owner/admin of their active org, regardless of whether organizationId is passed. Fail-closed; unauthenticated requests still fall through to sessionMiddleware (→ 401). New helpers resolveActor() (hook-order-independent cookie/bearer resolution) and isOrgOrPlatformAdmin() (mirrors customSession's role derivation; reads via withSystemReadContext).
  • A4 — default role: pass organizationProvisioning.defaultRole:'member' to sso() so a first-time federated login lands with an explicit role (over SecurityPlugin's member_default baseline).

Verification (browser E2E, local prod-like stack + a mock OIDC IdP)

  • Non-admin member register → 403 SSO_REGISTER_FORBIDDEN (was 200 before this change).
  • Org owner register → 200 (discovery hydrated).
  • External-IdP OIDC login → JIT user created; better-auth list-accounts shows providerId set = the sys_account source marker.
  • Role-less federated user reaches a usable env (member_default); anon denied.
  • tsc adds no new errors (the two pre-existing DTS errors — @better-auth/scim @1241 and passwordRejectBreached @865 — are from prior commits, not this one).

Scope

Only packages/plugins/plugin-auth/src/auth-manager.ts. The SSO mechanism stays framework-open (no identity-governance added). Closes the A-workstream gap for cloud#551.

🤖 Generated with Claude Code

…t-role JIT (ADR-0024)

@better-auth/sso's POST /sso/register only enforces org-admin when
`body.organizationId` is supplied — a GLOBAL (org-less) provider passes
on nothing but a valid session. So any authenticated env member could
register an env-wide external IdP (a JIT-provisioning / login-routing
vector), violating ADR-0024's "registerSSOProvider is admin-only".

Add a before-hook gate on /sso/register requiring the caller to be a
platform admin OR an owner/admin of their active org, regardless of
whether organizationId is passed. Fail-closed; unauthenticated requests
still fall through to sessionMiddleware's 401. New helpers resolveActor()
(hook-order-independent cookie/bearer resolution) and isOrgOrPlatformAdmin()
(mirrors customSession's role derivation, reads via withSystemReadContext).

Also pass `organizationProvisioning.defaultRole:'member'` to sso() so a
first-time federated login lands with an explicit default role (over the
SecurityPlugin member_default baseline).

Verified E2E on the local prod-like stack against a mock OIDC IdP:
non-admin register → 403 SSO_REGISTER_FORBIDDEN; owner → 200; external-IdP
login JIT-provisions the user with sys_account.provider_id set.

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 12:52pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth.

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

  • content/docs/concepts/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/concepts/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/auth-sso.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/kernel-services.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/production-readiness.mdx (via @objectstack/plugin-auth)
  • 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.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jun 27, 2026
@os-zhuang
os-zhuang merged commit 22b32c1 into main Jun 27, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the adr-0024-sso-admin-gate branch June 27, 2026 12:54

This branch was successfully deployed

1 active deployment
Preview — 09e9e94e 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 tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant