Single sign-on with SAML 2.0 Module

Vaks PM · Integration guide · SAML 2.0 · July 2026

What you will end up with. A button on the login screen that federates sign-in through your SAML identity provider. This guide covers Microsoft Entra ID, Google Workspace, AD FS, Okta and Keycloak. If your provider also speaks OpenID Connect, prefer the OIDC guide — it is simpler to configure and less brittle. Use SAML when OIDC is not an option.

How it works

Vaks PM acts as a SAML service provider. Its constraints are deliberately narrow, and knowing them up front saves most of the debugging:

The attributes to send

This is the part that decides whether your integration works, and it is where non-Microsoft providers need care. Vaks PM reads Microsoft's claim URIs by default, and the claim names cannot be changed in the admin interface. Whatever your provider, configure it to emit these exact attribute names:

PurposeAttribute name to emitFallback if absent
Account key (required)http://schemas.microsoft.com/identity/claims/objectidentifierThe NameID
Email (required)http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddressemail, then the NameID
Display namehttp://schemas.microsoft.com/identity/claims/displaynamedisplayName, then http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname, then the email
Groups (for role mapping)http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsNone. Absent means no role mapping at all
Send a stable account key, not an email address. Accounts are matched on the object identifier alone. If your provider does not emit that attribute, Vaks PM falls back to the NameID — and since the requested NameID format is an email address, the account key silently becomes the user's email. The day someone's address changes, their next sign-in no longer matches any account and is refused as a collision. Every provider below can emit an arbitrary attribute name: point that attribute at an immutable internal identifier (Entra object ID, Google unique ID, AD FS object GUID, Okta user ID, Keycloak user ID).

Prerequisites

SideWhat you need
Identity providerThe right to create an application or relying-party trust and to define its attribute statements.
Vaks PMThe org:manage permission — an organization administrator.
LicenceA licence covering single sign-on. Configuration works without it; federated sign-in does not.
InfrastructureRedis reachable (the handshake is held there for 10 minutes) and accurate clocks on every host.
Keep a break-glass administrator able to sign in with a local password and MFA, before you enable anything.

Step 1 — Start in Vaks PM to get the service-provider values

Open Admin → Integrations → Single sign-on, add a provider, and set:

The form then generates the three values your provider needs:

Field in Vaks PMValueCalled this in most providers
ACS / Reply URL (generated)https://<host>/api/v1/auth/saml/<slug>/callbackAssertion Consumer Service URL, Reply URL, ACS URL
SP entityID (Identifier)https://<host>/api/v1/auth/saml/<slug>Identifier, Entity ID, Audience URI
SP metadata (XML file)https://<host>/api/v1/auth/saml/<slug>/metadataService-provider metadata, uploadable to most providers

Note that the entity ID is the ACS URL without /callback — they are deliberately different values, and providers reject the assertion if the audience does not match exactly. Save the provider now, so you can export the metadata file; it stays disabled until you enable it in step 5.

Metadata is served before the provider is enabled, so you can configure your side first. If your provider accepts a metadata upload, use it: it fills in the ACS URL and entity ID without transcription errors.

Step 2 — In your identity provider

Microsoft Entra ID

  1. Go to Microsoft Entra admin center → Identity → Applications → Enterprise applications → New application → Create your own application, choose the non-gallery option, then open Single sign-on → SAML.
  2. In Basic SAML Configuration, either upload the metadata file from step 1, or set Identifier (Entity ID) and Reply URL to the two generated values.
  3. Entra already emits the object identifier, email and display name under the expected claim URIs, so the default attribute statement works as-is. For role mapping, add a group claim under Attributes & Claims.
  4. Under SAML Certificates, download Certificate (Base64) and copy the Login URL.
  5. Assign the users or groups who should have access under Users and groups.

