Inheriting the company's identity server — and teaching it to scale
- Context
Every platform has a front door: the identity server. It's the service that handles every login — checking credentials and issuing the tokens that prove to every other system who you are. When it's down, nobody gets in. Ours was an IdentityServer4 (ASP.NET Core) instance approaching end of support, and the move to its successor, Duende IdentityServer, had no owner.
The engineers who knew the service were stretched across other priorities, so it needed a new owner — and the team put it in my hands. Not because I was the most experienced; I had little application-code experience at the time. Because I was the engineer they trusted to take care of it. That trust set the bar. My first contributions were unglamorous — fixing dead links and broken routing — and what followed was a year of Duende documentation, trial and error in local and dev environments, and progressively bigger swings at the service every login depends on.
- Problem
- Every login ran through one hand-managed, end-of-support IdentityServer4 instance: secrets in local config files, a restart to rotate anything, and no way to scale past a single replica.
- Diagnosis
Two things made the risk concrete. Secrets handling predated centralized tooling — connection strings and client secrets lived in local configuration files, which bothered me before anything else did. And when clients depend on the platform to meet hard external deadlines, identity downtime doesn't just pause work — it costs trust, and the repeat business that trust brings.
The blockers formed a dependency chain, not a list. You can't scale horizontally until every replica signs tokens with the same keys. You can't rotate secrets safely until configuration is externalized. You can't deploy repeatably until the app and its schema are self-contained. That ordering became the roadmap — portability, then secrets, then rotation, then the key ring — with each step shipping on its own instead of waiting on a big-bang rewrite.
- The fix
Portability first (about a week). Dockerized the service, with EF Core migrations standing up the new Duende configuration database from code, so a fresh environment builds from the repo alone. It became one of the first workloads in the Azure Container Apps environment I'd built for the company — a story of its own.
Secrets next (about a month), including a self-inflicted lesson. I wired Azure Key Vault in as a runtime configuration source via the Azure.Extensions provider — and my first pass loaded secrets by number: 1-secret, 2-secret. It worked, and it was poor planning: rotating anything meant remembering which number belonged to which client. I redid the scheme around client-based names (clientname-secret), so a human can read the vault and know what everything is.
Then rotation. A sentinel watcher monitors Key Vault for value changes and reloads configuration every 24 hours or on a manual trigger — secrets rotate with zero restarts.
The cutover was the scary part. Every client had to resolve the right secret on the first production load. So I built a throwaway verification page — restricted to the team, local and QA environments only, never deployed to production — showing each client against the secret it resolved. We verified the mapping by eye before trusting the cutover, instead of hoping.
Finally, the signing key ring (a couple of months). We held a Duende license but weren't using its key management, so token signing was pinned to a single instance. I persisted the key ring to Azure Blob Storage, protected through the existing Key Vault — and hit the best bug of the project: replicas were publishing different
kidvalues in their JWKS documents, so a token signed by one instance failed validation on another. The culprit was a local-development setting that had leaked into server configuration; the fix drives the active key from a database field so every replica agrees.Proof, not vibes. I split Container Apps traffic 50/50 across two replicas and watched real logins land on both in the logs — sessions intact, tokens validating everywhere — before calling horizontal scaling done.
- Impact
- The company's front door went from a fragile single-instance pet to a portable, horizontally scaled service — and the change users actually feel is the one nobody sees: deploys, reboots, and scale events no longer end sessions, so nobody gets logged out because we shipped. Secret rotation is a Key Vault write instead of a config edit and a restart. Environments rebuild from code. Ownership also means continuous hardening — dependency currency and configuration tightening as a standing duty, not an afterthought. And it reshaped my role: the engineer who started by fixing dead links ended up owning the authentication platform.
- Postscript
The ecosystem has moved since I shipped this. Duende archived the IdentityServer4 repository read-only (announced March 2025). In 2026, Rock Solid Knowledge forked that Apache-2.0 codebase into Open.IdentityServer — independently maintained and free, with documented migration paths from both IdentityServer4 and Duende, and explicitly not affiliated with or endorsed by Duende. Duende's own line went the other way: source-available under a paid production license, free only below a revenue threshold, and now at v8 (June 2026). This is ecosystem awareness, not experience — I haven't run Open.IdentityServer, and the stack listed here is what I actually ran.
Two things I'd take from that. Licensing is an architectural constraint, not a procurement footnote. The capability that finally unlocked horizontal scaling was key management we already owned and hadn't switched on. On a free fork, that's the first thing I'd verify rather than assume — the whole scaling story depends on every replica agreeing about signing keys. And almost none of the work was vendor-specific. Containerizing, externalizing configuration, rotating without restarts, and persisting a shared key ring are properties of the system, not the SDK — which is the real test of whether the modernization was sequenced correctly.
- Lessons
- Name things for the person doing the 2 a.m. rotation — numbered secrets “worked” and were still wrong.
- Prove cutovers with your own eyes: throwaway verification tooling and a 50/50 traffic split beat hoping.
- Sequence modernization as a dependency chain so every step ships value alone: portable → externalized config → rotation → shared keys → scale.
- Read what your vendor already sold you — the licensed key-management feature that unlocked scaling was sitting unused.
- Inexperience isn't a blocker to ownership; owning a production service is the fastest way to stop being inexperienced.