> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/federation/saml-service-provider.md).

# OpenIAM as a SAML service provider

Configure OpenIAM as a SAML 2.0 service provider against an external identity provider, keep the assertion consumer service reachable, and prove correlation and entitlement separately.

## What this is, and when you need it

Create a `SAML_SP_PROVIDER` when an **external** identity provider authenticates the user and OpenIAM consumes its assertion — a corporate IdP in front of the Webconsole or SelfService, or a partner tenant signing its own users into your portal. You federate when the population you need to admit already has credentials elsewhere.

This is the opposite direction from [OpenIAM as a SAML identity provider](/administration-guide/federation/saml-identity-provider.md). The two provider types render nearly identical forms, and picking the wrong one is the most common wasted afternoon in this section.

## At a glance

|                          |                                                                                                                                                                                                                                                                                                             |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | One external IdP signing an existing, correlated OpenIAM user into one test route, with `SAML_SP_PROCESS_SAML_RESPONSE` audited, and an unknown user provably refused                                                                                                                                       |
| **Where**                | **Access Control → Authentication Providers → New** (`/webconsole/newAuthProvider`), type **SAML Service Provider**, then one test content provider or URI pattern                                                                                                                                          |
| **You need first**       | The IdP's entity ID, SSO endpoint and binding, its signing certificate, the NameID it will send; the SP entity ID you will register at the IdP; the managed system the assertion correlates to; and four test identities — one correlated, one unknown, one disabled, and a local break-glass administrator |
| **Takes**                | Half a day, plus whatever the partner's own change process costs                                                                                                                                                                                                                                            |
| **Reversible**           | Yes, if you attached it to one narrow route. **No in-product way back** if you attached it to a content provider serving the Webconsole — that is the break-glass case below                                                                                                                                |

{% hint style="danger" %}
This is the change class [Change configuration safely](/administration-guide/start/change-safely.md) calls unrecoverable: you are handing sign-in for a population to a system you do not control. Establish and **test** a break-glass administrator on a content provider that does not use this trust, in a separate browser session, before you attach anything.
{% endhint %}

## Before you begin

Collect from the external IdP or its metadata: the IdP entity ID and its SSO endpoint URL and binding; its signing certificate and its rotation schedule; whether it signs the assertion, the response, or both; the NameID format and value it will send, plus any attributes you will use to correlate; which digest, signature and canonicalization algorithms it supports; and its single logout URL and binding.

Decide on your side: the SP entity ID you will register at the IdP; the authentication policy and password policy the form requires — **both, and the IdP type has neither**; the managed system whose principals the assertion correlates to; and JIT, decided deliberately and **off** for the first test.

{% hint style="warning" %}
**One path does two jobs, and it is what eats the first attempt at every inbound SAML integration.** `/idp/saml2/sp/login` is both the SP-initiated *entry point* and the *Assertion Consumer Service* the external IdP returns its answer to. So **do not protect `/idp/saml2/sp/login` with a rule that redirects to this same external IdP**: the assertion comes back to the very path whose protection would send the browser to the IdP again, and you get a redirect loop. Make the ACS pattern public, or protect it with a rule the returning request already satisfies. Register the **external, public** host the browser and the IdP see, not an internal container hostname.
{% endhint %}

## Configure the trust

{% stepper %}
{% step %}

### Create the provider, with the two policies this type requires

**Access Control → Authentication Providers → New**, type **SAML Service Provider**. Use **Import from Identity Provider Metadata** when you have trusted metadata; review every imported value and the certificate before saving. Name it for the IdP and the environment, and put the IdP, owner, routing scope, JIT decision and certificate expiry in the description.

| Field                 | What to select                                                                                                                                  |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Managed System        | The managed system representing the external IdP or domain. Correlation happens against **this** system's principals                            |
| Authentication Policy | The policy OpenIAM applies after SAML authentication. This type both has the field and requires it, so the form will not save while it is empty |
| Password Policy       | Also present and also required. It matters for a JIT-created identity even though SAML itself sets no password                                  |
| {% endstep %}         |                                                                                                                                                 |

