DE EN

Journal

SAML for one enterprise customer in an afternoon

A corporate customer wants sign-in through Entra ID or Okta. What that takes with EAuth, what their IT does, and what is still missing.

Samuel Krauss, Founder · · 6 min read

The first corporate customer who says "our people sign in through Entra ID" is usually the moment a small product discovers what enterprise software costs to build. SAML is old, verbose and full of ways to get it wrong, and the customer's IT department will test it against exactly one provider, theirs. With EAuth the whole of it is one page in the console. This post walks through it, including the parts that are not ours to speed up.

Organisations first

Single sign-on in EAuth belongs to an organisation, not to your application as a whole. An organisation is a customer: a group of your application's accounts with a role each, an email domain the customer has verified, and, if they want it, a connection to their identity provider.

So two things come before SAML. Tick Enable organisations in the application's settings, then create the customer's organisation and verify their domain with the TXT record the console shows. The domain decides who is sent to the provider, and it stops one customer's provider from vouching for another customer's addresses. Without a verified domain, single sign-on cannot be turned on.

What you do

Open the organisation, choose Set up single sign-on, and the console shows EAuth's side of the connection: an entity ID, a reply URL, and metadata to download that carries both and the certificate for this connection. Each connection has its own 3072-bit RSA key pair, so one customer's setup never touches another's.

The customer's administrator creates an application for it at their provider with those two values, or reads in the metadata, and sends you their federation metadata back. Paste it into the console, or type the entity ID, the sign-in URL and the signing certificate by hand. Decide two things: whether people become members at their first sign-in, and whether members may sign in to the organisation only through the provider. Then turn it on.

That is the whole of your part. Budget an afternoon for it, and expect most of that to go to the mail exchange in the middle and to the customer's administrator finding the right attribute settings.

The provider must take sign-in requests by HTTP redirect, as Entra ID, Okta and Google Workspace do, and send the address as the NameID or as an email attribute. The default attribute names are the ones Entra ID and Okta send; where a provider differs, you set the names in the console. A provider that only does IdP-initiated sign-in, where it posts an answer nobody asked for, is not supported, on purpose. The section on checks says why.

What the customer's people see

When your application names the organisation in the authorization request, with the organization parameter and its id or slug, a person who is not signed in goes straight to their provider without typing anything. That is how a "Sign in with SSO" button in your product works.

When it does not, the EAuth sign-in page has a Use single sign-on button: the person types their work address and is sent to their organisation's provider. Sending the form with an address in the domain and no password does the same. Registering with such an address goes to the provider too, since the provider makes the account.

The first sign-in through the provider creates the account at your application, already confirmed and without a password. That matters since 3 October, when EAuth stopped giving any application an account whose address nobody has confirmed: for these accounts, the provider's word is the confirmation. With members at first sign-in turned on, the account also joins the organisation with the default role. With it off, it joins nothing until somebody invites it, and your application gets access_denied for the organisation.

An account that already existed with that address keeps its id and its memberships. If its owner had confirmed the address, it is linked as it is. If nobody had, it may have been registered by somebody other than the person, so the provider's word takes it over: its password, passkeys and second factor go, its sessions end, and your webhook hears session.revoked with the reason.

With only through the provider turned on, passwords and passkeys stop counting for the organisation. A password typed for an address in the domain is not even looked at, the organisation's refresh tokens end when the setting takes effect, and a token that came from another kind of sign-in is refused when it is used.

What we check on every answer

This is the part that takes the time to build, and the reason to take it from somebody who has. XML signature checking is a maintained library's job: signature wrapping attacks have broken implementations at large vendors, because XML canonicalisation is subtle in ways that pass every test a developer writes. The library checks the signature against the certificate you pasted, never one that arrived inside the answer, and checks the destination, the issuer, the times and the recipient.

On top of that, EAuth ties every request to the browser that started it, with a cookie only that browser holds, so an answer somebody obtained elsewhere and posted from another browser signs nobody in. A request stays open for ten minutes and is answered once, and every assertion id is remembered for longer than the assertion could be accepted, so a captured answer posted a second time is refused. The audience must be this connection's entity ID, and the asserted address must be in the organisation's verified domain.

When an answer is refused, the person sees that it was, and the organisation's page in the console shows why: a signature that does not match, an audience or reply URL the provider has wrong, an address outside the domain, a provider that did not authenticate the person again when asked. That is where to look while the customer's IT is setting it up, and it saves the round trip in which each side writes "it does not work" to the other.

Removing people, and fresh sign-ins

When the customer removes somebody at their provider, that person cannot sign in through it again. EAuth does not hear of the removal, though. Refresh tokens your application already holds for them keep working until they expire, which is up to 30 days, unless the member is removed or suspended in the console or the organisation is suspended. Those end the organisation's refresh tokens at once, and access tokens already issued run out within 15 minutes.

An application that wants a fresh sign-in can say so. With prompt=login, the provider is asked to authenticate the person again (ForceAuthn), and an answer carrying an earlier sign-in is refused. With max_age, the provider's own session counts when it is recent enough, and when it is not, the provider is asked again. The ID token's auth_time is when the provider says the person authenticated, and EAuth's session ends no later than the provider says it should.

What it does not do

There is no SCIM provisioning: accounts appear at the first sign-in and are not created ahead of it or removed after the provider drops someone. That is the gap in the previous section, and the reason the remove button in the console matters. Group claims from the provider are not mapped to roles; roles in the organisation are set in the console. The artifact binding is not supported.

A word on testing. Our end-to-end tests run against an identity provider built on the same SAML library, signing its answers the way Entra ID and Okta do. That exercises the protocol and our refusals. It is not the same as a session in each vendor's admin console, and the first connection to a given provider is where its particular settings show up. The refusal reason on the organisation's page is there for that afternoon.

Sources

  1. Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0, OASIS, read
  2. SAML Security Cheat Sheet, OWASP, read
  3. On Breaking SAML: Be Whoever You Want to Be, USENIX Security 2012, read
  4. Single sign-on SAML protocol, Microsoft, read
  5. Enterprise SSO with SAML, EAuth documentation, Elchi Studios, read

How can we help?

Call us +41 58 513 63 11 · Weekdays 8 to 18, usually straight away Write to us contact@elchi.dev · A reply within two working days Talk for 15 minutes No commitment, no preparation What does it cost? CHF 6,000. Then CHF 300 a month.