The IT director has decided the team is moving to single sign-on. Fine — except "the team" is 340 people, and they're not all in the same place.
Most of them live in the existing Keycloak realm. But the security group wants to trial Microsoft Entra ID with 20 volunteers before committing the whole org. And the company acquired a smaller firm last year that's still on Okta, with its own 20-odd people who aren't migrating any time soon.
A big-bang cutover — everyone moves to one provider on one weekend — is exactly the kind of project that goes wrong at 2am and gets rolled back by Monday. What you actually want is to move people in cohorts, watch each one, and keep a way back in if any of it breaks.
FreeITSM's single sign-on is built for that messy middle. It speaks OpenID Connect (OIDC), the modern web-SSO standard, and it treats "which provider authenticates this person" as a per-user decision rather than a system-wide one.
Different users, different identity providers, the same login page — all at once.
This piece is about three things: the flexibility that makes a phased rollout possible, the break-glass design that means nobody gets locked out, and what's actually happening under the hood when FreeITSM talks to a provider — including the part a JWT plays in making that conversation trustworthy.
One: the rollout bends to you, not the other way around
You can configure several identity providers at once. Each one is a separate entry — a display name, the provider's issuer URL, a client ID and secret — with its own on/off switch. Add Keycloak, Entra and Okta side by side and all three appear as options on the login page.
Then the key idea: every analyst is assigned a single sign-in method. Local password, or one specific provider. That assignment is what carves 340 people into clean cohorts:
Because the assignment is per-user, the three pilots run in parallel and stay isolated. FreeITSM enforces that an analyst may only sign in via the provider they're assigned to — so an Entra-cohort user can't accidentally come in through Keycloak and muddy the pilot, and an SSO login can never silently take over a different account. You get clean feedback from each group without them bleeding into each other.
How do people become SSO users? Two ways, and you pick per provider:
Just-in-time, for the big rollout
Turn on auto-create for a provider and the first time someone signs in through it, FreeITSM creates their analyst account automatically — matched and stamped to that provider, ready to go. Ideal for the 300-person Keycloak group where pre-creating accounts by hand would be a chore. You can even cap what a freshly-created user can see by default, so auto-provisioned pilot users don't all arrive as administrators.
Pre-assigned, for the tight pilots
Leave auto-create off and only people who already have an account — whom you've explicitly assigned to that provider — can get in. That's the control you want for a 20-person Entra trial. Assigning an existing analyst is a single dropdown in their profile; on their next sign-in their identity links up automatically, matched by their verified email.
When the Entra pilot goes well, you flip the next cohort over. When you're ready, the whole org. No forklift, no cutover weekend, no holding your breath.
Two: nobody gets locked out
The fastest way to lose trust in an SSO rollout is to lock the admins out of their own system the first time the identity provider has a bad morning. So local username/password login is treated as a permanent break-glass route, not something SSO replaces.
There are two switches. A master "enable single sign-on" kill switch — turn it off and everyone instantly falls back to local login. And an "allow local login" toggle for when you want a clean SSO-only front door. Crucially, even with local login switched off, it's hidden, not disabled: a discreet "sign in with a local account" link — and a ?local=1 URL — always brings the local form back for an administrator who needs it.
The design rule is blunt: local authentication is never hard-blocked. A misconfigured client secret, an expired provider certificate, an IdP outage — none of them can wall every human out of the system. There is always a way back in.
One more nicety: with SSO, the identity provider owns authentication, including multi-factor. FreeITSM doesn't double-prompt an SSO user for its own local TOTP code — if your provider enforces MFA, that's the MFA. And when an SSO user logs out of FreeITSM, it also ends their session at the provider, so they're not silently waved back in on the next visit.
Three: how FreeITSM talks to the provider — securely
Here's the part that earns the trust. When someone signs in with SSO, FreeITSM never sees their password — it only ever gets typed into the identity provider. What FreeITSM receives is proof of who they are. Getting that proof safely is a small, well-worn dance called the Authorization Code flow with PKCE:
state value (to prevent cross-site request forgery) and a nonce (to tie the eventual token to this login attempt). It also sends a PKCE challenge — a one-way hash of a secret it keeps to itself.Every link in that chain is doing a specific job. state stops an attacker from forging the callback. The nonce stops a captured token being replayed into a different session. PKCE stops a stolen authorization code from being redeemed by anyone but FreeITSM. The client secret — stored encrypted at rest and never sent to the browser — is how the provider knows it's genuinely FreeITSM asking, and not an impostor site. And the whole back-channel exchange happens server-to-server over TLS.
Four: the part the JWT plays
So FreeITSM has the tokens. The important one is the ID token, and it's a JWT — a JSON Web Token. Understanding what that is explains the last, most important security step.
A JWT is a compact, digitally signed token in three dot-separated parts: a header, a payload, and a signature. The payload is a set of claims — small facts about the user and the token:
sub— the user's stable, unique id at the provider (the thing FreeITSM links an account to)email,name— profile details used to match or create the analystiss— who issued it (the provider)aud— who it's for (FreeITSM's client ID)nonce— the value FreeITSM planted back in step 1exp— when it expires
Here's the crux: a JWT's payload is only encoded, not encrypted — anyone can read it. What makes it trustworthy is the signature. The provider signs the token with its private key, which only it holds. FreeITSM verifies that signature using the provider's matching public keys, which the provider publishes at a well-known address (its JWKS — JSON Web Key Set). If the signature checks out, FreeITSM knows two things for certain: the token genuinely came from that provider, and not a single character of it has been altered in transit.
The JWT is the proof. Its signature is what turns "someone claims to be Alice" into "the provider vouches this is Alice".
Signature verification is the classic place authentication code goes wrong — subtle mistakes (accepting an unsigned token, trusting the wrong algorithm, fetching the wrong key) turn into account-takeover bugs. So FreeITSM doesn't hand-roll it: the JWT and JWKS handling uses the well-established, audited firebase/php-jwt library. On top of the signature, FreeITSM independently checks that the iss matches the provider, the aud is its own client ID, the nonce matches the one it sent, and the token hasn't expired. Only when all of those pass does the sign-in succeed.
At that point FreeITSM maps the verified identity to an analyst — by an existing link on the sub, by verified email, or by just-in-time creation — checks the strict-isolation rule, and establishes the session. The user lands in the app having never given FreeITSM their password, with a cryptographically verified token as the only thing that crossed the boundary.
Five: one form, every provider
The reason all of this works the same way for Keycloak, Entra, Okta, Google and the rest is a single standard mechanism: discovery. Every compliant OIDC provider publishes a document at <issuer>/.well-known/openid-configuration that lists exactly where its login, token, key and logout endpoints live.
So FreeITSM doesn't need a bespoke connector per vendor. You give it the issuer URL and it reads the rest. Provider-specific concepts that usually trip people up — an Entra tenant, a Keycloak realm, an Okta org — all collapse into that one URL. The config form is the same four fields every time, and a Test button confirms the discovery document is reachable before you save.
That's what makes the cohort story practical rather than aspirational: adding the Entra pilot alongside the Keycloak rollout is one more provider entry, not a fork of the login code.
What this earns the right to do
SSO is usually sold as a binary — you're either on it or you're not, and getting there is a project with a capital P. FreeITSM treats it as a dial you can turn one cohort at a time, with a hand permanently on the break-glass lever and a properly paranoid handshake doing the actual work underneath.
Pilot a provider with twenty people on Monday. Keep three hundred where they are. Never risk locking out the admins. And when you're ready, turn the dial.