{% step %}

### Fill the trust identity

| Field             | Attribute ID              | Value                                                                              |
| ----------------- | ------------------------- | ---------------------------------------------------------------------------------- |
| SAML Issuer Name  | `SamlSPIssuerName`        | Your SP entity ID. Register the identical string at the IdP                        |
| Sign-in page URL  | `SamlSPLoginURL`          | The **IdP's** SSO endpoint, from its metadata                                      |
| Sign-out page URL | `SamlSPLogoutURL`         | The IdP's SLO endpoint. Required by the form regardless of whether SLO is in scope |
| Issuer Format     | `SamlRequestIssuerFormat` | Only if the IdP specifies one. Do not invent a URI                                 |

`SamlSPLoginURL` is the **IdP's** SSO endpoint, not yours. Its label *"Sign-in page URL"* and its description *"URL for signing in to your system"* both read the wrong way round; the behaviour is what matters, and you can see it directly — whatever you put here is the URL the browser is sent to when you start an SP-initiated sign-in.
{% endstep %}

{% step %}

### Save once, then publish your metadata

{% code overflow="wrap" %}

```
https://<OPENIAM_HOST>/idp/saml2/sp/metadata/<PROVIDER_ID>
```

{% endcode %}

Register that URL at the IdP. Confirm the ACS in the published metadata is `https://<OPENIAM_HOST>/idp/saml2/sp/login` with the **external** host, not an internal one — if it shows an internal hostname, the proxy's `Host` and `X-Forwarded-Proto` handling is the cause and the fix is deployment-side.
{% endstep %}

{% step %}

### Configure trust, and demand a signature

Import the IdP's certificate as the validation public key; inspect subject, issuer, fingerprint and validity. This type offers a **validation** certificate field and **no** private-key field — that is the visible sign you are on the right provider type.

Set `SPWantAssertionsSigned` and/or `SPWantResponseSigned` to match the IdP's contract. **Set at least one.** Do not turn signature validation off to work around a certificate mismatch — that converts a configuration problem into an authentication bypass.

If you enable `SPSignAuthnRequests`, remember `SPSignatureAlgorithm` defaults to SHA-384 and `SpDigestAlgorithm` has **no** default at all, so an IdP that only verifies SHA-256 will reject a signed request from a default configuration.
{% endstep %}

{% step %}

### Configure NameID, logout and relay state: only what the IdP asked for

`SPNameIdToSendInSAMLRequest` only if the IdP wants a `NameIDPolicy` format; leave it empty and the outbound `AuthnRequest` carries no `Format`. `SpNameQualifierForSP`, `SpAuthnRequestDestination` and `SpAuthnContextClassRef` only on request — each of them makes the request stricter, and some IdPs refuse a request that asks for something they cannot satisfy. `SPLogoutMethodBinding` to a binding the IdP supports.

Choose relay state deliberately, and never reflect an arbitrary external URL. Where no relay state applies the browser goes to `org.openiam.sp.login.redirect.success.url`, shipped `/selfservice`.
{% endstep %}

{% step %}

### Configure incoming attribute mapping

After the provider is saved, the attribute table maps assertion values. Enter the exact SAML attribute **Name**, its data type, and the target OpenIAM attribute or a reviewed Groovy script.

Test null, missing, duplicate, case-variant and multi-valued input. **Do not grant roles, groups or target-system access from an unvalidated external claim** — that turns a trust into a privilege escalation path.
{% endstep %}

{% step %}

### Attach it to one narrow route, and keep the ACS reachable

Associate the provider with one test content provider or one exact URI pattern. Then check three things:

* The ACS, `/idp/saml2/sp/login`, is not protected by a rule that redirects to this IdP.
* The logout and health-check paths are not protected by a rule that prevents them completing.
* Your break-glass route does **not** match the external-IdP route. Confirm by signing in as the break-glass account in a separate session, right now.

