Skip to content
Introducing the next era of Duende IdentityServer. Read our CEO’s announcement

SAML Configuration

This page documents the configuration options and models for the SAML 2.0 Identity Provider feature.

Call AddSaml() on the IdentityServer builder to enable SAML 2.0 support:

Program.cs
builder.Services.AddIdentityServer()
.AddSaml();

AddSaml() registers all SAML services and endpoints. You can call it with no arguments when all Service Provider configuration is managed via a store (the IdP’s entity ID and other defaults are derived automatically). Pass an options callback to configure protocol-level settings explicitly.

SamlOptions controls the global behavior of the SAML 2.0 Identity Provider: how claims map to SAML attributes, how assertions are signed, how NameIDs are resolved, and what tolerances apply to timestamps and request lifetimes.

Access SamlOptions when calling AddSaml():

Program.cs
builder.Services.AddIdentityServer()
.AddSaml(saml =>
{
saml.DefaultSigningBehavior = SamlSigningBehavior.SignAssertion;
saml.DefaultClockSkew = TimeSpan.FromMinutes(5);
saml.WantAuthnRequestsSigned = true;
});
});

Use SamlOptions when you need to set defaults that apply across all Service Providers (for example, a shared assertion lifetime, a common set of AuthnContext mappings, or a global signing policy). Individual SPs can override most of these defaults via their own SamlServiceProvider configuration.

Available options:

  • EntityId The SAML entity identifier for IdentityServer when acting as an IdP. Most deployments do not need to set this; the default value is derived from the host URL combined with EntityIdPath. Defaults to {host}/Saml2.

  • EntityIdPath The path segment appended to the host URL to form the default EntityId. Defaults to /Saml2.

  • SigninStateLifetime How long sign-in request state is retained while the user authenticates. This controls the TTL for records in the ISamlSigninStateStore. Defaults to 15 minutes.

  • LogoutSessionLifetime Controls how long logout session tracking state is retained while front-channel logout completes. This controls the TTL for records in the ISamlLogoutSessionStore. Defaults to 5 minutes.

  • MaxMessageSize Maximum size (in characters) of inbound SAML messages that IdentityServer will accept. Messages exceeding this limit are rejected. Defaults to 1,048,576 (1 MB).

  • Endpoints Configures the URL paths and supported bindings for SAML endpoints. See SamlEndpointOptions below.

  • Metadata Configures metadata document generation. See SamlMetadataOptions below.

  • WantAuthnRequestsSigned When true, the IdP requires all AuthnRequests to be signed. Defaults to true.

  • RequireSignedLogoutResponses When true, IdentityServer requires LogoutResponse messages from SPs to be signed. Defaults to true, per SAML 2.0 Profiles §4.4.4. Individual SPs can override this via SamlServiceProvider.RequireSignedLogoutResponses.

  • DefaultClaimMappings Maps OIDC claim types to SAML attribute names. See Default Claim Mappings below.

  • SupportedNameIdFormats Supported NameID formats advertised by the IdP. Defaults to [ EmailAddress, Unspecified ].

    The NameID format determines how the user is identified to the SP. emailAddress uses the user’s email claim and is human-readable but is considered a good identifier. Unspecified uses the user’s sub claim value. Inbound AuthnRequests are validated against the formats configured here; requests specifying an unsupported format are rejected. If you implement a custom NameID format via ISamlNameIdGenerator, add it to this list so that validation passes. See Name Identifiers for a full explanation.

  • DefaultClockSkew Clock skew tolerance for validating SAML message timestamps. Defaults to 5 minutes.

  • DefaultRequestMaxAge Maximum age for SAML AuthnRequests. Defaults to 5 minutes.

  • DefaultSigningBehavior Default signing behavior for SAML responses. Defaults to SignAssertion.

  • MaxRelayStateLength Maximum length (in UTF-8 bytes) of the RelayState parameter. Defaults to 80.

    RelayState is an opaque string that an SP may include in its AuthnRequest to preserve application state across the SSO round-trip. IdentityServer echoes it back unchanged so the SP can keep state that it needs for processing after authentication. The SAML specification mandates that RelayState MUST NOT exceed 80 bytes in length; this limit enforces that requirement. See RelayState for more context.

  • DefaultAuthnContextMappings Maps OIDC acr/amr values to SAML AuthnContextClassRef URIs. Used when an SP requests a specific AuthnContext and IdentityServer needs to translate the user’s authentication method into the corresponding SAML identifier (a URI).

    Default mappings include pwdurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport and externalurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified.

    Per-SP overrides are set via SamlServiceProvider.AuthnContextMappings.

  • DefaultAssertionLifetime How long issued assertions are considered valid. Defaults to 5 minutes. Per-SP overrides are set via SamlServiceProvider.AssertionLifetime.

  • EmailNameIdClaimType The claim type used to resolve an email-format NameID. Defaults to "email". Per-SP overrides are set via SamlServiceProvider.EmailNameIdClaimType.

