fix(auth): admin-gate self-service SSO provider registration + default-role JIT (ADR-0024 / cloud#551)#2363
Merged
Merged
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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'sPOST /sso/registeronly enforces org-admin whenbody.organizationIdis 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
before-hook on/sso/registerrequiring the caller to be a platform admin OR an owner/admin of their active org, regardless of whetherorganizationIdis passed. Fail-closed; unauthenticated requests still fall through tosessionMiddleware(→ 401). New helpersresolveActor()(hook-order-independent cookie/bearer resolution) andisOrgOrPlatformAdmin()(mirrorscustomSession's role derivation; reads viawithSystemReadContext).organizationProvisioning.defaultRole:'member'tosso()so a first-time federated login lands with an explicit role (over SecurityPlugin'smember_defaultbaseline).Verification (browser E2E, local prod-like stack + a mock OIDC IdP)
SSO_REGISTER_FORBIDDEN(was 200 before this change).list-accountsshowsproviderIdset = thesys_accountsource marker.tscadds no new errors (the two pre-existing DTS errors —@better-auth/scim@1241 andpasswordRejectBreached@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