A URI pattern's rules *replace* the content provider's, so a pattern with its own rule stops honouring `DEF_PASSWD_AUTH_RULE` — which is exactly how a route becomes external-IdP-only, deliberately or by accident. See [The authentication model](/administration-guide/security/authentication-model.md).
{% endstep %}

{% step %}

### Leave JIT off, and prove it is off

`SamlSpJITGroovyScript` empty. Prove the trust with an existing correlated user first. An unknown user must be **rejected**: with JIT off and the lookup failing, OpenIAM refuses the sign-in with *"Could not find principal '%s' using managed System '%s"*, misplaced closing quote and all.

Seeing that message for a deliberately unknown user is your proof that JIT really is off — an empty field alone is not proof, because a script left on the provider from an earlier test would not show up here. See [Just-in-time provisioning](/administration-guide/federation/jit-provisioning.md) when you are ready to turn it on.
{% endstep %}
{% endstepper %}

## Verification

Work through these in order; the negatives are where the value is.

| What to check                                 | How                                                                                                                                                                                                                                                                                                                                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The provider is live                          | 60 s: `org.openiam.auth.provider.sweep.time`                                                                                                                                                                                                                                                                                                                                    |
| The ACS you published is reachable and public | `/idp/saml2/sp/metadata/<PROVIDER_ID>` shows the external host, and the IdP's answer to `/idp/saml2/sp/login` is not redirected back to the IdP                                                                                                                                                                                                                                 |
| The outbound request is what you configured   | Start a sign-in at `https://<OPENIAM_HOST>/idp/saml2/sp/login` from the test route and capture the `AuthnRequest` in a SAML tracer: issuer, destination, binding, request id, `NameIDPolicy`, `RequestedAuthnContext`, signature. `Destination` and `RequestedAuthnContext` must be present **only** if you asked for them                                                      |
| The inbound response validates                | Authenticate at the IdP, capture its answer in non-production, and verify issuer, audience, recipient/ACS, `InResponseTo`, `NotBefore` / `NotOnOrAfter`, signature, NameID and attributes. Clock tolerance is `org.openiam.saml.time.threshold.minutes`, shipped `10`                                                                                                           |
| Correlation and entitlement, **separately**   | OpenIAM must resolve the intended existing user, **and** that user must be entitled to the provider's resource. A user who resolves but is not entitled is refused with `SAML_USER_NOT_AUTHORIZED_TO_SSO`. Test the two apart, or you will read an entitlement failure as a correlation failure                                                                                 |
| JIT is off                                    | `SamlSpJITGroovyScript` has no value, **and** a deliberately unknown user is refused with *"Could not find principal"*                                                                                                                                                                                                                                                          |
| Signature validation is on                    | `SPWantAssertionsSigned` or `SPWantResponseSigned` is set. Present a tampered assertion and confirm rejection                                                                                                                                                                                                                                                                   |
| Attributes persisted                          | Re-open the provider and read the fields back. For the stored values rather than the rendered ones, the read-only break-glass query is `SELECT AUTH_ATTRIBUTE_ID, VALUE FROM AUTH_PROVIDER_ATTRIBUTE WHERE PROVIDER_ID='<PROVIDER_ID>'` — the only way to see an attribute the form does not render                                                                             |
| The negatives all fail                        | An unknown user with JIT off; a disabled OpenIAM user; a missing or duplicate correlation attribute; a wrong audience; an expired assertion; an untrusted or rotated-out certificate; an unsolicited response if your contract disallows it; a replay. None may produce a session                                                                                               |
| Logout, both directions                       | Sign out at `https://<OPENIAM_HOST>/idp/saml2/sp/logout/<PROVIDER_ID>` and record what actually ends — the OpenIAM session, the IdP session, both, or neither. Then have the IdP send a logout to `/idp/saml2/sp/logout`                                                                                                                                                        |
| Break-glass, again                            | In a separate session, confirm the local administrator still signs in                                                                                                                                                                                                                                                                                                           |
| Audit records exist                           | `SAML_SP_GENERATE_SAML_REQUEST` (an outbound `AuthnRequest`), `SAML_SP_PROCESS_SAML_RESPONSE` (an inbound assertion), `SAML_SP_GENERATE_SLO_REQUEST` and `SAML_SP_PROCESS_SLO_REQUEST`. A sign-in that went through JIT also carries the `JUST_IN_TIME_AUTH` audit attribute. See [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md) |
| Both sides agree                              | Correlate `SAML_SP_PROCESS_SAML_RESPONSE` with the IdP's own event                                                                                                                                                                                                                                                                                                              |
| Debugging                                     | `/idp/debugSAMLRequest` and `/idp/debugSAMLResponse`                                                                                                                                                                                                                                                                                                                            |

