Skip to content

feat(saml): add dedicated_scope option for SAML providers - #2893

Open
hf wants to merge 5 commits into
masterfrom
stojan/saml-dedicated-scope
Open

hf wants to merge 5 commits into
masterfrom
stojan/saml-dedicated-scope

Conversation

@hf

@hf hf commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Some SAML identity providers (famously Entra) don't allow the same entity ID to be registered with multiple "applications," making Supabase Auth usable only with one application. Some enterprise use-cases are therefore hard to build.

In this PR we add a dedicated_scope optional boolean on an SSO provider, which in SAML means that the provider can only be used via its own new Entity ID and URLs scoped to the provider's unique ID.

Dedicated scope Entity ID, Metadata URL ACS URL
False or Null /sso/saml/metadata /sso//saml/acs
True /sso/{uuid}/saml/metadata /sso/{uuid}/saml/acs

This way in Entra you can register many SPs served by the same Supabase Auth project by creating them with the dedicated_scope boolean.

Notes:

  • Adds a dedicated_scope boolean to SAML SSO providers. When set, that provider's ACS URL and SP metadata URL (and, by extension, the entity ID GoTrue presents to the IdP, which defaults to the metadata URL) are scoped to the provider's own ID, instead of using the URLs shared by every SAML provider in the project. The same signing key/certificate is still used.
  • Useful for IdPs that require a unique ACS URL/entity ID per connection, and isolates assertions issued for one connection from being replayed against another.
  • New dedicated routes GET /sso/{sso_provider_id}/saml/metadata and POST /sso/{sso_provider_id}/saml/acs alongside the existing shared /sso/saml/* endpoints; the ACS handler rejects a provider hitting the wrong endpoint for its scope setting.
  • dedicated_scope can be set on create/update via the admin SSO provider API and is documented in openapi.yaml.

Lets a SAML SSO provider opt into using its own provider ID as part of
its ACS URL and entity ID (which defaults to the SP metadata URL),
instead of the URLs shared by every SAML provider in the project. The
same signing key is still used. This is useful for IdPs that require a
unique ACS URL/entity ID per connection, and isolates assertions
issued for one connection from being replayed against another.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hf
hf requested a review from a team as a code owner October 30, 2026 06:59
Comment thread internal/models/sso.go Resolved
hf and others added 2 commits October 30, 2026 08:10
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The dedicated-scope guard in the ACS handler previously ran before
ParseResponse, using the SSO provider resolved from an unauthenticated
Issuer peeked out of the raw, unverified SAMLResponse XML in the
IdP-initiated flow. Move that guard to after the assertion has been
cryptographically validated, and require IdP-initiated requests on a
dedicated-scope endpoint to carry an explicit, matching Destination
rather than relying on the library's conditional check.

Also log a WARN with the old/new value whenever dedicated_scope is
toggled on an existing provider via the admin API, since it
immediately changes the ACS URL/entity ID that provider expects.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hf

hf commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the security review findings on the dedicated_scope ACS/entity-ID scoping (fixed in 46c0560):

  • Scope-confusion bypass in IdP-initiated flow: the dedicated-scope guard now runs after ParseResponse succeeds (i.e. after cryptographic validation), instead of before it based on the unauthenticated Issuer peeked from the raw SAMLResponse XML. Also added a check that an IdP-initiated request hitting a dedicated-scope endpoint must carry an explicit Destination matching that endpoint's ACS URL, rather than relying on the library's conditional check.
  • No protection against toggling dedicated_scope on a live provider: the admin update handler now logs a WARN (with old/new value and provider ID) whenever dedicated_scope changes on an existing provider, since this immediately changes the ACS URL/entity ID that provider expects.
  • The informational note about the pre-validation XML peek of the untrusted SAMLResponse is pre-existing behavior (used to look up which provider's key to validate against) and unchanged by this PR.

hf and others added 2 commits November 4, 2026 11:45
dedicated_scope effectively changes the entity ID this SP presents to
the IdP (it defaults to the SP metadata URL, which becomes scoped to
the provider's ID when enabled), so toggling it after the IdP has been
configured would silently break the connection. Reject any attempt to
change it on an existing provider with a clear error instead.

Also move the column from sso_providers to saml_providers: it's a
SAML-protocol detail (alongside entity_id/metadata_url/name_id_format)
rather than provider-agnostic config like resource_id/disabled, and
it's now exposed nested under `saml` in the admin API response to
match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously the explicit Destination check was only enforced on
dedicated-scope ACS endpoints. Generalize it to every IdP-initiated
response so the global, shared ACS endpoint doesn't implicitly accept
assertions meant for another endpoint, asserting the invariant
ourselves rather than relying solely on crewjam/saml's internal
Recipient check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants