feat(sso): several identity providers per organization - #7652
Merged
Conversation
An organization can now run more than one identity provider, one per verified domain: Okta for eng.acme.com and Entra for acme.com, for example. The schema already allowed several rows; the product could not show, pick, or remove them. - Sign-in names the provider explicitly. A new POST /api/auth/sso/resolve maps an email domain to the provider that serves it, preferring a verified domain and breaking ties on provider id, so routing no longer depends on the SSO plugin's unordered domain lookup. - Registration refuses a second provider on a domain the caller already routes (SSO_DOMAIN_ALREADY_ROUTED), keeping email-domain routing deterministic. The check shares the existing cross-tenant domain read. - DELETE /api/auth/sso/providers/[providerId], owned by Sim since the plugin's delete-provider stays blocked: organization owners and admins remove an organization provider, the creator a personal one. Accounts and memberships the provider admitted are untouched. - The provider list is ordered by provider id; the settings sign-in tab lists providers, opens one by URL (`provider=`), adds another, and deletes with confirmation. JIT admissions record the provider id in their audit metadata. - Docs describe multiple providers, domain routing, and deletion. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CkZjSKgtFdE5mzsQ31gGLY
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
The PR is not yet safe to merge because provider routing can select unverified claims, concurrent registration can create ambiguous domain routes, and some authorized self-hosted administrators cannot access the new management UI. Findings
|
…ettings - Registration guard uses the same domain expression as sign-in resolution and domain verification - Self-host registration script stores the normalized provider domain - Sign-in reports "no provider" only on 404; other failures keep the generic message - Settings: canonical back slot and title on provider detail and form, Edit then Delete, no duplicate Open in the row menu, awaited list refetch before navigating, replace-history on close, unsaved-changes guard on back - Docs: state that an organization can use more than one identity provider
…ve only verified providers - Partial unique index on sso_provider (organization_id, normalized domain), built concurrently after a duplicate pre-check; a lost race maps to the same 409 as the pre-check - Sign-in resolution returns only providers whose domain is verified - Settings controls follow the organization owner/admin model the server enforces, not the creator
Collaborator
Author
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.
Summary
eng.acme.com, Entra ID foracme.com)POST /api/auth/sso/resolve, verified domains only) and passesproviderIdto Better Auth instead of relying on its unordered domain lookup0333_sso_provider_org_domain_uniqueadds a partial unique index onsso_provider (organization_id, normalized domain), built concurrently behind a duplicate pre-check (production has none). Registration checks first for a clear 409 (SSO_DOMAIN_ALREADY_ROUTED) and maps a lost race to the same responseDELETE /api/auth/sso/providers/[providerId](Sim-owned; the plugin's delete stays blocked). Org owners/admins delete org providers, creators delete personal ones. Accounts and memberships are untouched?provider=, adds another, deletes with confirmation; an org with none goes straight to the formproviderIdin audit metadataType of Change
Testing
bun run check:audits,check:migrations origin/staging,type-check,docs-manifest:check,check:openapiproviderIdpath (sign-in, OIDC and SAML callbacks, catch-all deny list, in-flight sign-ins after delete); no live IdP runChecklist