## When it goes wrong

| Symptom                                                                                      | Cause                                                                                                                                  | Fix                                                                                                                                                                                                                                                                                                                |
| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Redirect loop on first attempt                                                               | The ACS, `/idp/saml2/sp/login`, is protected by a rule that redirects to this IdP                                                      | Make the ACS pattern public, or use a rule the returning request satisfies                                                                                                                                                                                                                                         |
| The IdP reports an unknown SP or entity                                                      | `SamlSPIssuerName` does not match the registered SP entity ID                                                                          | Make them identical and refresh metadata at the IdP                                                                                                                                                                                                                                                                |
| The IdP rejects the ACS                                                                      | The registered ACS is not `https://<external host>/idp/saml2/sp/login`                                                                 | Register the exact external URL. If your published metadata shows an internal hostname, the proxy's `Host` and `X-Forwarded-Proto` handling is the cause and the fix is deployment-side — [Installation Guide](/installation/installation.md)                                                                      |
| The IdP rejects a signed `AuthnRequest`                                                      | `SPSignatureAlgorithm` defaults to `rsa-sha384` and `SpDigestAlgorithm` has no default                                                 | Set both to what the IdP verifies                                                                                                                                                                                                                                                                                  |
| OpenIAM rejects issuer or audience                                                           | The IdP entity ID or the response audience does not match the trust                                                                    | Fix the IdP application. Do not weaken audience validation                                                                                                                                                                                                                                                         |
| Signature validation fails                                                                   | Wrong, rotated or expired IdP certificate; signed element mismatch; algorithm mismatch                                                 | Import the current certificate. Never disable validation to diagnose                                                                                                                                                                                                                                               |
| Assertion expired or not yet valid                                                           | Clock skew between the IdP and OpenIAM                                                                                                 | `org.openiam.saml.time.threshold.minutes` is `10`, and widening it hides a systemic problem. Compare `NotBefore` / `NotOnOrAfter` in the assertion with the audit record's timestamp to size the drift; correcting time synchronization is a deployment task — [Installation Guide](/installation/installation.md) |
| The IdP rejects the request for asking too much                                              | `SpAuthnContextClassRef` is set, so a `RequestedAuthnContext` is emitted                                                               | Clear it unless the IdP asked                                                                                                                                                                                                                                                                                      |
| The IdP rejects `Destination`                                                                | `SpAuthnRequestDestination` is on, and the value OpenIAM emits is the **IdP's** SSO endpoint despite what the field's description says | Turn it off, or compare the emitted value with the IdP's metadata character for character                                                                                                                                                                                                                          |
| No `NameIDPolicy` is sent                                                                    | Only `SPNameIdToSendInSAMLRequest` controls it                                                                                         | Set the format string                                                                                                                                                                                                                                                                                              |
| The IdP demands `AllowCreate` in the `NameIDPolicy`                                          | `SpNameIdAllowCreate` saves but is never applied                                                                                       | No configuration produces it; confirm from the outbound `AuthnRequest` and raise it as a product gap                                                                                                                                                                                                               |
| Existing user not found                                                                      | NameID or attribute correlation, normalisation, or the wrong managed system                                                            | Fix correlation. **Do not enable JIT as a workaround**                                                                                                                                                                                                                                                             |
| Duplicate users appear                                                                       | JIT is on and the lookup key is unstable or case-sensitive                                                                             | Disable the test route, remediate under identity policy, then fix the script                                                                                                                                                                                                                                       |
| A JIT-created user is created and then refused                                               | JIT runs before the entitlement check                                                                                                  | Expected. Grant the resource, or reject earlier inside the script — [Just-in-time provisioning](/administration-guide/federation/jit-provisioning.md)                                                                                                                                                              |
| Sign-in succeeds but access is wrong                                                         | Effective roles, groups and resources, or claim-to-access mapping                                                                      | Correct access independently of authentication                                                                                                                                                                                                                                                                     |
| Chaining fails with *"No attribute named 'lastSAMLMessageId' found for the current session"* | The chained `AuthnRequest` is not in Redis for this session                                                                            | A deployment matter — session affinity and Redis availability, [Installation Guide](/installation/installation.md)                                                                                                                                                                                                 |
| The user lands somewhere unexpected after sign-in                                            | `SPStaticRelayState`, `SPRelayStateGenerator`, or the default `org.openiam.sp.login.redirect.success.url` = `/selfservice`             | Set relay state deliberately                                                                                                                                                                                                                                                                                       |
| Administrators locked out                                                                    | The trust was attached to a content provider serving the Webconsole                                                                    | There is no in-product way back; see the break-glass recovery below                                                                                                                                                                                                                                                |