Google Workspace

  1. In the Google Admin console, go to Apps → Web and mobile apps → Add app → Add custom SAML app.
  2. On the Google identity-provider details screen, download the certificate and copy the SSO URL.
  3. For service-provider details, set ACS URL and Entity ID to the two values from step 1. Leave Signed response unchecked — the assertion is signed by default, which is what is required.
  4. Under Attribute mapping, map:
    • Basic information → Primary email → app attribute http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
    • Basic information → the user's full namehttp://schemas.microsoft.com/identity/claims/displayname. Do not map the first name alone here — it becomes the display name for every user.
    • A stable identifier — Google's unique ID — → http://schemas.microsoft.com/identity/claims/objectidentifier
  5. For role mapping, add a group membership mapping and name the app attribute http://schemas.microsoft.com/ws/2008/06/identity/claims/groups.
  6. Turn the app on for the relevant organizational units.

Active Directory Federation Services (AD FS)

  1. In the AD FS management console, go to Relying Party Trusts → Add Relying Party Trust, choose Claims aware, and import the metadata from step 1 by URL if your AD FS host can reach it — otherwise enter the entity ID and ACS URL by hand.
  2. Set the secure hash algorithm to SHA-256 under the trust's Advanced properties.
  3. Add a claim rule Send LDAP Attributes as Claims against Active Directory:
    • E-Mail-AddressesE-Mail Address
    • Display-Name → a custom claim named http://schemas.microsoft.com/identity/claims/displayname
    • objectGUID → a custom claim named http://schemas.microsoft.com/identity/claims/objectidentifier
  4. Add a second rule for group membership, emitting to http://schemas.microsoft.com/ws/2008/06/identity/claims/groups.
  5. Add a rule transforming the email address to Name ID with format Email.
  6. Export your token-signing certificate from Service → Certificates, and note the sign-on URL, usually https://<adfs-host>/adfs/ls/.
AD FS rotates its token-signing certificate automatically by default, typically once a year. Vaks PM holds a fixed copy, so the rotation silently breaks sign-in on the day it happens. Either disable automatic certificate rollover, or diarise updating the certificate here before each rotation.

Okta

  1. In the Okta admin console, go to Applications → Create App Integration → SAML 2.0.
  2. Set Single sign-on URL to the ACS URL and Audience URI (SP Entity ID) to the entity ID, both from step 1. Leave the NameID format as EmailAddress.
  3. Under Attribute Statements, add:
    • Name http://schemas.microsoft.com/identity/claims/objectidentifier, value user.id
    • Name http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, value user.email
    • Name http://schemas.microsoft.com/identity/claims/displayname, value user.displayName
  4. Under Group Attribute Statements, add name http://schemas.microsoft.com/ws/2008/06/identity/claims/groups with a filter matching the groups you want to send.
  5. From the Sign On tab, take the identity-provider sign-on URL and download the signing certificate.

Keycloak

  1. In your realm, go to Clients → Create client, type SAML, and set the client ID to the entity ID from step 1 — in Keycloak the client ID is the expected audience.
  2. Set Valid redirect URIs and the master SAML processing URL to the ACS URL.
  3. In the client's settings, turn Sign assertions on. Leave Sign documents as you prefer, and leave assertion encryption off. Set the signature algorithm to RSA-SHA256, and the NameID format to email.
  4. Under Client scopes → dedicated scope → Add mapper, add:
    • A User Property mapper for id, with SAML attribute name http://schemas.microsoft.com/identity/claims/objectidentifier
    • A User Property mapper for email, attribute name http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
    • A Group list mapper, attribute name http://schemas.microsoft.com/ws/2008/06/identity/claims/groups
  5. Take the realm's signing certificate from Realm settings → Keys, and the sign-on URL https://<host>/realms/<realm>/protocol/saml.

Step 3 — Finish in Vaks PM

