SSO is a perimeter decision, not a login box

Most write-ups treat single sign-on as a feature you bolt on: stand up an identity provider, point the apps at it, enjoy one login. That framing misses the actual decision. SSO moves your trust boundary — every app now trusts one issuer, and every account is one credential away from all of them. Get it right and you’ve hardened the whole estate at once. Get it wrong and you’ve built a single, central thing to lose.
Here’s how I think about it after wiring an OIDC provider in front of a self-hosted fleet.
Enforce the second factor at the flow, not per app
The leverage is putting 2FA on the authentication flow itself, not on each application. Every app that federates to the provider inherits it for free — you can’t forget to enable 2FA on app number nine, because app number nine never sees a password. Enrolment (TOTP / WebAuthn) is mandatory at the identity layer, and the apps just receive an already-verified identity.
That’s the whole point of centralising: you set the policy once, at the boundary, and everything downstream gets it.
Keep a key under the mat
The flip side of “one issuer for everything” is that when the issuer is down — or you fat-finger the auth flow — you can lock yourself out of everything, including the tools you’d use to fix it. So every critical app keeps a local break-glass admin that bypasses SSO, and the recovery codes for 2FA enrolment live offline.
SSO is the front door. Break-glass is the fire exit. You don’t ship a building with only one.
The gotchas that actually cost time
Two traps ate real hours, and they’re the same lesson wearing two hats:
- A baked-in config file silently overrode environment variables. I set the OIDC scopes via env, login
kept failing with “missing fields: email”, and the cause was a config file winning over the env. The
provider needs
openid email profileexplicitly — set where the app actually reads it, not where you assume it does. - In-cluster back-channels can’t use the public hostname. The browser hits the public login URL fine, but the app’s server-side token exchange runs inside the cluster, where split-horizon DNS doesn’t resolve the public name. The fix: point the back channel at the internal service address while the browser-facing URL stays public.
Both say the same thing: SSO has two channels — the one the user sees and the one the server uses — and they don’t live on the same network.
The principle
Centralising identity is a force multiplier, but it concentrates risk by design. So design the concentration: enforce the strong factor at the boundary, plan the failure mode before you need it, and remember that OIDC authenticates a user — it doesn’t, on its own, make a sensitive service safe to expose. The login box is the easy part. The perimeter is the decision.