{% hint style="danger" %}
**Break-glass: undoing a trust that locked everyone out.** If the SAML SP trust was attached to a content provider serving the Webconsole and no one can sign in, nothing in the product can undo it — the screen you would use is behind the trust. The recovery is at the database, and it is outside the supported change path.

Look first, without changing anything, at what the affected content provider now demands:

{% code overflow="wrap" %}

```sql
SELECT CONTENT_PROVIDER_ID, RULE_ID, EXEC_ORDER FROM CP_RULE_XREF
 WHERE CONTENT_PROVIDER_ID = '<CONTENT_PROVIDER_ID>' ORDER BY EXEC_ORDER;
```

{% endcode %}

Then remove only the row that attached the external trust, leaving `DEF_PASSWD_AUTH_RULE` in place, and wait out `org.openiam.auth.provider.sweep.time` (60 s) before retrying sign-in.

What this costs: the deletion produces **no audit record**, so the change is invisible to anyone reviewing the trail afterwards — write it into your own change record. It also validates nothing, so a wrong `CONTENT_PROVIDER_ID` silently un-protects a route you meant to keep protected; read before you delete. The supported alternative is to have the deployment owner or OpenIAM support make the change, which is slower but auditable.
{% endhint %}

## Rollback and retirement

Remove the test URI or content-provider association first and prove local sign-in. Then clear `SamlSpJITGroovyScript` — an active JIT path is the part that creates state you cannot un-create. Then disable the external IdP application. Preserve certificate overlap during rotation.

Delete the provider only after content-provider, URI-pattern, chain, managed-system, script and active-session dependencies are gone. Retain redacted SAML traces, fingerprints, audit events and the result of the break-glass test.

## The endpoints OpenIAM exposes

The SP paths sit under `/idp/saml2/sp`, where `/idp` is `org.openiam.idp.context.path`.

These are the values you register at the external IdP, or link from your own UI.

