Single sign-on with SAML 2.0 Module
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 assertion must be signed (the outer response need not be), with SHA-256 signature and digest algorithms.
- Encrypted assertions are not supported. There is no service-provider private key, so a provider configured to encrypt will fail validation. Leave assertion encryption off.
- The assertion is consumed by HTTP POST, and the audience must match the service-provider entity ID exactly.
- Clock-skew tolerance is 30 seconds. Validity windows are enforced with that much grace on either side, so your identity provider and the Vaks PM hosts still both need NTP — drift beyond half a minute produces intermittent, generic validation failures that look like a certificate problem but are not. An operator can widen the tolerance with the
SSO_SAML_CLOCK_SKEW_MSenvironment variable, capped at five minutes; raising it works around a broken clock rather than fixing it. - Requests are not signed by Vaks PM, since it holds no private key. Providers accept this; do not require signed authentication requests.
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:
| Purpose | Attribute name to emit | Fallback if absent |
|---|---|---|
| Account key (required) | http://schemas.microsoft.com/identity/claims/objectidentifier | The NameID |
| Email (required) | http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress | email, then the NameID |
| Display name | http://schemas.microsoft.com/identity/claims/displayname | displayName, 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/groups | None. Absent means no role mapping at all |
Prerequisites
| Side | What you need |
|---|---|
| Identity provider | The right to create an application or relying-party trust and to define its attribute statements. |
| Vaks PM | The org:manage permission — an organization administrator. |
| Licence | A licence covering single sign-on. Configuration works without it; federated sign-in does not. |
| Infrastructure | Redis reachable (the handshake is held there for 10 minutes) and accurate clocks on every host. |
Step 1 — Start in Vaks PM to get the service-provider values
Open Admin → Integrations → Single sign-on, add a provider, and set:
- Protocol — SAML. Settable only at creation.
- Slug — lowercase letters, digits and hyphens, 2 to 40 characters. Frozen once saved, since it is baked into the URLs below.
- Display name — the label on the login button.
The form then generates the three values your provider needs:
| Field in Vaks PM | Value | Called this in most providers |
|---|---|---|
| ACS / Reply URL (generated) | https://<host>/api/v1/auth/saml/<slug>/callback | Assertion 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>/metadata | Service-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.
Step 2 — In your identity provider
Microsoft Entra ID
- 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.
- 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.
- 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.
- Under SAML Certificates, download Certificate (Base64) and copy the Login URL.
- Assign the users or groups who should have access under Users and groups.
Google Workspace
- In the Google Admin console, go to Apps → Web and mobile apps → Add app → Add custom SAML app.
- On the Google identity-provider details screen, download the certificate and copy the SSO URL.
- 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.
- 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 name →
http://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
- Basic information → Primary email → app attribute
- For role mapping, add a group membership mapping and name the app attribute
http://schemas.microsoft.com/ws/2008/06/identity/claims/groups. - Turn the app on for the relevant organizational units.
Active Directory Federation Services (AD FS)
- 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.
- Set the secure hash algorithm to SHA-256 under the trust's Advanced properties.
- Add a claim rule Send LDAP Attributes as Claims against Active Directory:
- E-Mail-Addresses →
E-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
- E-Mail-Addresses →
- Add a second rule for group membership, emitting to
http://schemas.microsoft.com/ws/2008/06/identity/claims/groups. - Add a rule transforming the email address to Name ID with format Email.
- Export your token-signing certificate from Service → Certificates, and note the sign-on URL, usually
https://<adfs-host>/adfs/ls/.
Okta
- In the Okta admin console, go to Applications → Create App Integration → SAML 2.0.
- 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.
- Under Attribute Statements, add:
- Name
http://schemas.microsoft.com/identity/claims/objectidentifier, valueuser.id - Name
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, valueuser.email - Name
http://schemas.microsoft.com/identity/claims/displayname, valueuser.displayName
- Name
- Under Group Attribute Statements, add name
http://schemas.microsoft.com/ws/2008/06/identity/claims/groupswith a filter matching the groups you want to send. - From the Sign On tab, take the identity-provider sign-on URL and download the signing certificate.
Keycloak
- 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.
- Set Valid redirect URIs and the master SAML processing URL to the ACS URL.
- 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.
- Under Client scopes → dedicated scope → Add mapper, add:
- A User Property mapper for
id, with SAML attribute namehttp://schemas.microsoft.com/identity/claims/objectidentifier - A User Property mapper for
email, attribute namehttp://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
- A User Property mapper for
- 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
| Field | Value |
|---|---|
| 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. |
-----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.
- Order is priority — a user holds exactly one organization role, and the first matching row from the top wins. Matching is exact string equality.
- No match gives the default role to a new account, and leaves an existing account's role untouched.
- Identity provider is authoritative for roles additionally demotes users matching no mapping back to the default role at each sign-in. Leave it off if you assign roles by hand.
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.
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:
- The user lands back in Vaks PM signed in.
- Their role in Admin → Users matches the expected mapping — proving the groups attribute arrived.
- An
auth.sso.loginentry appears in the audit log.
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.
| Symptom | Cause & 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 day | The 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 pattern | Clock 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 role | The 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.