> For the complete documentation index, see [llms.txt](https://docs.durohub.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.durohub.com/account-management/enterprise-sso.md).

# Enterprise SSO (SAML)

Let your team sign in to Duro with your company's identity provider — Google Workspace, Microsoft Entra ID, Okta, JumpCloud, or any SAML 2.0 IdP.

Setup is self-serve and takes about twenty minutes. It happens on one screen in Duro, plus one visit to your IdP console and one DNS record. No ticket to Duro, no waiting on anyone.

**Where this lives:**

```
https://durohub.com/org/@<your-org-slug>/settings/authentication/saml
```

Or navigate there: **Organization settings → Authentication → SAML single sign-on**. The page is directly linkable, so if the person who administers your IdP is not you, send them the URL.

## What your members will see

Once this is on, someone signing in types their email address, Duro recognizes the domain, and the form offers **Continue with SSO** instead of asking for a password.

```
  dana@acme.com typed on the Duro sign-in page
        │
        ▼
  Duro recognizes acme.com as your verified domain
        │
        ▼
  "Continue with SSO"  ─────────►  your identity provider
        │                                   │
        │                          user authenticates
        ▼                                   │
  signed assertion posted back to Duro ◄────┘
        │
        ▼
  session issued, browser lands in Duro
```

Duro is the Service Provider, and your IdP asserts directly to Duro. Each organization gets its own Entity ID and Reply URL, so an assertion minted for your organization cannot be used against another.

### Where a failed sign-in lands

SSO can be started from more than one place — the sign-in module embedded on `https://durohub.com/`, the `/login` page, or a link that sent someone to either. Duro remembers which page started the flow and returns the person there if the assertion is rejected, with the error code in the URL, so they see the failure in the form they were already using rather than on a page they never visited.

That memory is a short-lived cookie set when the flow starts. Because your IdP posts the assertion back cross-site, the cookie is issued with `SameSite=None; Secure`, which is what allows it to survive the round trip. A browser configured to block cross-site cookies entirely drops it — the sign-in still fails with the same error code, it just lands on the default sign-in page instead of the one the user started from.

Nothing about this is configurable, and it changes nothing in your IdP. It only affects where the user ends up when something is wrong; the error codes themselves are unchanged and are listed under [Troubleshooting](#troubleshooting).

## Before you start

|                             |                                                                                                                                        |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Duro role**               | A role with the `organization.saml.configure` permission — Site Admin by default                                                       |
| **Plan**                    | Must include the `SSO_SAML` entitlement. Without it the page loads but saving fails with *Single sign-on is not included in your plan* |
| **IdP access**              | Administrator access to your identity provider                                                                                         |
| **DNS access**              | Ability to add a TXT record to your email domain                                                                                       |
| **A way in that isn't SSO** | Sign in with a password or Google to do this setup                                                                                     |

## The page unlocks in order

Worth knowing before you open it. The SAML page is four cards, and each is inert until the one above it is done:

```
  Verify your domain             ← everything else is greyed out until this passes
        ↓
  Identity provider fields       ← unlock once the domain is verified
        ↓
  Enable SAML single sign-on     ← reads "Complete the steps below first"
        ↓                          until an SSO URL is saved
  Create accounts automatically  ← settable only while SAML is enabled
```

So expect to open the **Identity provider** card twice: once to save your IdP details, and again after you switch SAML on if you want automatic account creation. That last switch is deliberately disabled while SAML is off — the server refuses to store it in that state, so a live switch would accept the change and persist nothing.

## Step 1 — Verify your domain

SAML cannot be enabled until you have proven you control the email domain your users sign in with. Your IdP can assert any address it likes; this is what limits it to addresses that are yours.

1. Under **Verify your domain**, type your domain — `acme.com`, no scheme or path — and click **Get DNS record**
2. Duro shows a **Record name** and a **Value**, each with a copy button
3. Add the TXT record at your DNS provider
4. Click **I have added the record**

On success the input disappears and a green tag with your domain appears beside the heading. That tag is how you know the rest of the page is live.

### The DNS record

|                 |                                                                                                       |
| --------------- | ----------------------------------------------------------------------------------------------------- |
| **Type**        | `TXT`                                                                                                 |
| **Name / Host** | `_duro-verification` — most providers append the domain for you, giving `_duro-verification.acme.com` |
| **Value**       | `duro-domain-verification=k3Jd9vQ2…` — the whole string, including the prefix                         |

The token is unique to your organization. DNS changes usually take minutes and sometimes an hour, so click **I have added the record** again rather than assuming failure. Once verified, the record can be removed; verification does not re-run.

{% hint style="info" %}
**One domain, matched exactly.** Duro stores a single verified domain per organization, and the sign-in page routes an address to SSO only when the part after the `@` matches it exactly. With `acme.com` verified, `dana@mail.acme.com` gets the password form, not **Continue with SSO**. Verify the domain your users' addresses actually end in.
{% endhint %}

### If the domain is refused

| What Duro says                                                                     | What it means                                                                                               |
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| *That is a public email provider. Use a domain your organization controls.*        | Consumer mail domains — `gmail.com`, `outlook.com`, `icloud.com` and the like — cannot be claimed by anyone |
| *Another organization has already verified that domain.*                           | Contact Duro support                                                                                        |
| *We could not find the TXT record yet. DNS changes can take a while to propagate.* | Not propagated, or the name or value does not match. Confirm with `dig TXT _duro-verification.acme.com`     |
| *We could not reach DNS to check that record. Try again shortly.*                  | A lookup failure on our side, not a wrong record                                                            |
| *That does not look like a valid domain.*                                          | Use a bare hostname — no scheme, path, port, or wildcard                                                    |

## Step 2 — Copy the two values Duro gives you

{% hint style="info" %}
**Five values change hands, and they travel in opposite directions.** Getting this backwards is the most common reason a setup stalls, because most IdP consoles put both sides on one screen without saying which is which.

|                | Direction           | Values                                           |
| -------------- | ------------------- | ------------------------------------------------ |
| **Step 2 → 3** | Duro **→** your IdP | Entity ID, Reply URL (ACS)                       |
| **Step 3 → 4** | your IdP **→** Duro | SSO URL, Issuer (Entity ID), signing certificate |

Both sides have something called an *Entity ID*. They are different values and neither is optional. Duro's names Duro; your IdP's names your IdP.
{% endhint %}

At the bottom of the page, under **Give these to your identity provider**, are two values with copy buttons:

| Duro shows                                                         | Your IdP may call it                                  |
| ------------------------------------------------------------------ | ----------------------------------------------------- |
| **Entity ID** — `https://api.durohub.com/auth/saml/acme/metadata`  | Entity ID, Identifier, Audience URI, or SP Entity ID  |
| **Reply URL (ACS)** — `https://api.durohub.com/auth/saml/acme/acs` | ACS URL, Reply URL, or Assertion Consumer Service URL |

{% hint style="success" %}
**Skip the transcription.** Duro's Entity ID is also a live SP metadata document — the same URL, served as SAML XML. Most IdPs accept either the URL or a saved copy of it and fill in both fields for you, which removes the whole class of trailing-slash and wrong-host mistakes. Open the Entity ID in a browser tab and save the page if your IdP wants a file rather than a link.

Google Workspace, Entra ID, Okta and JumpCloud all take one or the other.
{% endhint %}

Duro does not sign AuthnRequests, so there is no SP certificate to install.

{% hint style="info" %}
**Duro does not ask for a particular authentication method.** Its AuthnRequests carry no `RequestedAuthnContext`, so how someone proves who they are is entirely your IdP's decision — password, MFA, a certificate, a passkey, Windows Hello, or an existing session it chooses to reuse. Whatever your conditional access or sign-on policy requires is what applies, and Duro accepts the assertion that comes back.
{% endhint %}

{% hint style="warning" %}
**Copy the host from your own settings page, do not assume `api.durohub.com`.** Dedicated tenants are served from their own hostname — `https://acme-api.durohub.com/auth/saml/…`, for example. The values shown under **Give these to your identity provider** are always correct for your deployment; the ones in this guide are illustrative.
{% endhint %}

## Step 3 — Configure your identity provider

You are setting the same four things whatever the IdP:

| Setting              | Value                                  |
| -------------------- | -------------------------------------- |
| Entity ID / Audience | Duro's **Entity ID** from Step 2       |
| ACS / Reply URL      | Duro's **Reply URL (ACS)** from Step 2 |
| NameID format        | Email address                          |
| NameID value         | The user's primary email               |

Then collect three values **back** from your IdP: its SSO URL, its Entity ID, and its signing certificate. Those go into Duro in Step 4.

{% hint style="info" %}
**An Entity ID is an identifier, not an address.** Nothing ever fetches it — not Duro, not your IdP. It looks like a URL by convention only; a URN such as `urn:duro:jumpcloud` is equally valid, and plenty of SAML deployments use one.

On most IdPs you **choose** this value rather than look it up. There is no correct answer to discover. It only has to be unique within your IdP, and character-for-character identical to what you put in Duro's **Issuer (Entity ID)** — the comparison is case-sensitive and exact, with no normalization.

Two things here are genuine addresses and do have to resolve: your IdP's **SSO URL**, and Duro's own **Entity ID**, which is a real URL because it doubles as our SP metadata document.
{% endhint %}

{% hint style="warning" %}
**Duro requires the assertion itself to be signed — not just the response.**

SAML lets an identity provider sign the `<Response>` wrapper, the `<Assertion>` inside it, or both. A number of IdPs sign only the response by default, and Duro rejects that: a signed wrapper around an unsigned assertion leaves the assertion's own contents unauthenticated, which is precisely what assertion-swapping attacks rely on.

Find the signing option in your IdP and set it to **Assertion**, or **Assertion and Response**. Signing the response alone fails every sign-in, and because the rejection happens before Duro trusts anything in the document, the error page cannot tell you which of several causes it was.

This is the single most common reason a correctly-configured SAML app still will not sign anyone in.
{% endhint %}

### Google Workspace

1. [admin.google.com](https://admin.google.com) → **Apps** → **Web and mobile apps** → **Add App** → **Add custom SAML app**
2. Name it `Duro`, click **Continue**
3. **Download the IdP metadata**, or note these manually:

   * **SSO URL** — `https://accounts.google.com/o/saml2/idp?idpid=XXXXXXXXX`
   * **Entity ID** — `https://accounts.google.com/o/saml2?idpid=XXXXXXXXX`
   * **Certificate** — download the `.pem`

   <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>Those two URLs look nearly identical and are not. One has <code>/idp</code>, one has <code>/saml2</code>. Swapping them is the single most common setup mistake.</p></div>
4. On **Service provider details**, paste Duro's Reply URL (ACS) and Entity ID from Step 2
5. **Name ID format**: `EMAIL`. **Name ID**: `Basic Information > Primary email`
6. Skip attribute mapping — click **Finish**
7. Open the app → **User access** → **ON for everyone**, or select specific organizational units → **Save**

Google takes a few minutes to propagate.

### Microsoft Entra ID

1. [entra.microsoft.com](https://entra.microsoft.com) → **Identity** → **Applications** → **Enterprise applications** → **New application** → **Create your own application**
2. Name it `Duro`, choose **Integrate any other application you don't find in the gallery**, click **Create**
3. **Users and groups** → **Add user/group** → assign who should have access
4. **Single sign-on** → **SAML** → **Edit** on **Basic SAML Configuration**:
   * **Identifier (Entity ID)** — Duro's Entity ID
   * **Reply URL** — Duro's Reply URL (ACS)
   * **Sign on URL** — leave blank
5. Save, then under **SAML Certificates** download **Certificate (Base64)**
6. Under **Set up Duro**, copy the **Login URL** — this is Duro's **SSO URL** — and the **Microsoft Entra Identifier**, which is Duro's **Issuer (Entity ID)**

### JumpCloud

1. [console.jumpcloud.com](https://console.jumpcloud.com) → **SSO Applications** → **Add New Application**
2. Search for **Custom Application**, select it, then **Next**
3. Choose **Manage Single Sign-On (SSO)** → **Configure SSO with SAML** → **Next**
4. Name it `Duro`, then **Save Application**
5. Open the app and select the **SSO** tab

Everything else happens on that one tab, which is where the confusion lives — it shows both sides of the exchange at once:

| Field on the SSO tab          | Direction        | What to do                                                                                                                                                                                                                                                          |
| ----------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Service Provider Metadata** | Duro → JumpCloud | Upload Duro's SP metadata. This fills in the next two rows for you                                                                                                                                                                                                  |
| **SP Entity ID**              | Duro → JumpCloud | Duro's **Entity ID**                                                                                                                                                                                                                                                |
| **ACS URLs**, index `0`       | Duro → JumpCloud | Duro's **Reply URL (ACS)**                                                                                                                                                                                                                                          |
| **IdP Entity ID**             | JumpCloud → Duro | **You choose this.** JumpCloud does not generate it — its own tooltip says to enter a value matching the one configured on the service provider. Anything unique works; `urn:duro:jumpcloud` is fine. Copy it verbatim into Duro's **Issuer (Entity ID)** in Step 4 |
| **IDP URL**                   | JumpCloud → Duro | Copy it into Duro's **SSO URL** in Step 4                                                                                                                                                                                                                           |
| **IdP Certificate**           | JumpCloud → Duro | Download it — this is Duro's **Signing certificate**                                                                                                                                                                                                                |

{% hint style="warning" %}
**Two fields on this screen are called an Entity ID, and they are not interchangeable.**

**SP Entity ID** describes Duro and you paste it in. **IdP Entity ID** identifies JumpCloud for this connection and you author it, then carry it over to Duro.

If you are looking at an application someone set up previously and wondering where a value like `https://sso.jumpcloud.com/saml2/duro` came from: whoever built it typed it. It is not derived from anything, and there is no equivalent value waiting to be found for a new application.
{% endhint %}

Then **Save**, open the **User Groups** tab, and assign the groups who should have access — nobody can sign in until you do.

**Set `Sign` to `Assertion and Response`.** It is a radio group near the bottom of the SSO tab, below **Signature Algorithm**, and JumpCloud defaults it to **Response** — which Duro rejects. `Assertion` alone is also fine. Leave **Signature Algorithm** on `RSA-SHA256`.

No attribute mapping is needed. JumpCloud sets NameID to the user's email by default, and Duro reads the email from NameID when no email attribute is present. `SAMLSubject NameID Format` can stay on `unspecified`.

If you would rather read the SSO URL, Entity ID and certificate out of one document than off the form, **Export Metadata** at the top of the SSO tab gives you all three together.

### Okta, OneLogin, and others

Create a generic SAML 2.0 application, map the four settings above, then collect the SSO URL, Issuer / Entity ID, and X.509 signing certificate from the app's setup instructions.

## Step 4 — Paste your IdP's details into Duro

Back on the SAML page, the **Identity provider** card is now editable. Three fields:

| Field                   | What to paste                                 | Notes                                                                                           |
| ----------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| **SSO URL**             | Your IdP's sign-in URL                        | Required. Must be `https` and must resolve publicly                                             |
| **Issuer (Entity ID)**  | Your IdP's Entity ID, **not Duro's**          | JumpCloud calls it **IdP Entity ID**; Entra calls it **Microsoft Entra Identifier**             |
| **Signing certificate** | The PEM text, or click **Upload certificate** | Accepts `.pem`, `.crt`, `.cer`, `.txt`. The file is read in your browser, not uploaded anywhere |

Leave **Create accounts automatically** alone for now — it is greyed out until SAML is enabled. Click **Save**.

Once a certificate is stored, the card shows its **Fingerprint (SHA-256)** and **Expires** date. Every IdP console displays the same fingerprint, so compare the two screens to confirm you installed the right certificate. From then on the field's helper text changes to *Replacing this changes who can sign in* — a reminder that this box is a live dependency of everyone's sign-in, not a draft.

{% hint style="warning" %}
**A blank box means two different things here.**

The **Signing certificate** field is empty every time the page loads, even when a certificate is stored — Duro never sends the PEM back, only its fingerprint. Saving with it blank therefore keeps the stored certificate.

The **Issuer (Entity ID)** field is different: it is filled in from what is saved, so clearing it and saving genuinely clears it.
{% endhint %}

Duro validates before it stores. A certificate is rejected rather than saved if it will not parse, has already expired, is not yet valid, is an RSA key under 2048 bits, or uses a key type SAML IdPs do not issue. The reasoning is the same in every case: an organization with SAML on and an unusable certificate is an organization nobody can sign in to.

## Step 5 — Turn it on and test

Flip **Enable SAML single sign-on** at the top of the page. It commits immediately — there is no Save button for it. Until your domain is verified and an SSO URL is saved it stays disabled and reads *Complete the steps below first*.

Then, in a private browsing window:

1. Go to the Duro sign-in page and enter an address on your verified domain
2. Confirm the form switches to **Continue with SSO** instead of asking for a password
3. Complete the redirect to your IdP and back
4. Check the profile — name and email populated, correct organization
5. Refresh, confirm the session survives, then sign out

Only once that round trip is clean should you consider the two switches below.

## Creating accounts automatically

Return to the **Identity provider** card. **Create accounts automatically** is now live, and turning it on means an assertion for someone with no Duro account creates one. With it off, that person is turned away and your IdP's sign-in appears to succeed while Duro sends them back to the sign-in page they started from, with `saml_no_account` in the URL.

Flip it and click **Save**.

{% hint style="info" %}
This governs **new** accounts only. Someone who already has a Duro account with an address inside your verified domain is attached to your organization either way.

Understand the implication: anyone your IdP will authenticate can obtain a Duro account. For most organizations [SCIM provisioning](/account-management/scim-provisioning.md) is the better answer — it creates accounts deliberately from your directory, and deprovisions them too.
{% endhint %}

Turning SAML off switches off automatic account creation, SSO enforcement, and SCIM along with it.

## Requiring SSO

Once SSO is working, you can stop members signing in with a password.

{% hint style="info" %}
**There is no toggle for this in the web app.** It was removed on purpose: an organization can enable it and lock itself out with no way back in through the product. Ask your Duro contact to set it, or set it through the API — see [Automating this](#automating-this).
{% endhint %}

Password sign-in is then refused with `AUTH_SAML_ENFORCED` — *Your organization requires signing in with SSO*.

{% hint style="warning" %}
**Three things to check before asking for this.**

It follows the person, not the organization they are currently working in. If any organization a user belongs to requires SSO, password sign-in is refused for them everywhere.

It breaks CAD plugins that sign in with a password rather than through a browser. The Onshape and SolidWorks integrations authenticate through a real browser and are unaffected; the Altium plugin uses a password and cannot complete a SAML flow, so affected users get `AUTH_SAML_ENFORCED` with no fallback.

It disables domain-based auto-join. Your IdP becomes the only way in, so anyone who needs access must be assigned the application there.
{% endhint %}

## Certificate rotation

Duro stores one certificate at a time, so rotation is a hard cutover: paste the new certificate into **Signing certificate** and save at the moment your IdP starts signing with the new key.

Watch the **Expires** date on the card. Expiry is checked when each assertion arrives, not only at upload, so an expired certificate fails sign-in for everyone — and with SSO enforced, there is no password fallback.

## Session policy

Not SAML, but it sits on the **Authentication** page one level up and is usually the next thing an IT admin wants.

| Field                           | Blank means         | Range                |
| ------------------------------- | ------------------- | -------------------- |
| **Sign out after inactivity**   | No inactivity limit | 5 minutes to 30 days |
| **Require sign-in again after** | The 30-day default  | 1 hour to 30 days    |

{% hint style="warning" %}
**Saving a session policy signs out every member of this organization immediately, including you.** Sessions carry the policy they were created under, so the only way to apply a new one is to end the old sessions. Duro confirms before it does this.

Members who also belong to another organization get the least restrictive limit of the ones that apply to them.
{% endhint %}

## Troubleshooting

### Start with the status on the Authentication page

The **SAML single sign-on** row summarizes the whole setup in a few words, without your having to open the form:

| The row says                            | What is missing                                   |
| --------------------------------------- | ------------------------------------------------- |
| *Not configured*                        | No verified domain. Start at Step 1               |
| *Domain verified, no identity provider* | No SSO URL saved. Step 4                          |
| *Configured, not enabled*               | Everything is saved but the toggle is off. Step 5 |
| Your domain, e.g. `acme.com`            | SSO is live for that domain                       |

### The sign-in page asks for a password instead of offering SSO

Discovery routes an address to SAML only when all four are true: the domain is verified, SAML is enabled, an SSO URL is saved, and the address matches the verified domain exactly. The status row above covers the first three. For the fourth, remember `dana@mail.acme.com` does not match a verified `acme.com`.

### Your IdP rejects the request before showing a login screen

Almost always the Entity ID or Reply URL. Re-copy them from **Give these to your identity provider**, or point your IdP at the metadata URL instead of typing them.

On Entra ID, `AADSTS75011` — *Authentication method by which the user authenticated with the service doesn't match requested authentication method* — was a separate cause with a distinctive shape: sign-in failed in a normal browser but succeeded in a private window. Duro used to request an exact `PasswordProtectedTransport` context, which Entra could not match when the user's existing session had been established with MFA or a certificate. Duro no longer requests an authentication context at all, so this no longer happens. If you still see `AADSTS75011`, it is coming from a policy in your IdP rather than from Duro's request.

### Sign-in bounces back to the sign-in page with `saml_failed`

The assertion was rejected, and Duro returned the user to the page they started from with `saml_failed` in the URL — see [Where a failed sign-in lands](#where-a-failed-sign-in-lands). Check the signing setting first: if your IdP signs only the `<Response>` and not the `<Assertion>`, every sign-in fails this way even though the IdP reports success and nothing else is misconfigured. Set it to **Assertion** or **Assertion and Response**.

After that, the usual causes are a certificate mismatch (the one saved in Duro is not the one signing), an expired certificate, or an audience mismatch. Compare the **Fingerprint (SHA-256)** shown in Duro against your IdP console.

All of these produce the same `saml_failed`, deliberately — the code is shown to whoever triggered it, so it does not report which check failed. Work through them in that order.

### Sign-in fails with `saml_no_account`

The assertion was valid and the person has no Duro account. They are returned to the page they started from with `saml_no_account` in the URL. Turn on [Create accounts automatically](#creating-accounts-automatically), invite them, or provision them through [SCIM](/account-management/scim-provisioning.md).

### Everyone was signed out unexpectedly

Someone saved a session policy. See [Session policy](#session-policy) — saving one ends every session in the organization by design.

### Saving fails with *Single sign-on is not included in your plan*

Your plan does not include the `SSO_SAML` entitlement. Contact your Duro account team. See [Entitlement Errors](/advanced-topics/error-handling.md#entitlement-errors).

## Automating this

Everything above is available through the GraphQL API at `https://api.durohub.com/graphql`, for organizations that manage tenant configuration as code. All of it requires the `organization.saml.configure` permission and the `SSO_SAML` entitlement.

| Operation                                  | What it does                                                                                                                  |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `organization.verifyOrganizationDomain`    | Issues the DNS challenge — returns `recordName` and `recordValue`                                                             |
| `organization.checkOrganizationDomain`     | Checks the TXT record and records success                                                                                     |
| `organization.configureSaml`               | Writes the IdP details and the `samlEnabled`, `samlJitEnabled`, `enforceSaml` flags                                           |
| `organization.configureSessionPolicy`      | Sets the session policy — and revokes every session in the organization                                                       |
| `authentication.getSamlAdminConfig(slug:)` | Reads back the current configuration, including `spEntityId`, `spAcsUrl`, `certificateFingerprint` and `certificateExpiresAt` |

A configuration save looks like this:

```graphql
mutation {
  organization {
    configureSaml(input: {
      id: "550e8400-e29b-41d4-a716-446655440000"
      samlIdpEntryPoint: "https://accounts.google.com/o/saml2/idp?idpid=XXXXXXXXX"
      samlIdpIssuer:     "https://accounts.google.com/o/saml2?idpid=XXXXXXXXX"
      samlIdpCertificate: "-----BEGIN CERTIFICATE-----\nMIID…\n-----END CERTIFICATE-----"
      samlJitEnabled: false
    }) { id }
  }
}
```

{% hint style="warning" %}
**Every field is written only when supplied**, so send only what changed. Do not echo values you did not mean to set: posting `samlEnabled: false` from a stale read turns "save my IdP details" into "switch SAML off", which also switches off automatic account creation and SCIM.
{% endhint %}

`enforceSaml` is the one setting with no web UI at all, for the reason given under [Requiring SSO](#requiring-sso):

```graphql
mutation {
  organization {
    configureSaml(input: {
      id: "550e8400-e29b-41d4-a716-446655440000"
      enforceSaml: true
    }) { id }
  }
}
```

Note that `samlIdpCertificate` is never returned by any query — only its fingerprint and expiry — so there is no read-modify-write available for that field.

## Next steps

* [SCIM Provisioning](/account-management/scim-provisioning.md) — provision users and teams from the same IdP
* [Role-Based Access Control](/advanced-topics/rbac.md) — what those users can do once they are in


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.durohub.com/account-management/enterprise-sso.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