| Purpose                                                          | Concrete URL                                                       | Audited as                                                        |
| ---------------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- |
| SP metadata, published as XML                                    | `https://<OPENIAM_HOST>/idp/saml2/sp/metadata/{serviceProviderId}` | —                                                                 |
| **Assertion Consumer Service**, and the SP-initiated entry point | `https://<OPENIAM_HOST>/idp/saml2/sp/login`                        | `SAML_SP_GENERATE_SAML_REQUEST` / `SAML_SP_PROCESS_SAML_RESPONSE` |
| Single logout, per provider                                      | `https://<OPENIAM_HOST>/idp/saml2/sp/logout/{serviceProviderId}`   | `SAML_SP_GENERATE_SLO_REQUEST`                                    |
| Single logout, generic                                           | `https://<OPENIAM_HOST>/idp/saml2/sp/logout`                       | `SAML_SP_PROCESS_SLO_REQUEST`                                     |

**Both logout URLs exist and they differ.** `/logout/{serviceProviderId}` starts a logout toward the named IdP; the bare `/logout` receives an inbound one. Register the bare `/logout` with the IdP; link `/logout/{serviceProviderId}` from your own UI.

## Every SP attribute key, with its shipped default

`AUTH_ATTRIBUTE` rows with `PROVIDER_TYPE='SAML_SP_PROVIDER'`. The **Property** column is the fixed key that binds each field to its stored `AUTH_ATTRIBUTE_ID`; nothing on screen shows you the ID, so these tables are the mapping between what you set and what is stored. The whole set was cleared and rebuilt in 3.2.0.0, so nothing about the SP attributes predates that release.

### Identity of the trust

| ID                        | Label             | Required | Shipped default | Property                                       |
| ------------------------- | ----------------- | -------- | --------------- | ---------------------------------------------- |
| `SamlSPIssuerName`        | SAML Issuer Name  | **Y**    | —               | `org.openiam.saml.sp.attribute.issuer.name.id` |
| `SamlSPLoginURL`          | Sign-in page URL  | **Y**    | —               | `...attribute.login.url.id`                    |
| `SamlSPLogoutURL`         | Sign-out page URL | **Y**    | —               | `...attribute.logout.url.id`                   |
| `SamlRequestIssuerFormat` | Issuer Format     | N        | —               | `org.openiam.saml.sp.issuer.format`            |

All four have shipped since 3.2.0.0 except `SamlRequestIssuerFormat`, which arrived in 3.3.1.0 and is described as *"The Format of the AuthnRequest Issuer"*. `SamlSPIssuerName`'s description says it plainly: *"The Issuer to use. This will be the Request Issuer in the IDP"*. All three of the required fields are `REQUIRED='Y'`, so the form will not save without a logout URL even when single logout is out of scope.

### Signing, digest and canonicalization

| ID                       | Label                           | Shipped default                                     | Property                                               | Since    |
| ------------------------ | ------------------------------- | --------------------------------------------------- | ------------------------------------------------------ | -------- |
| `SPSignatureAlgorithm`   | Signature Algorithm             | `http://www.w3.org/2001/04/xmldsig-more#rsa-sha384` | `org.openiam.saml.sp.signature.algorithm.attribute.id` | 4.0.0.0  |
| `SPCanonicalAlgorithm`   | Canonicalized Algorithm         | `http://www.w3.org/2001/10/xml-exc-c14n#`           | `...cannonical.algorithm.attribute.id`                 | 4.0.0.0  |
| `SpDigestAlgorithm`      | SP Digest Algorithm             | —                                                   | `...digest.algorithm.attribute.id`                     | 4.0.0.0  |
| `SPSignAuthnRequests`    | SP Sign Authentication Requests | —                                                   | `org.openiam.saml.sp.saml.sign.authn.reqeusts`         | 4.0.0.0  |
| `SPWantAssertionsSigned` | SP Want Assertions Signed       | —                                                   | `...want.assertions.signed`                            | 4.0.0.0  |
| `SPWantResponseSigned`   | SP Want Response Signed         | —                                                   | `...want.response.signed`                              | 4.2.1.14 |

