Single sign-on has been a settled standard for over twenty years, yet SAML is still the line item that stalls a deal or triggers a call to sales. How enterprise auth became a premium upsell, who built that world and who warned us about it, how wide the moat really is and what it is made of, and the one force quietly draining half of it.
Monitoring is the code that only runs during a disaster, the one moment you cannot afford to find it broken. So we built a harness that drives a real signal for every alert rule and refuses to pass until the notification physically lands in a capture relay. The first run found our flagship error-rate alert stone dead, then found worse: a retention setting that had quietly stamped instant expiry on our logs, so a whole class of alerts evaluated over nothing while ingestion looked healthy.
A hundred megabytes of documentation screenshots moved to Cloudflare R2, behind a CDN base the app derives from its own hostname at runtime. It worked in the browser and did nothing to the origin's traffic, because the pages a crawler sees are prerendered on 127.0.0.1, where a hostname tells you nothing. The fix is a placeholder the HTML carries out of the build and the edge fills in per request, and the reason it has to work that way is a read-only filesystem.
Just-in-time provisioning was on by default, so a federated identity provider could create accounts in a customer's tenant simply by asserting that someone existed. Turning that default off is a one word change. Turning it off without silently changing the behaviour of every connection already stored, none of which ever wrote the column, is the actual problem, and the trick that solved it also explains why a default is API surface.
A backend-for-frontend takes OAuth tokens out of the browser and leaves it holding nothing but an opaque cookie. Serving many tenants from one BFF is mostly a plumbing problem, until back-channel logout arrives: a server-to-server POST with no cookie, no session, and no browser, where the only thing identifying the tenant is a claim inside a token you have not verified yet.
We added a signature-checking admission webhook to our Kubernetes cluster so unsigned images could never run. Hours later one Deployment had 2,242 ReplicaSets, a fresh one every three seconds, and no available pods. The webhook logs showed it admitting every single request. The damage came from the one word in "mutating admission webhook" that we had stopped reading.
A returning user watched "Loading…" for precisely ten seconds before login appeared, every time. The round number was the tell: our SSO library opened a hidden silent-renew iframe against an auth host serving X-Frame-Options: DENY and frame-ancestors 'none', so the frame never loaded, never errored, and the library waited out its full timeout. Clickjacking defense and OIDC silent renew, each correct, hung in the seam between them.
We encrypted every email address at rest, and the admin search box quietly stopped autocompleting — no error, no log line. A blind index restores exact-match lookup over ciphertext, but HMAC's entire job is to destroy the ordering that prefix search needs. Here's the design that got type-ahead back — index every prefix as its own value — and the three traps that taught us searchable encryption is a systems problem, not a crypto problem.
We moved JWT signing from RS256 to ES256 and every token we minted was cryptographically valid — and rejected. Three times, for three different reasons: DER where JWS wants raw R‖S, integer padding that fails one token in 128, and a base64 dialect that changed under our feet. Here's how to read the mangled bytes and name the guilty layer from the signature length alone.
The two documents a service reads to validate one of our JWTs — the discovery doc and the JWKS — are the most-requested, least-secret things we serve, and every fetch used to come all the way back to origin. Caching them on Cloudflare was the easy part. The hard part is that a stale public-key cache must never reject a valid token, and here's the rotation-safe ordering that makes an aggressively-cached JWKS safe.
Duende's LITE tier is $5,750/yr but caps at 2 client apps and ships no SAML at all. The moment you need enterprise SSO or a third app, you're on STANDARD at $12,500. A look at what Duende actually costs a real product, and the client-count tax nobody warns you about.
One private key signs every token our auth server issues, and it has moved twice in its life: born inside the process, exiled to a vault it can't leave, then reincarnated as a smaller, faster kind of key entirely. Each move happened on a live issuer with tokens in flight. Here's what custody and algorithm changes actually take, and the quiet regression that rode in on a "more secure" commit.
Your SP validated the assertion signature, pinned the certificate, and logged the user in. Every check passed. I still replayed a login I captured an hour ago. The trick was not breaking the signature. It was deleting a field the signature never covered.
We challenged the second factor on the login page and never re-checked it at the token endpoint. So a valid password plus a hand-edited return URL walked straight past MFA. The fix, and the rule it taught us: MFA is a property of the session you issue, not a step in the login screen.
SCIM is a standardized CRUD API whose most important verb is the one that turns an ex-employee's account off. Vendors price it at $125 a month per connection, or park it in the enterprise tier next to SSO. Here is what a SCIM connection actually costs to serve, and why charging for deprovisioning is charging for security.
Azure Table Storage and DynamoDB have no delete markers, so a deleted row just vanishes, which means your incremental backup never sees the delete at all. Restore that backup and the user you offboarded is back, with the password you rotated away from. Here is why deletes have to be first-class, and the one table that makes them so.
Our rate limiter and lockout counter keyed off the client IP. Behind nginx that IP came from a header the client could set, so one rotating header handed every request a fresh IP and every per-IP control fell over. The one-line spoof, and the fix that is fiddlier than it looks on Kubernetes.
Azure Table Storage gives you a partition key, a row key, and almost nothing else. No joins, no secondary indexes worth the name, no increment. We run a multi-tenant auth system on it anyway. The patterns that make it work, and what you give up versus SQL.
An unauthenticated password-reset endpoint with no per-email rate limit is not just your problem. Point it at someone else's inbox and you have turned their reset form into a spam cannon, billed to your sender reputation. The fix, without leaking which emails have accounts.
Most auth vendors fence SSO, SCIM, MFA, and audit logs behind an Enterprise plan. We put all of them on the $0 tier. Here is the unit economics that makes giving features away survivable: the wall was never features, it is active users and support.
Single sign-on means trusting the identity provider. We trusted it for one thing too many: we resolved returning federated users by the email in the assertion, so any connection could assert someone else's address and land on their account. The fix wasn't more validation. It was changing the identity join key from email to a per-provider subject the asserting party can't forge.
Azure Container Apps promised to stop us paying for an idle auth cluster. We costed the migration and killed it, not over price or cold starts, but because serverless is missing the only number a leader-elected auth backplane actually needs.
The 'feature tax' as a real invoice. Three buyer scenarios (first enterprise deal, a handful of customers, SSO as table stakes) priced against each vendor's public rates. Plus the quieter surcharge nobody warns you about: running out of OAuth clients mid-integration.
A three-replica cluster kept disagreeing with itself. The cause was in our own code: the peer-discovery routine had a catch block that disabled discovery when multicast failed, and on managed Kubernetes multicast always fails. Here's why we deleted gossip entirely and elected a leader with a blob lease instead, and why the membership you can open in Storage Explorer beats the membership you have to infer.
Login is a weekend. The bill arrives after: signature verification, SCIM deprovisioning, MFA, audit logs, key rotation, breach liability, and on-call for a tier-zero system. An honest build-vs-buy, including when building is right.
OIDC or SAML? You rarely get to choose. What each protocol is, when each wins, the XML-signature footguns that make SAML dangerous to hand-roll, and why a B2B product ends up needing both.
Authagonal's support system: real-time messaging with no refresh, transparent two-way AI translation pinned from the customer's first message, and a thread that stays fully translated even over email. Simple, good-looking, and actually capable.
Two of our tenants were quietly the same tenant: same database, same token-signing key, different names. The cause was a one-line helper that sanitized slugs by stripping hyphens, turning a unique identifier into a non-unique one. Here's how a lossy string transform became a cross-tenant takeover, and why we found it before you did.
We sell authentication, so our most important test is one that becomes a customer: a single Playwright run that signs up a tenant, configures every feature, points a real app at it, logs a real user in with the right claims, then backs up, restores, and deletes itself. How exhaustive end-to-end testing earned our go-live confidence.
Our staff console authenticates through Authagonal itself, with Entra SSO and SCIM doing the provisioning. Being your own customer surfaces the edge cases no test suite thinks to write. Here are the ones that mattered.
Your Terraform state is a full map of your cloud plus a pile of secrets, and by default it lives behind a public endpoint. Here's how we bootstrapped remote state from nothing, put it behind a private endpoint, and reached it through a zero-trust connector instead of paying the VPN gateway tax, without ever locking ourselves out.
"Everyone resets their password" is a migration choice, not a law of physics. How to carry your users' existing password hashes across an identity-provider move so the cutover is invisible, and the one case where you genuinely can't.
The auth industry meters permission, not cost. It gates SSO, MFA, and provisioning behind tiers and per-connection fees for features that cost effectively nothing to provide. Here's the distinction that pricing pages hope you never draw.
Moving off self-hosted Duende IdentityServer to managed auth — what actually migrates from the SQL config and ASP.NET Identity, and the operational cost you stop carrying.
A practical look at what an Auth0 migration really involves: what's easy, what's fiddly, and the one thing Auth0 genuinely won't give you.