Count Your Password Rows
Here is a question worth asking out loud at your next team meeting. Someone leaves tomorrow. Name every system they can still log into.
We tried it. The list ran to more than a dozen: a fleet platform, a profitability tool, a proposal system, a document ingestion platform, static hosting, a pre-calculation app, a time tracker, an analytics platform, a signage system, a reservations system, a device registry, a business engine. Each one had its own users table. Each one had its own bcrypt hashes, its own password reset mail, its own session cookie, its own idea of what an administrator is.
Twelve rows to delete, in twelve databases, in the right order, from memory, on the day someone leaves and everyone is busy.
Nobody deletes twelve rows. People delete the four they remember.
The rest sit there being valid indefinitely, and the only reason nothing bad happens is that nothing bad has happened yet.
How You Get Here Without Deciding To
This is not carelessness. It is the completely reasonable outcome of a good habit.
We build products from a shared template. The template has authentication in it, because every product needs authentication, and having it built in is why a new product can be useful in a week instead of a month. Every new project inherits a working login on day one.
Twelve projects later, you have twelve working logins. Each one was the right local decision. Together they are an identity system nobody designed, with a security posture nobody can describe.
Every one of them was a sensible decision on its own. Nobody ever chose the tangle.
The other symptom is quieter. Nobody knew what existed. A colleague would ask whether we had a tool for something, and the answer was that we had shipped one eighteen months ago and they had never seen the URL. Tools that are hard to find get rebuilt.
One Front Door
DASH is the answer we landed on. It is two things wearing one skin.
The first is an OpenID Connect provider. Applications no longer hold passwords. They redirect to DASH, DASH authenticates, the application gets an identity back. Each connected application is a row: a slug that doubles as its client id, a client secret, and its allowed redirect URI.
The second is a dashboard. When you sign in, you see every tool you actually have access to, as tiles, with the ones you use most in front. That half sounds trivial next to the identity work. It has changed daily behaviour more.
On top of that sit the boring platform pieces: workspaces, roles, feature flags, and a superadmin view where you can see which people exist across which applications. Offboarding is now one screen. Access reviews are possible for the first time, because there is finally a list.
The Honest Part
Building your own identity provider is a decision with a bill attached. Here is ours, itemised.
It is a single point of failure, by construction. When DASH is down, nobody logs into anything. Before, an outage in one tool was an outage in one tool. Now the login path is shared, and it deserves more care than any individual application. Existing sessions survive a provider outage, which softens it, but it does not remove it.
Adding an application is a four-step procedure across three repositories. Register the row in the provider’s database. Add the issuer, client id and redirect URI to the application’s deployment values. Add the client secret to that application’s encrypted secrets file. Restart the provider.
That last step is not optional and it is not obvious. Our provider loads its client list once at boot into a static array, so a freshly registered application gets rejected as an unknown client until the provider restarts. With a single replica, that restart is a ten to twenty second window where nobody can log in anywhere.
We know how to fix it. We have not, because it happens rarely and a login blip at 11pm is cheaper than the code to avoid it. That is a legitimate answer, but it is only legitimate if you say it out loud instead of pretending the system is better than it is.
Failures are quiet and non-obvious. A missing client secret shows up as an HTTP 500 on the login route. A 404 on the same route means something completely different: the OpenID route is not in that build at all. Two failures, two causes, one symptom of “login is broken.” That knowledge is not in anyone’s head reliably, so it is written down as a runbook with the exact commands and the exact expected status codes.
If a procedure has four steps and any of them can fail silently, it needs to be a document.
Ours took an afternoon to write and has paid for itself several times.
You now own everything identity means. Password policy. Session lifetime. Reset flows. Multi-factor. Passkeys, which are a project in their own right rather than a checkbox. Every one of those is now yours, forever, including the parts nobody enjoys.
Why Not Just Buy One
Reasonable question, and for many companies the answer is: do that.
Two things pushed us the other way. The first is who logs in. Our applications are multi-tenant and a lot of the accounts are not employees. They are client staff, in workspaces, with roles that mean something inside a specific product. Per-seat identity pricing is priced for employees. It gets uncomfortable fast when the seats are your customers’ customers.
The second is that our applications already had a workspace and role model that everything else depends on, and an external provider still leaves you mapping identities onto it. You do not skip that work, you just do it against someone else’s abstractions.
There is also a smaller reason worth admitting. It is our own template. Making the template speak OpenID Connect once means every future product gets it free, and the marginal cost of the thirteenth application is one row and one secret.
What Changed in Practice
Offboarding is one screen instead of a scavenger hunt. That was the point.
Things people can find get used. Things sent once in a message quietly disappear.
The unglamorous win is different, though. New internal tools now get used. Before DASH, shipping something internal meant sending a link and a password and hoping. Now it appears on everyone’s dashboard the day it goes live. The gap between “we built this” and “people use this” was mostly discovery, and discovery turned out to be a login screen problem.
The trade-off we accepted, written plainly: we swapped twelve small risks for one large one, and we made the large one visible. Twelve forgotten password rows are a risk nobody can see and therefore nobody manages. One identity provider that everything depends on is a risk with a name, a runbook, and an obvious place to spend attention.
That is not the same as safety. It is the precondition for it.