The SP signature and canonicalization defaults are the same SHA-384 values as the IdP's, and they have been since 4.0.0.0. They apply when OpenIAM signs its own `AuthnRequest` or `LogoutRequest`. `SpDigestAlgorithm` has **no** default at all, unlike the IdP's `IdpDigestAlgorithm` — set it explicitly when you enable request signing, and read the algorithm URI out of the `Signature` block of the `AuthnRequest` in a SAML tracer to see what you are actually sending. The property key `org.openiam.saml.sp.saml.sign.authn.reqeusts` contains a typo; use it as written.

On this side of the trust, `SPWantAssertionsSigned` and `SPWantResponseSigned` mean what they say: what OpenIAM demands of the **incoming** message.

### NameID and the request

| ID                            | Label                              | Shipped default | Property                                                                     | Since                       |
| ----------------------------- | ---------------------------------- | --------------- | ---------------------------------------------------------------------------- | --------------------------- |
| `SPNameIdToSendInSAMLRequest` | NameID IN SAML Request             | —               | `org.openiam.saml.sp.saml.request.name.id.format`                            | 3.3.1.0                     |
| `SpNameIdAllowCreate`         | AllowCreate on the NameIdPolicy    | —               | `org.openiam.saml.sp.nameid.allow.create`, `booleanValue`                    | 3.3.1.0 — **has no effect** |
| `SpNameQualifierForSP`        | SP Name Qualifier                  | —               | `org.openiam.saml.sp.name.qualifier`                                         | 3.3.1.0                     |
| `SpAuthnContextClassRef`      | AuthnContextClassRef element value | —               | `org.openiam.saml.sp.context.class.ref`                                      | 3.3.1.0                     |
| `SpAuthnRequestDestination`   | Destination attribute enabled      | —               | `org.openiam.saml.sp.authn.request.destination.attribute.id`, `booleanValue` | 3.3.1.0                     |
| `NameIDGroovyScriptResolver`  | NameID Resolver Groovy Script      | —               | `org.openiam.saml.sp.saml.name.id.resolver.groovy.script`                    | 3.3.3.0                     |

None has a default. Four need a note.

{% hint style="warning" %}
**`SpNameIdAllowCreate` has no effect.** The field saves, and nothing reads it. The `NameIDPolicy` OpenIAM emits carries `SPNameQualifier` and `Format` but never `AllowCreate`, whatever this field is set to.

Confirm it yourself in one round trip: tick the box, capture the outbound `AuthnRequest` in a SAML tracer, and look at the `NameIDPolicy` element — there is no `AllowCreate` attribute on it. If an IdP requires `AllowCreate="true"`, no setting on this page will produce it; raise it as a product gap rather than looking for another field.
{% endhint %}

**Send-NameID-in-request.** A boolean `SendNameIdInSAMLRequest` existed briefly within 3.3.1.0 and was removed again in the same release, leaving only `SPNameIdToSendInSAMLRequest` as a single value described as *"NameID Format to send in the NameIDPolicy of the SAMLRequest"*. In a fully migrated database the boolean does not exist, so if you are following older notes that mention it, stop. The one remaining field decides both *whether* a `NameIDPolicy` format is emitted and *which* format it is: leave it empty and the outbound `AuthnRequest` carries no `Format`.

**`SpAuthnRequestDestination`.** Its shipped description is wrong. The description reads *"Does the AuthnRequest has a Destination attribute? If true, it will be set to the value of the ACS of this Service Provider"* — but the value OpenIAM actually emits is the **IdP's** SSO endpoint, the one you put in `SamlSPLoginURL`. That is the correct behaviour per SAML, where `Destination` must be the recipient's endpoint, so ignore the description and check the wire. Some IdPs require the attribute and some reject a value that does not equal their own endpoint exactly, so enable it only on request.

