For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

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:

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.

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.

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

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.

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 IDhttps://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

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

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.

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.

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.

Google Workspace

  1. admin.google.comAppsWeb and mobile appsAdd AppAdd custom SAML app

  2. Name it Duro, click Continue

  3. Download the IdP metadata, or note these manually:

    • SSO URLhttps://accounts.google.com/o/saml2/idp?idpid=XXXXXXXXX

    • Entity IDhttps://accounts.google.com/o/saml2?idpid=XXXXXXXXX

    • Certificate — download the .pem

  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 accessON for everyone, or select specific organizational units → Save

Google takes a few minutes to propagate.

Microsoft Entra ID

  1. entra.microsoft.comIdentityApplicationsEnterprise applicationsNew applicationCreate your own application

  2. Name it Duro, choose Integrate any other application you don't find in the gallery, click Create

  3. Users and groupsAdd user/group → assign who should have access

  4. Single sign-onSAMLEdit 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.comSSO ApplicationsAdd New Application

  2. Search for Custom Application, select it, then Next

  3. Choose Manage Single Sign-On (SSO)Configure SSO with SAMLNext

  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

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.

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.

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 is the better answer — it creates accounts deliberately from your directory, and deprovisions them too.

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.

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.

Password sign-in is then refused with AUTH_SAML_ENFORCEDYour organization requires signing in with SSO.

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

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, AADSTS75011Authentication 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. 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, invite them, or provision them through SCIM.

Everyone was signed out unexpectedly

Someone saved a session policy. See 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.

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:

enforceSaml is the one setting with no web UI at all, for the reason given under Requiring SSO:

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

Last updated

Was this helpful?