These three optional callbacks let you observe and react to errors that occur while IdentityServer parses or validates incoming SAML messages. They’re particularly useful when debugging interoperability issues with a specific SP, because they give you access to the raw XML and error details before IdentityServer returns a failure response. The inspector may suppress or even fix errors by inspecting the XML node and setting the corresponding value in the parsed object. A common example of suppression is that while the SAML specification requires absolute URIs for all identifiers, many deployments use simple strings. This is an error that can be safely suppressed if needed.

None of these are required. When not set, IdentityServer handles errors using its default behavior.

  • AuthnRequestErrorInspector A callback invoked when an error occurs while parsing or validating an incoming AuthnRequest.

  • LogoutRequestErrorInspector A callback invoked when an error occurs while parsing or validating an incoming LogoutRequest.

  • LogoutResponseErrorInspector A callback invoked when an error occurs while processing an incoming LogoutResponse from an SP during Single Logout (SLO).

The default DefaultClaimMappings dictionary maps common OIDC claim types to SAML 2.0 attribute names:

Claim typeSAML attribute name
namehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
emailhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
rolehttp://schemas.xmlsoap.org/ws/2005/05/identity/role

Claims not present in this mapping are still included in the assertion but use their original claim type as the attribute name. Override mappings globally via SamlOptions.DefaultClaimMappings or per Service Provider via SamlServiceProvider.ClaimMappings.

SamlMetadataOptions controls how the IdP metadata document is generated. Access it via SamlOptions.Metadata.

  • CacheDuration (TimeSpan) How long consumers (Service Providers and federation tools) should cache the metadata document before re-fetching it. This value is included as the cacheDuration attribute in the metadata XML. Defaults to 12 hours.

  • ExpiryDuration (TimeSpan) How long the metadata document is considered valid. This value is used to compute the validUntil attribute in the metadata XML. After this time, consumers should discard any cached metadata. Defaults to 5 days.

    Using CacheDuration and ExpiryDuration together improves stability. With the default values, a consumer should refresh every 12 hours, but if the refresh fails, it can still continue using the cached metadata for up to 5 days.

Program.cs
builder.Services.AddIdentityServer()
.AddSaml(saml =>
{
saml.Metadata.CacheDuration = TimeSpan.FromHours(6);
saml.Metadata.ExpiryDuration = TimeSpan.FromDays(7);
});

SamlEndpointOptions configures the URL paths and supported bindings for all SAML protocol endpoints. Access it via SamlOptions.Endpoints.

PropertyTypeDefaultDescription
SingleSignOnServicePathstring"/Saml2/SSO"Path for the SSO endpoint (receives AuthnRequests).
SingleSignOnServiceBindingsICollection<string>[HttpRedirect, HttpPost]Bindings advertised in metadata for the SSO endpoint. This controls what appears in the metadata document, not whether the endpoint accepts requests.
SingleSignOnCallbackPathstring"/Saml2/SSO/Callback"Path for the SSO callback endpoint (after user authenticates). This is an internal endpoint that is not visible in the metadata document.
SingleLogoutServicePathstring"/Saml2/SLO"Path for the SLO endpoint (receives LogoutRequests and LogoutResponses).
SingleLogoutServiceBindingsICollection<string>[HttpRedirect, HttpPost]Bindings advertised in metadata for the SLO endpoint. This controls what appears in the metadata document, not whether the endpoint accepts requests.
SingleLogoutCallbackPathstring"/Saml2/SLO/Callback"Path for the SLO callback endpoint (completes the logout flow). This is an internal endpoint that is not visible in the metadata document.
StateIdParameterNamestring"samlStateId"Query string parameter name used to pass the SAML sign-in state identifier through the return URL in the redirect to the login page.
Program.cs
builder.Services.AddIdentityServer()
.AddSaml(saml =>
{
saml.Endpoints.SingleSignOnServicePath = "/Saml2/SSO";
saml.Endpoints.SingleLogoutServicePath = "/Saml2/SLO";
});

SamlServiceProvider represents a registered SAML 2.0 Service Provider. Each SP has its own entity ID, ACS endpoints, signing certificates, and claim configuration. SPs can be registered statically in code or managed dynamically via a custom store.

Most properties on SamlServiceProvider are optional overrides of the global defaults set in SamlOptions. When a property is null, the corresponding SamlOptions default applies. This lets you configure sensible defaults once and only specify per-SP values where behavior needs to differ.

