Single sign-on with OpenID Connect Module

Vaks PM · Integration guide · OIDC · July 2026

What you will end up with. A button on the Vaks PM login screen that sends users to your identity provider and back, signed in. Accounts are matched on the provider's immutable object identifier, and can optionally be created on first sign-in with a role derived from group membership. This guide covers Microsoft Entra ID, Google Workspace, Okta and Keycloak; any spec-compliant provider works the same way.

How it works

Vaks PM acts as an OpenID Connect relying party. It uses the authorization-code flow with PKCE, and it discovers your provider's endpoints automatically — you never enter an authorization or token URL by hand, only the discovery address.

Three behaviours are worth understanding before you configure anything, because they determine what you must make your provider emit:

The claims read are, in order of fallback: object identifier from oid then sub; email from email, then preferred_username, then upn; display name from name, then the email; groups from groups, then roles.

Claim names are not editable in the admin interface. The mapping exists in the API but has no form field, so in practice your provider must emit claims under the standard names above. For groups this is the one thing to get right: name the claim exactly groups on the provider side. Okta and Keycloak both let you choose the claim name; do so rather than expecting Vaks PM to adapt.

Prerequisites

SideWhat you need
Identity providerThe right to register an application and, if you want role mapping, to add a groups claim to the token. On Entra this means an app registration plus token configuration; on Google Workspace, super-administrator access.
Vaks PMThe org:manage permission — an organization administrator.
LicenceA licence covering the single sign-on feature. You can configure a provider without one; federated sign-in stays blocked until the licence is imported under Admin → Organization → License.
InfrastructureRedis must be reachable — the sign-in handshake is held there for 10 minutes. The provider's discovery URL must be HTTPS and publicly resolvable; an identity provider on a private address range is rejected.
Keep a break-glass administrator. Before enabling anything, make sure at least one administrator can still sign in with a local password and MFA. Single sign-on depends on your provider, on Redis and on correct system clocks; any of the three can fail. Without a local administrator, that failure locks everyone out with no way back in.

Step 1 — Start in Vaks PM to get the redirect URI

You need the redirect URI before you can register anything with your provider, and Vaks PM generates it from a slug you choose.

Open Admin → Integrations → Single sign-on, add a provider, and fill in the first three fields:

The form then shows Redirect URI (generated) with a copy button. It looks like this:

https://<your-host>/api/v1/auth/oidc/<slug>/callback

Copy it. This exact string, character for character, is what your provider needs.

If you run several organizations on separate subdomains, the redirect URI is derived from the host that serves each one — so every subdomain produces a different URI, and every one of them must be registered with the provider.

Step 2 — In your identity provider

Follow the section for your provider, then return to step 3. All of them ask for the same three things: the redirect URI you just copied, a client ID and a client secret.

Microsoft Entra ID

  1. Go to Microsoft Entra admin center → Identity → Applications → App registrations → New registration. Name it, choose single-tenant, and under Redirect URI select platform Web and paste the URI from step 1.
  2. From Overview, copy the Application (client) ID and the Directory (tenant) ID.
  3. Under Certificates & secrets → Client secrets → New client secret, create one and copy its Value immediately — it is shown once.
  4. For role mapping, go to Token configuration → Add groups claim, choose which groups to include (security groups, or groups assigned to the application), and make sure the claim is emitted in the ID token. Entra emits group object IDs, so your mappings will use GUIDs rather than group names.

Vaks PM builds the discovery address for you from the tenant ID — you will paste the tenant ID rather than a URL.

Groups claim, or app roles. If your directory has users in very many groups, Entra replaces the claim with a link rather than the list, and role mapping stops working. In that case define app roles on the registration instead and assign users to them: they arrive in the roles claim, which Vaks PM reads as a fallback for groups.

Entra alternative — certificate authentication (no secret)

