All integrations

Login & identity Available

Single sign-on (OIDC)

OpenID Connect per organisation, configured on one screen. You prove the connection works before you make it mandatory, and there is always a way back.

What you need

  • An identity provider with OpenID Connect discovery: Google Workspace, Microsoft Entra ID, Authentik, Keycloak, Auth0.
  • The settings.auth.manage permission; owner and admin hold it by default.
  • Rights at that provider to register a web application with a client secret (a confidential client).
  • An installation reachable publicly over HTTPS, behind a reverse proxy whose headers the API trusts.

How to connect it

  1. 1 Open Instellingen → Single sign-on (Settings → Single sign-on) and copy the Callback-URL field at the top. That address is not configurable: it is always https://your-host/api/v1/auth/oidc/callback, built from your verified custom domain or your organisation's subdomain.
  2. 2 At your provider, register a web application with a client secret and paste that callback URL literally into the redirect URI field. For Google that is Google Cloud Console → APIs & Services → Credentials → OAuth client ID → Web application; a Workspace-only agency sets the consent screen to Internal.
  3. 3 Grant the application the scopes openid email profile. Nothing more is asked for, and email is mandatory: a login that returns no email claim is refused. No 'Authorized JavaScript origin' is needed, because this is a server-side redirect and not a browser flow.
  4. 4 On the settings screen, fill in Discovery-URL, Client-ID and Clientgeheim (client secret), plus a Weergavenaam (display name). That name is what the login button says.
  5. 5 Pick the Rol voor nieuwe gebruikers (role for new users) and decide on Maak bij de eerste keer inloggen een lidmaatschap aan (create a membership on first sign-in). With it off, only people who already have access here can sign in.
  6. 6 Tick Single sign-on inschakelen (enable single sign-on), press Opslaan and then Verbinding testen (test connection). The server fetches the discovery document and reports the issuer on success; on failure you see the provider's own error.
  7. 7 Sign in with the new button yourself first. Only then, if you want to, tick Single sign-on verplichten (enforce single sign-on). The API refuses to store it while the current connection fields have no passed test behind them.

Test before you enforce

'Verbinding testen' (test connection) fetches and validates the discovery document server-side. On success it reports the issuer; on failure it shows the provider's own error verbatim, so you are not left guessing. Only after a passed test will the API accept 'Single sign-on verplichten'. Change the discovery URL, client ID or secret afterwards and that marker is cleared: the app asks for a fresh test.

  • The client secret is write-only: the app only reports whether one is stored. Leave the field empty on a later save to keep the existing secret.
  • If SCHAKL_SECRET_KEY is still the shipped default and no SCHAKL_ENCRYPTION_KEY is set, the screen warns you: set a real key before storing a secret in production.
  • PKCE (S256) and state are always sent. Not a setting, not a choice.

Enforcing, and the way back

Enforcing switches password login off for everyone in the organisation, so from that moment every session goes through your provider's own MFA and conditional-access rules. If that provider goes down, whoever runs the installation sets SCHAKL_FORCE_LOCAL_LOGIN to true on the API container and restarts: password login works again, whatever is stored. Sign in, fix or disable SSO, then unset it.

  • Enforcing also switches off that organisation's entire two-factor surface, enrolment included; the identity provider supplies the MFA.
  • SCHAKL_FORCE_LOCAL_LOGIN is present in infra/compose.yaml and infra/compose.tunnel.yaml but not in infra/compose.portainer.yml, so on such a stack you add it by hand.
  • Signing out always stays reachable, whatever these settings say.

Who gets in, and who stays out

Auto-provisioning is about first contact with this organisation, not about 'has no membership right now'. Remove somebody in Instellingen → Team & gebruikers (Settings → Team & users) and their next SSO sign-in does not quietly restore them: the app remembers. Restoring access is a deliberate act, with the role you mean. Someone the provider recognises but who holds no membership here gets no session at all, and reads on the login page that they should ask an admin for access.

  • An existing local account is only linked to an SSO identity when the provider asserts email_verified; that guards against providers with self-service signup.
  • The provider's profile picture is refreshed on every login; a photo you uploaded yourself wins.
  • New users get the role you picked under 'Rol voor nieuwe gebruikers', member by default.

The first-setup trap

Almost every failed first attempt looks the same: the provider answers with redirect_uri_mismatch and the details carry a redirect URI starting with http://. The app is not trusting your reverse proxy's headers and is emitting the internal scheme. You fix that in your deployment, not on the settings screen.

  • Run the API container with --proxy-headers and --forwarded-allow-ips="*", and make sure the public request really is HTTPS (Cloudflare 'Always Use HTTPS', for instance).
  • Set SCHAKL_AUTH_COOKIE_SECURE to true in production.
  • At Google, a change to the redirect URIs can take a few minutes to propagate.
  • An http:// discovery URL is deliberately allowed: a Keycloak or Authentik on your own network is a supported case.

Permissions

Grant these to the role that manages this integration (Settings → Roles). Admin-only by default, and never to the client role.

  • settings.auth.manage Opens Instellingen → Single sign-on: reading and saving the settings and running the connection test. Admin only by default; the owner holds it through the wildcard.

What it deliberately does not do

  • The redirect URI is not configurable. There is no environment variable to override it, and providers match it character for character.
  • SSO covers signing in, not access to the Google Workspace API. Calendar, Gmail and Drive are a separate connection with a grant of their own.
  • The old SCHAKL_OIDC_* variables are retired: they are ignored and can come out of your compose file.
  • A discovery URL that redirects is refused; the document is fetched without following redirects (an SSRF guard).
  • One session belongs to one organisation: signing in on organisation A's hostname is not a session at organisation B, even for a member of both.
  • No SSO button appears while the configuration is off or the discovery URL, client ID or secret is missing.

Where to find it Instellingen → Single sign-on (Settings → Single sign-on), plus the 'Inloggen met ...' button on the login page.

Read the guide

More in this category

Other integrations