Available options:

  • EntityId (string) The SP’s entity identifier, as declared in its SAML metadata. Required.

  • DisplayName (string) Human-readable name shown in logs and consent screens. Required.

  • Description (string?) Optional description. Defaults to null.

  • Enabled (bool) When false, all SAML requests from this SP are rejected. Defaults to true.

  • ClockSkew (TimeSpan?) Per-SP clock skew override. Uses SamlOptions.DefaultClockSkew when null. Defaults to null.

  • RequestMaxAge (TimeSpan?) Per-SP request maximum age. Uses SamlOptions.DefaultRequestMaxAge when null. Defaults to null.

  • AssertionConsumerServiceUrls (ICollection<IndexedEndpoint>) ACS endpoints where SAML responses will be delivered. At least one is required. Each entry is an IndexedEndpoint that specifies the URL, binding, ordering index, and whether it is the default endpoint. See IndexedEndpoint below.

    AssertionConsumerServiceUrls =
    [
    new()
    {
    Location = "https://sp.example.com/saml/acs",
    Binding = SamlBinding.HttpPost,
    Index = 0,
    IsDefault = true
    }
    ]
  • SingleLogoutServiceUrls (ICollection<SamlEndpointType>) The SP’s Single Logout Service endpoints. Each entry is a SamlEndpointType that pairs a Location (URL) with a Binding (SamlBinding). You can configure multiple endpoints for different bindings. Currently only HTTP Redirect is supported for SLO. Required for SLO support. Defaults to an empty collection. See SamlEndpointType below.

    SingleLogoutServiceUrls =
    [
    new()
    {
    Location = "https://sp.example.com/saml/slo",
    Binding = SamlBinding.HttpRedirect,
    }
    ]
  • RequireSignedAuthnRequests (bool?) When true, unsigned AuthnRequests from this SP are rejected. When null, falls back to the global SamlOptions.WantAuthnRequestsSigned default. Defaults to null.

  • RequireSignedLogoutResponses (bool?) Per-SP override for whether LogoutResponse messages must be signed. When null, falls back to SamlOptions.RequireSignedLogoutResponses. Defaults to null.

  • Certificates (ICollection<ServiceProviderCertificate>?) Certificates associated with this SP, with use annotations indicating whether each certificate is used for signature verification, encryption, or both. See ServiceProviderCertificate below. Defaults to null.

  • AllowIdpInitiated (bool) When true, IdP-initiated SSO is allowed for this SP. Defaults to false.

  • ClaimMappings (IDictionary<string, string>) Per-SP claim-to-attribute mappings (internal claim name → SAML attribute URI) that override SamlOptions.DefaultClaimMappings. Defaults to {}.

  • DefaultNameIdFormat (string) Default NameID format to use when the SP does not specify one. Defaults to urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified.

  • SigningBehavior (SamlSigningBehavior?) Per-SP signing behavior. Uses SamlOptions.DefaultSigningBehavior when null. Defaults to null.

  • AssertionLifetime (TimeSpan?) Per-SP override for how long issued assertions are valid. Uses SamlOptions.DefaultAssertionLifetime when null. Defaults to null.

  • AllowedScopes (ICollection<string>) Identity resource names associated with this SP. Used to determine which identity resources (and their claim types) are available for inclusion in assertions. Only identity resource names are valid here - including API scope names causes resource validation to fail. Should not be empty.

  • AuthnContextMappings (IDictionary<string, string>) Per-SP override for acr/amrAuthnContextClassRef URI mappings. Overrides SamlOptions.DefaultAuthnContextMappings when set. Defaults to empty.

  • RequestedClaimTypes (List<string>) Claim types this SP expects in assertions. Used to drive claim population for the SP.

  • EmailNameIdClaimType (string?) Per-SP override for the claim used to resolve an email-format NameID. Uses SamlOptions.EmailNameIdClaimType when null. Defaults to null.

  • AllowedSignatureAlgorithms (List<string>?) Signature algorithms this SP accepts. When null, the IdP’s default algorithm is used. Defaults to null.

SAML bindings define how messages travel over HTTP. HTTP Redirect encodes the message into the URL query string, which works well for small messages such as AuthnRequest but is limited by URL length constraints. HTTP POST encodes the message in a hidden HTML form field and submits it automatically, making it the right choice for larger payloads (such as assertions with many attributes) and for keeping message content out of server access logs. See Bindings for a deeper explanation.

SamlBinding is used on SamlEndpointType (for each entry in SingleLogoutServiceUrls) and on the derived IndexedEndpoint (for each ACS endpoint in AssertionConsumerServiceUrls).

ValueDescription
HttpRedirectHTTP Redirect binding. The SAML message is sent as a query parameter.
HttpPostHTTP POST binding. The SAML message is sent in an HTML form.

