Somebody decides to move identity providers. There is usually a good reason. The licence renewal is absurd, or an acquisition arrived with a different provider, or the security team has finally lost patience with something. A number gets attached to the work, and the number comes from the identity team, because it is an identity project.
Then the work starts, and it turns out not to be an identity project at all.
Standing up the new provider takes a fortnight and most of that is waiting for approvals. The provider is not the problem. The problem is the forty-one applications that integrated with the old one, each of which learned the shape of it: the discovery document, the claim names, the group format, the SDK version, the quirk about how one provider spells an email address and another one does not. None of that was a decision anybody made. It is sediment.
So the estimate was for the fortnight, and the work is the forty-one.
Why the estimate is always wrong in the same direction
The reason this happens is structural, and it is worth naming precisely, because it is not incompetence.
When an application integrates directly with an identity provider, the integration is a dependency in the wrong direction. Your application depends on a specific external system, and the coupling is invisible in normal operation. Nothing is broken. There is no failing test. The coupling only becomes visible at the moment you try to change the thing you are coupled to, which is the moment you have already committed to the change.
Every other dependency in your stack is one you would notice. You know which database you are on because it is in the connection string, in the migrations, in the query dialect. You know which cloud you are on. But the identity coupling is spread thin across forty-one repositories in the shape of claim names and redirect addresses, and no inventory of it exists until somebody builds one, by hand, during the migration.
The second migration is worse
Here is the part that should change how you plan. Teams treat the migration as a one-off, and they finish it by welding every application to the new provider. Which means the next move, three years later, is the same project again.
Except worse, because now there are sixty applications, four of the teams that did the first migration have moved on, and the institutional memory of the first one has been reduced to a wiki page that says which claims were mapped.
I have watched an organisation cost this out twice and be surprised twice.
What actually fixes it
The fix is not better documentation of the coupling. It is not owning the coupling. It is putting something in the middle that your applications depend on instead, and keeping the parts that should be yours on your side of it.
Concretely, an application should hold one client registration against one issuer that you control. Behind that issuer, the provider is federated, and which provider it is becomes a configuration fact rather than an architectural one. Then the assets that would otherwise be lost in a move live with you:
- The identity record, keyed by something you own, with the provider's identifier as a link beside it rather than as the primary key. This is the single most important detail and it is the one most often got wrong. If your application data references the provider's identifier, you have not decoupled, you have added a hop.
- The profile attributes, which no provider has any particular interest in holding for you.
- The authorization model, which is almost always richer than what the provider offers and almost always ends up half in the provider's groups and half in application code.
- The graph of which application may call which, which is a fact about your estate and not about your provider.
Get those four on your side and a provider change is a change in one place. The test is simple and unkind: how many application deployments does a provider change require? If the answer is not zero, the layer is not doing its job, and that includes any layer we sell you.
The objection worth taking seriously
The honest objection to this argument is that you are adding a component to your critical path in order to avoid a project you may never do. If you have five applications and no plausible reason to change provider, that trade is bad and you should not make it.
It gets good quickly, though, and it gets good for reasons other than migration. The same layer is what lets you give each customer a tenant with its own issuer, express authorization your provider cannot express, and hold service accounts and agents in the same model as your people. The migration insurance turns out to be the least interesting thing you bought.
But it is the thing that gets it funded, because it is the thing somebody has already paid for twice.