FieldValue
IdP Sign-on URL (entryPoint)The sign-on URL from step 2. Must be HTTPS.
IdP signing certificate (X.509)Use Upload certificate file and select the file you downloaded (.cer, .crt, .pem).
Default role (JIT)Role given to a newly created user matching no group mapping.
Unknown user (no matching objectId)Create the account automatically (consumes a licence seat) or Refuse the sign-in.
Upload the certificate file rather than pasting its contents. The server requires PEM with its -----BEGIN CERTIFICATE----- header, and providers hand you a bare base64 body. The upload button converts what you give it — PEM, raw base64 or binary — whereas pasting the base64 body straight into the box is rejected.

Step 4 — Map groups to roles

Under Group → role mappings, add one row per group. On Entra the value is the group's object ID; on the other providers it is whatever your attribute statement emits, usually the group name.

An empty or absent groups attribute is treated as "unknown" rather than "no groups", so a misconfigured attribute cannot demote your whole organization. The last remaining global administrator is never demoted.

Unlike OIDC, SAML has no fallback for the groups attribute. If it is not emitted under that exact URI, role mapping does nothing at all — silently.

Step 5 — Test, then enable

Press Test on the provider's row, but understand what it does: for SAML it only checks that a sign-on URL is present and that the certificate looks like valid PEM. It does not contact your provider and does not verify the certificate cryptographically. A passing test means the form is complete, nothing more.

The real test is a sign-in. Tick Enabled (shown on the login screen), save, then in a private window sign in as a pilot user and confirm:

Complete the sign-in within 10 minutes; the handshake is single-use and expires after 600 seconds.

Troubleshooting

Messages are shown in the language your browser requests, since the user is not signed in yet. They are reproduced below in English.

SymptomCause & fix
SAML assertion validation failed.The catch-all, deliberately vague so it cannot be used as a probing oracle. In practice it is one of four things, in order of likelihood: the certificate does not match the one actually signing; clock drift on either side; the audience does not equal the service-provider entity ID; or the provider is encrypting the assertion, which is unsupported. The real reason is in the api container logs — read them rather than guessing.
Missing SAML response (SAMLResponse absent).The provider posted nothing usable, usually because the ACS URL points somewhere else or the binding is not HTTP-POST.
An account with this email already exists but is not linked to this IdP identity (objectId) …An account holds that email but was never linked. If it appears for every user, your provider is almost certainly not emitting the object-identifier attribute, so the email is being used as the account key. Fix the attribute statement.
No account matches this identity (objectId) and auto-provisioning is disabled.Automatic creation is off and no account matches. Provision the account first, or switch the setting.
L'assertion SAML ne contient pas d'identifiant (objectId/nameID) ou d'email exploitable.Neither an object identifier nor a usable NameID or email arrived. Check the attribute statement and the NameID format.
Invalid or expired SAML session state.More than 10 minutes elapsed, the URL was replayed, or Redis is down. Note that provider-initiated sign-in is not supported — users must start from the Vaks PM login page.
Incomplete SAML provider (entryPoint / IdP certificate missing).Enabled with a field missing. Reopen and complete it.
It worked, then stopped for everyone on the same dayThe provider rotated its signing certificate. Download the new one and upload it here. Common with AD FS, whose automatic rollover is on by default.
Intermittent failures, no patternClock drift beyond the 30-second tolerance. The api container log names this case explicitly — look for CLOCK SKEW between IdP and this host, which also prints the server time and the tolerance in force. Check NTP on the identity provider and on every Vaks PM host before suspecting the certificate.
Everyone lands on the default roleThe groups attribute is not arriving under the exact URI expected. There is no fallback for it in SAML.

What is logged

Under Admin → Security & Compliance → Audit log: auth.sso.login on each federated sign-in (recording the protocol and whether the account was created), auth.sso.failure with the refusal reason, user.provisioned.sso on account creation, and idp.created / idp.updated / idp.deleted for configuration changes, all at critical severity.

Assertion-validation failures are logged with their true cause in the api container, never in the browser.


Related: OIDC single sign-on, preferable where available · SCIM provisioning · all integrations.