SAML assertions and/or responses are signed with the IdP’s private key to prove their authenticity and prevent tampering. The signing behavior controls which XML elements carry a digital signature. SignResponse is the recommended choice for most deployments: it signs the response, providing integrity protection to the entire SAML message. See Assertions for background on why signing matters.

Controls what elements are signed in SAML responses:

ValueDescription
DoNotSignNo signing. For testing only. Do not use in production.
SignResponseSigns the entire SAML <Response> element. Recommended.
SignAssertionSigns the <Assertion> element inside the response.
SignBothSigns both the <Response> and the <Assertion>. Maximum security, larger messages.

SamlEndpointType is a record that pairs a URL location with a SAML binding. It is used as the element type of SamlServiceProvider.SingleLogoutServiceUrls to describe where the SP’s SLO service lives and which HTTP binding it accepts.

new SamlServiceProvider
{
// ...
SingleLogoutServiceUrls =
[
new SamlEndpointType
{
Location = "https://sp.example.com/saml/slo",
Binding = SamlBinding.HttpRedirect,
}
]
}

Properties:

  • Location (string): The URL of the endpoint.
  • Binding (SamlBinding): The HTTP binding the endpoint accepts.

IndexedEndpoint represents a single Assertion Consumer Service (ACS) endpoint on a Service Provider. It extends the basic location-and-binding pair with an index (for ordering when multiple ACS endpoints are registered) and an optional default flag.

IndexedEndpoint is used as the element type of SamlServiceProvider.AssertionConsumerServiceUrls.

Properties:

  • Location (string): The ACS URL where SAML responses are delivered.
  • Binding (SamlBinding): The HTTP binding the endpoint uses. Must be SamlBinding.HttpPost for the ACS endpoint. HTTP Redirect is not supported for SAML Response delivery.
  • Index (int): Integer index used to order multiple endpoints. Lower values take precedence.
  • IsDefault (bool?): When true, this endpoint is the default endpoint of its kind. When multiple endpoints are registered, exactly one should be marked as default.

Example:

AssertionConsumerServiceUrls =
[
new IndexedEndpoint
{
Location = "https://sp.example.com/saml/acs",
Binding = SamlBinding.HttpPost,
Index = 0,
IsDefault = true
}
]

ServiceProviderCertificate pairs an X.509 certificate with a use annotation that tells IdentityServer how to apply it for a given SP. Use it to configure signature verification certificates, encryption certificates, or certificates that serve both purposes.

Properties:

  • Certificate (X509Certificate2): The X.509 certificate. Required.
  • Use (KeyUse): How the certificate is used. Defaults to KeyUse.Signing. See KeyUse below.

KeyUse is a flags enum that controls how a ServiceProviderCertificate is applied.

ValueDescription
SigningUsed to verify signatures on messages from this SP.
EncryptionUsed to encrypt assertions sent to this SP.
BothUsed for both signature verification and encryption. Equivalent to Signing | Encryption.

The SAML add-on integrates with IdentityServer’s built-in caching infrastructure. When you register a custom SP store with AddSamlServiceProviderStoreCache<T>(), IdentityServer wraps your store with an in-memory cache to reduce repeated lookups.

The cache duration is controlled by SamlServiceProviderStoreExpiration on IdentityServerOptions.Caching:

  • SamlServiceProviderStoreExpiration (TimeSpan) How long SP lookups are cached when you use AddSamlServiceProviderStoreCache<T>(). Defaults to 15 minutes. This setting has no effect unless you call AddSamlServiceProviderStoreCache<T>().
Program.cs
builder.Services
.AddIdentityServer(options =>
{
options.Caching.SamlServiceProviderStoreExpiration = TimeSpan.FromMinutes(30);
})
.AddSaml()
.AddSamlServiceProviderStoreCache<MySamlServiceProviderStore>();

IdP-initiated SSO is a flow where the Identity Provider sends a SAML assertion to a Service Provider without first receiving an AuthnRequest. This is commonly used in portal pages (for example, a “My Apps” dashboard) where the user is already authenticated and clicks a tile to launch an SP application.

There is no built-in endpoint for IdP-initiated SSO. Instead, inject IIdpInitiatedSsoService into your own Razor Pages or controllers to generate and send the SAML response programmatically. See IIdpInitiatedSsoService for usage details.

To allow IdP-initiated SSO for a given SP, set AllowIdpInitiated = true on its SamlServiceProvider configuration:

new SamlServiceProvider
{
EntityId = "https://sp.example.com",
AllowIdpInitiated = true,
// ...
}

For a working example, see the SAML 2.0 IdP-Initiated sample.