SAML 2.0 Identity Provider
When to Use This Skill
- Setting up IdentityServer as a SAML 2.0 Identity Provider (IdP)
- Registering SAML Service Providers with the
SamlServiceProvidermodel - Configuring SP-initiated SSO and Single Logout (SLO) flows
- Customizing claim-to-attribute mappings via
ClaimMappingsor extensibility interfaces - Implementing production SP stores (EF Core, custom
ISamlServiceProviderStore) - Extending SAML behavior (custom NameID generation, signing, metadata, multi-tenant issuer)
- Linking an external SAML IdP as a federated authentication source (SP mode)
Core Principles
- SAML 2.0 IdP support is built into Duende.IdentityServer (v8.0+) — no separate NuGet package
- Requires Standard (add-on), Advanced, or Custom Edition license
- SP-initiated SSO is the default; IdP-initiated SSO is opt-in per service provider
SignAssertionis the default signing behavior;SignResponseis recommended for most deployments- Use EF Core stores for service providers in production; in-memory is for development only
- Front-channel SLO uses iframes (not redirect chains); partial logout is expected behavior
- The claim pipeline flows: AllowedScopes → RequestedClaimTypes → ClaimMappings
Docs: https://docs.duendesoftware.com/identityserver/saml
Setup
Update the login page to call DenyAuthenticationAsync for SAML cancellation support (when user cancels login during a SAML flow).
Endpoints
Paths are customizable via SamlOptions.Endpoints.
Profile Active Check
IProfileService.IsActiveAsync is called on every SSO request, including when the user already has an active session.
If IsActive returns false: passive requests (IsPassive=true) receive a SAML NoPassive error response; all other requests are redirected to the login page.
This is the recommended mechanism for blocking disabled or locked accounts without waiting for session expiry.
Observability
All SAML endpoints emit audit events and OpenTelemetry telemetry counters.
SSO and SLO endpoints participate in distributed tracing via the Duende.IdentityServer activity source.
See docs for SAML audit events and TelemetryMetricsCounters.SamlSso.
SamlServiceProvider Model
Claim Pipeline
Use SamlOptions.DefaultClaimMappings for global defaults; per-SP ClaimMappings override them.
Configuration (SamlOptions)
Metadata Options
Endpoint Options
SAML Signing Keys (X.509)
SAML signing requires an X.509 certificate. OIDC and SAML share the same signing credentials; rotation timing is governed by Automatic Key Management PropagationTime and RetentionDuration.
- Automatic Key Management (RSA): auto-generated RSA keys are auto-wrapped into a self-signed X.509 container. You do not need
UseX509Certificatejust to enable SAML. - Manual / raw RSA keys — including
AddDeveloperSigningCredential(): cannot be auto-wrapped. Register an X.509 certificate with a private key instead. - The default SAML signing service is RSA-only.
UseX509Certificateis not supported for EC keys — implement a customISamlSigningServicefor EC (or HSM/Key Vault) scenarios.
Service Provider Stores
In-Memory (Development)
EF Core (Production — Recommended)
Run EF migrations: dotnet ef migrations add Update_DuendeIdentityServer_v8_0
Custom Store
Note — Operational store auto-registration:
AddOperationalStore()automatically registers EF Core implementations of bothISamlSigninStateStoreandISamlLogoutSessionStore. When using the EF operational store, these do not need to be registered separately.
Caching & Validation
Cache duration is controlled by IdentityServerOptions.Caching.SamlServiceProviderStoreExpiration (default: 15 minutes):
All stores are automatically wrapped with ValidatingSamlServiceProviderStore<T> that checks: EntityId required, ≥1 ACS URL (HTTP-POST only), ≥1 AllowedScopes, positive lifetimes. Invalid SPs are treated as non-existent.
Single Logout (SLO)
SLO uses front-channel logout via iframes (not redirect chains):
- SP sends LogoutRequest to
/Saml2/SLO - IdentityServer ends local session
- Renders iframes sending LogoutRequests to all other active SPs
- Collects LogoutResponses from SPs
- Sends final LogoutResponse to originating SP
Key points:
- Partial logout is normal (some SPs may not respond)
- User must stay on logout page for iframes to complete
- Use
ISamlLogoutSessionStorefor distributed deployments (tracks which SPs have active sessions) - Short session lifetimes serve as SLO fallback
IdP-Initiated SSO
⚠️ CSRF Warning: IdP-initiated SSO is inherently vulnerable to CSRF. There is no SAML-compliant way to implement it without CSRF exposure. Only enable it after careful security review.
Recommended alternative: Mimic OIDC third-party initiated login — create a dedicated SP endpoint that accepts a target application hint and redirects the user to the IdP with a standard SP-initiated AuthnRequest. This avoids the CSRF risk entirely.
Enabling per SP: If IdP-initiated SSO is genuinely required, set AllowIdpInitiated = true on the SamlServiceProvider.
No built-in endpoint: There is no built-in IdP-initiated SSO endpoint. Implement your own Razor Page or controller and inject IIdpInitiatedSsoService:
Call CreateResponseAsync from your custom endpoint to generate and return the SAML Response to the SP. The SP must have AllowIdpInitiated = true; otherwise the call will fail.
Extensibility
DI ordering is NOT required: Custom SAML services do not need to be registered before
AddSaml(). Defaults are registered withTryAdd*(e.g.TryAddScoped), so a custom scoped registration takes precedence regardless of order.
State serializer &
Extensions: The defaultISamlSigninStateSerializerignores theExtensionsproperty. To persist custom SAML extension data across the sign-in round-trip, implement a custom serializer.
Example: Custom NameID Generator
SAML Authentication Context in Login UI
Inject IIdentityServerInteractionService and call GetAuthenticationContextAsync(returnUrl); pattern-match the result to SamlAuthenticationContext for customizing login flows per SP.
SamlAuthenticationContext properties:
ServiceProvider— the SP that initiated the requestIdP(string?) — IdP entity ID fromScoping, null if multiple IdPs listedLoginHint(string?) — login hint from NameID in AuthnRequestTenant(string?) — tenant identifier from RequestedAuthnContextPromptModes— derived fromForceAuthnandIsPassiveflagsRelayState(string?) — relay state from the AuthnRequestIsIdpInitiated(bool) — whether this is an IdP-initiated SSO flowRequestedAuthnContext— authentication context requirements from the SPStateId(Guid) — identifier for sign-in state entry; needed when callingDenyAuthenticationAsync
Using IdentityServer as a SAML Service Provider (SP Mode)
IdentityServer can consume SAML assertions from external IdPs via federation. Add a SAML authentication handler and configure it as an external provider in IdentityServer's login UI — same pattern as any external authentication scheme.
Native SAML SP handler (AddSamlServiceProvider)
Register the built-in Duende SAML SP handler as an external scheme feeding the IdentityServer external cookie:
Callback handling (IdP-initiated): authenticate against IdentityServerConstants.ExternalCookieAuthenticationScheme. The IdP-supplied RelayState surfaces at AuthenticationProperties.Items["relayState"] (only when ≤ MaxRelayStateLength); "scheme" and "returnUrl" items are also populated.
⚠️ Security: treat RelayState as untrusted input. Always validate it before using it as a redirect target.
Dynamic providers: the dynamic SamlProvider model gains the same AllowUnsolicitedAuthnResponse and IdpInitiatedCallbackUrl properties. Note the dynamic model uses SigningCertificateBase64 (singular string), whereas the static handler uses SigningCertificatesBase64 (list, supports rollover).
Third-party handlers
Alternatively, use a third-party handler (e.g., Sustainsys.Saml2 or ITfoxtec.Identity.Saml2) configured as an external scheme.
For step-by-step setup instructions, see the official docs: https://docs.duendesoftware.com/identityserver/ui/login/saml-provider/
Managing many SAML IdPs? For scenarios with a large or changing set of external SAML identity providers, consider using dynamic providers instead of static registration. Dynamic providers allow you to manage IdP configurations at runtime without redeployment. See: https://docs.duendesoftware.com/identityserver/ui/login/dynamicproviders/#saml-providers
Common Anti-Patterns
❌ Enabling AllowIdpInitiated on all SPs — only enable where explicitly required (less secure)
❌ Using DoNotSign outside of local testing
❌ Using in-memory SP stores in production
❌ Omitting AllowedScopes — SP gets no claims in the assertion
❌ Configuring ACS URLs with HTTP-Redirect binding (only HTTP-POST is supported)
Common Pitfalls
- Edition requirement:
AddSaml()requires Standard (add-on), Advanced, or Custom Edition license. - ACS binding: Only HTTP-POST is supported for AssertionConsumerServiceUrls. HTTP-Redirect will fail validation.
- Clock skew: Default 5 minutes. Increase if SPs report "response not yet valid" errors.
- Partial SLO: Front-channel logout via iframes means some SPs may not respond. This is expected — don't treat it as an error.
- DenyAuthenticationAsync: Login page must call this for SAML cancellation. Without it, users get stuck if they cancel.
- Operational stores: For multi-node deployments, configure
ISamlSigninStateStoreandISamlLogoutSessionStore(e.g., EF Core, Redis). Without them, SSO/SLO state is lost across nodes. - Certificate rotation: Metadata is cached (default 12h). SPs may not pick up new signing certs until cache expires.
- ClaimMappings vs AllowedScopes: If
AllowedScopesdoesn't include a resource containing a claim type, that claim won't reachClaimMappings.
Related Skills
identityserver-configuration— IdentityServer host configuration and optionsidentityserver-stores— Persistent store patterns (EF Core, custom stores)identity-security-hardening— Key rotation, HTTPS enforcementidentityserver-ui-flows— Login/logout UI flows that SAML integrates withidentityserver-upgrade-v7-to-v8— Migration guide including SAML EF migrations