**`SpAuthnContextClassRef`.** *"If present, the AuthnRequest will contain a RequestedAuthnContext element with a AuthnContextClassRef"* — its presence is the switch. Some IdPs fail a request that asks for a context they cannot satisfy, so leave it empty unless the IdP asked for it.

**`NameIDGroovyScriptResolver`.** Its shipped description is the clearest statement of when to use it: *"This groovy script gets called in order to coorelate the SAML Response to a valid User in OpenIAM. This gives the user additional flexibility in case matching by NameID is not enough."* Extend `DefaultServiceProviderNameIDResolver` and override `getPrincipal(managedSysId, justInTimeAuthEnabled)`, which is the method the SP flow calls. Note the second argument: your resolver is told whether JIT is on, so it can behave differently for a user it expects not to find.

### Logout, chaining, post-processing and JIT

| ID                      | Label                                   | Property                                                           | Since   |
| ----------------------- | --------------------------------------- | ------------------------------------------------------------------ | ------- |
| `SPLoginMethodBinding`  | SP Login Method Binding                 | `org.openiam.saml.sp.saml.login.binding`                           | 4.0.0.0 |
| `SPLogoutMethodBinding` | SP Logout Method Binding                | `...logout.binding`                                                | 4.0.0.0 |
| `SPStaticRelayState`    | SP Static Relay State                   | `org.openiam.saml.sp.saml.relay.state.static.value`                | 4.0.0.0 |
| `SPRelayStateGenerator` | Relay State Generator Groovy Script     | `...relay.state.groovy.script`                                     | 4.0.0.0 |
| `SPChainingEnabled`     | SP Chaining Enabled                     | `org.openiam.saml.sp.saml.chaining.enabled`, `booleanValue`        | 4.0.0.0 |
| `SPPostProcessorScript` | SP Post Processor Script                | `org.openiam.saml.sp.saml.post.processor.script`                   | 4.1.6.0 |
| `SamlSpJITGroovyScript` | Just-in-time provisioning groovy script | `org.openiam.saml.sp.attribute.issuer.justintime.groovy.script.id` | 3.3.0.0 |

None has a default. `SPRelayStateGenerator`'s description is the safety statement: *"If defined, this script is used to determine the redirect URL after successful SAML Authentication into the Service Provider"* — a script that can emit an arbitrary URL is an open redirect, so allow-list the destination inside the script.

`SamlSpJITGroovyScript` is the whole of [Just-in-time provisioning](/administration-guide/federation/jit-provisioning.md). Its mere presence enables JIT — there is no separate switch, and that is the single most consequential fact about this provider type. Only the two SAML provider types can be chained; no other type offers the field.

## Multiple IdPs

For several external IdPs or partner tenants, keep issuer trust, certificates, routing, correlation namespace, JIT population and break-glass behaviour **explicit per provider**. Two specific tests:

* Two identities that look identical from two different issuers. They must not collapse into one OpenIAM user. Email is not a safe cross-issuer key.
* A user from IdP A reaching the route configured for IdP B. The audience and issuer checks must refuse it.

Provider chaining (`SPChainingEnabled`) is for an intentional chain, and the chained path reads the stored `AuthnRequest` back out of Redis, correlated by the `lastSAMLMessageId` cookie. When it is not there, the sign-in fails with *"No attribute named 'lastSAMLMessageId' found for the current session"*. Session affinity and Redis availability are therefore prerequisites for chaining, and both are deployment concerns — see the [Installation Guide](/installation/installation.md). What you can observe from your side is that exact message, plus a missing `lastSAMLMessageId` cookie in the browser.

## Related

* [OpenIAM as a SAML identity provider](/administration-guide/federation/saml-identity-provider.md) — the outbound direction
* [Just-in-time provisioning](/administration-guide/federation/jit-provisioning.md) — `SamlSpJITGroovyScript` in full
* [The authentication model](/administration-guide/security/authentication-model.md) — why a URI pattern's rules replace a content provider's, and how the ACS gets protected by accident

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/federation/saml-service-provider.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