Instead of a shared client secret, Entra accepts an application authenticating with a certificate (private_key_jwt, RFC 7523): the application itself signs a short-lived proof of identity with its private key, and Entra only ever holds the matching public certificate. Nothing secret travels over the network or is shared with Entra. This is a full alternative to the client secret from the previous step, not an addition — you pick one method or the other.

  1. Generate a private key / self-signed certificate pair (for example with OpenSSL): openssl req -x509 -newkey rsa:2048 -keyout vaks.key -out vaks.cer -days 730 -nodes -subj "/CN=vaks-pm". Keep vaks.key (the private key, PEM PKCS#8) somewhere safe — that is what you paste into Vaks PM.
  2. In Entra, on the same application registered for SSO, go to Certificates & secrets → Certificates → Upload certificate and upload the public vaks.cer file.
  3. Once the upload is accepted, Entra displays the certificate thumbprint — copy it.
  4. In Vaks PM, on the provider form, switch Authentication to Certificate, paste the thumbprint and the contents of vaks.key (PEM private key). Leave the client secret field blank.
How Vaks PM picks between the two methods. The rule is simple: if a private key is stored for this provider, certificate authentication is used; otherwise, the client secret is. The private key is encrypted at rest and never displayed again after saving — when editing, leave it blank to keep the stored one, or paste a new one to replace it. The thumbprint itself is not secret and stays visible.
Known pitfall: the standard OIDC library does not work here. Most generic OIDC clients sign a private_key_jwt assertion with a kid header, but Entra only matches the certificate on the x5t header (the certificate thumbprint). An assertion without x5t is silently rejected by Entra. Vaks PM therefore signs this assertion itself with the correct header — this detail only matters if you are trying to reproduce this flow elsewhere.

The same certificate authentication method, with the same steps on the Entra side, is also available for Microsoft Teams provisioning (which the SharePoint context inherits from) and for an Entra Agent ID directory — four integrations, one certificate to manage if you reuse the same Entra application across several of them, or a separate certificate per application if you prefer to keep them isolated.

Google Workspace

  1. In the Google Cloud console, select or create a project, then go to APIs & Services → OAuth consent screen and configure it as Internal so only your Workspace users can use it.
  2. Go to APIs & Services → Credentials → Create credentials → OAuth client ID, application type Web application. Under Authorized redirect URIs, paste the URI from step 1.
  3. Copy the Client ID and Client secret shown after creation.
  4. The discovery URL is the same for every Google tenant: https://accounts.google.com.
Google does not emit group membership in its ID token. Group-to-role mapping therefore cannot work with Google Workspace over OIDC — every user will receive the default role, and you assign any elevated role manually in Vaks PM. If role mapping matters to you, use SAML with Google instead, where group attributes can be sent.

Okta

  1. In the Okta admin console, go to Applications → Create App Integration, choose OIDC — OpenID Connect and Web Application.
  2. Under Sign-in redirect URIs, paste the URI from step 1. Set the assignments to the users or groups who should have access.
  3. Copy the Client ID and Client secret from the application's General tab.
  4. Your discovery URL is https://<your-org>.okta.com/oauth2/default (or the custom authorization server you intend to use).
  5. For role mapping, open Security → API → Authorization Servers, select the server, and under Claims add a claim named exactly groups, included in the ID token, with a filter matching the groups you want to send.

Keycloak

  1. In the realm you use, go to Clients → Create client, client type OpenID Connect, and give it a client ID.
  2. Enable Client authentication (this makes it a confidential client, which is what Vaks PM expects), and leave the standard flow enabled.
  3. Under Valid redirect URIs, paste the URI from step 1.
  4. Take the client secret from the Credentials tab.
  5. Your discovery URL is https://<keycloak-host>/realms/<realm>.
  6. For role mapping, go to the client's Client scopes, open the dedicated scope, and add a Group Membership mapper with token claim name groups. Turn off Full group path unless you intend to map paths like /engineering/backend.

Any other provider

Nothing here is Microsoft-specific. Any provider that publishes a standard discovery document works: register a confidential web client with the redirect URI from step 1, collect the client ID and secret, and supply the discovery address. The only two requirements are that the ID token carries a stable subject identifier and an email address, and — if you want role mapping — a claim named groups.

Step 3 — Finish in Vaks PM

Back in the provider form, complete the remaining fields:

FieldValue
Microsoft Entra — Directory (tenant) IDFor Entra: the tenant GUID, a verified domain, or organizations. The discovery URL is built from it and shown below the field.
OIDC discovery URLFor every other provider: follow the Use a custom OIDC discovery URL link and paste the address from step 2 (https://accounts.google.com, your Okta authorization server, your Keycloak realm). Must be HTTPS.
Client ID (application ID)From step 2.
AuthenticationClient secret (default) or Certificate — see the certificate alternative for Entra. Both methods are encrypted at rest and never displayed again after saving; leave fields blank when editing to keep what is already stored.
ScopesSpace- or comma-separated extras. openid is always included; leaving this empty requests openid profile email, which is what you want in almost every case.
Default role (JIT)The role given to a newly created user who matches no group mapping.
Unknown user (no matching objectId)Create the account automatically or Refuse the sign-in. See below.

The choice on unknown users is the one with real consequences. Create automatically provisions the account on first sign-in and consumes a licence seat. Refuse the sign-in means only accounts that already exist can authenticate — the safer setting when seats are limited, or when you provision through SCIM and want the directory to remain the only source of accounts.

Step 4 — Map groups to roles

Under Group → role mappings, add one row per group: the group's identifier on the left, the Vaks PM role on the right. On Entra the identifier is the group's object ID; on Okta and Keycloak it is the group name as emitted in the claim.

Two rules govern evaluation:

The checkbox Identity provider is authoritative for roles extends this to demotion: on every sign-in, a user matching no mapping is reset to the default role. Leave it off if you grant elevated roles by hand inside Vaks PM, since it will undo them.

Two safeguards worth knowing. An empty or absent groups claim is treated as "unknown", never as "belongs to no group" — so a misconfigured claim cannot silently demote your whole organization at the next sign-in. And the last remaining global administrator is never demoted, whatever the mappings say.

Step 5 — Test, then enable

Save the provider, then press Test on its row. For OIDC this performs a live discovery fetch and reports the issuer it found, the client ID and whether a secret is stored. It confirms that the discovery URL is reachable and coherent — it does not attempt a sign-in.

Then tick Enabled (shown on the login screen) and save. The button appears on the login page.

Verify with a real sign-in, ideally in a private browser window and with a pilot user rather than your own administrator account:

Complete the sign-in within 10 minutes. The handshake is held in Redis for 600 seconds and is single-use. Pausing halfway, or replaying a URL with the browser's back button, fails with an invalid-state error. This is normal and not a misconfiguration.

Troubleshooting

Sign-in errors are shown on the login page, in the language your browser requests — the user is not signed in yet, so no interface preference exists. They are reproduced below in English.

SymptomCause & fix
An account with this email already exists but is not linked to this IdP identity (objectId) …A local account already holds that email but was never linked to this identity. Deliberate — accounts are never taken over by email. Set the account's external identifier to the provider's object ID, or delete the account permanently and let it be recreated.
No account matches this identity (objectId) and auto-provisioning is disabled.Automatic creation is off and no account matches. Either create the account first (by hand or via SCIM), or switch the unknown-user setting to automatic creation.
L'id_token ne contient pas d'identifiant ou d'email exploitable.The token carries no usable email. Add the email scope or claim on the provider side; on Entra, confirm the account actually has a mail address.
Invalid or expired SSO session state.More than 10 minutes elapsed, the URL was replayed, or Redis is unavailable. Retry from a clean login page; if it persists, check Redis.
IdP response validation failed.The token failed validation. The real reason is deliberately not shown in the browser — read the api container logs, which record it.
Incomplete OIDC provider (discovery/clientId missing).The provider was enabled with a field missing. Reopen and complete it.
OIDC single sign-on requires an active license.Configuration is allowed without a licence, sign-in is not. Import the licence under Admin → Organization → License.
Everyone lands on the default roleThe groups claim is not arriving. On Entra, check Token configuration includes it in the ID token; on Okta and Keycloak, check the claim is named exactly groups. On Google Workspace this is expected — groups are not emitted at all.
Sign-in works, but the user sees nothing after landingThe account has no licence seat. Grant one in Admin → Users, or use a team with automatic licensing.

What is logged

Under Admin → Security & Compliance → Audit log: auth.sso.login on every federated sign-in, recording the provider and whether the account was created; auth.sso.failure with the refusal reason; user.provisioned.sso when an account is created, with the role applied; and idp.created / idp.updated / idp.deleted for configuration changes, all at critical severity. Client secrets never appear in the audit log.

Signature and token-validation failures are deliberately vague in the browser to avoid giving an attacker a probing oracle. Their real cause is in the api container logs.


Related: SAML single sign-on if your provider does not speak OIDC · SCIM provisioning to create and retire accounts automatically · all integrations.