Migrating identity providers without a flag day: A zero-downtime playbook
A four-phase playbook for moving off Auth0, Cognito, Clerk, or Firebase without a 2 AM incident.
Switching identity providers is one of the highest-stakes infrastructure changes you can make. It touches every user session, every login flow, every SSO connection. Get it wrong and you're staring at a support queue full of locked-out customers. Frontegg recently kicked off a migration mini-series acknowledging this exact problem. Provider switching is something more teams are dealing with, and most of them are terrified of it.
They should be. But not because migration is inherently dangerous. They should be terrified of the flag day: that moment where you flip a switch, pray everything works, and roll back if it doesn't. Flag days are how migrations fail.
Here's the WorkOS playbook for migrating from Auth0, Cognito, Clerk, or Firebase Auth without one.
The core principle: No flag days
A flag day migration means you cut over all users from the old provider to the new one at a single point in time. Every user. Every connection. All at once.
This is tempting because it's conceptually simple. It's also how you end up with an incident at 2 AM.
The alternative is running both providers in parallel and migrating users gradually. More work upfront, but it eliminates the single point of failure that makes flag days so dangerous. The same pattern applies to identity providers as it does to database cutovers, API versioning, and DNS changes.
The playbook has four phases: shadow authentication, JIT provisioning on login, password hash import, and SSO connection cutover.
Phase 1: Shadow authentication
Shadow authentication means running your old identity provider and WorkOS side by side. Both systems are live. Both can authenticate users. Your application routes login requests to the appropriate provider based on whether a given user has been migrated yet.
The key decisions during this phase:
- Where does the routing logic live? Your application needs to know which provider owns a given user at any point in time. A simple flag on the user record works. If the flag says "migrated," route to WorkOS. Otherwise, route to the legacy provider.
- How long do you run both? As long as it takes. The whole point is removing time pressure. Some teams run shadow auth for weeks. That's fine.
- What about new users? New signups go straight to WorkOS from day one. Only existing users need the routing logic.
Phase 2: Gradual user migration via JIT provisioning
Once shadow auth is in place, you migrate users one at a time, at the moment they log in. This is just-in-time (JIT) provisioning applied to a migration context.
Here's how it works: a user who hasn't been migrated yet hits your login flow. Your application authenticates them against the legacy provider as usual. On successful authentication, you provision that user in WorkOS, flip the migration flag, and from that point forward all their login requests go to the new provider.
The user notices nothing. No password reset email. No "please re-authenticate" interstitial. They logged in, and now they're on the new system.
JIT provisioning handles your active users automatically. Users who haven't logged in during your migration window need a different approach.
Phase 3: Password hash import
JIT provisioning catches users who log in during the migration window. But what about users who don't? You can't leave them on the legacy provider forever, and you can't ask them all to reset their passwords.
Password hash import solves this. You export password hashes from your legacy provider and import them into WorkOS. The user's existing password continues to work. They authenticate against WorkOS using the same credentials they've always had.
The specifics of hash export vary by provider. Auth0 uses bcrypt and supports bulk user export via the Management API, but password hashes are not included in that export. You need to open a support ticket to request them separately, and the process can take a week or more. Cognito does not support password hash export at all, so users who haven't logged in during JIT provisioning will need to reset their passwords; a Migrate User Lambda can reduce the blast radius here by letting Cognito authenticate users on first login to the new system. Clerk exports bcrypt hashes via a CSV download from the dashboard or the Backend API, and WorkOS supports bcrypt natively, so hashes can be imported directly using the WorkOS Update User API without requiring any password resets. Firebase Auth uses a modified scrypt variant and provides hash export via the Firebase CLI along with the hash parameters needed for verification. The critical thing across all providers is preserving the hash, salt, and algorithm metadata so the receiving system can verify passwords without ever seeing them in plaintext.
After the hash import (or alternative migration strategy for providers that don't support export), you can flip the remaining users' migration flags and shut down the routing logic. Every user is now on WorkOS, whether they logged in during the migration window or not.
Phase 4: SSO connection cutover
SSO connections are the trickiest part of any identity provider migration, because each SSO configuration (specifically the SP metadata such as the ACS URL and entity ID) points to a single identity system. You can't run shadow auth on an SSO connection the way you can with password-based users.
SSO connections therefore require a coordinated cutover per connection. Not a flag day for your entire user base, but a mini-cutover for each SSO-connected customer. How you handle that cutover depends on how many connections you're moving.
Fewer than 15 connections: Admin Portal handoff. Generate a setup link per connection via the WorkOS Admin Portal and send it to each customer's IT admin. They reconfigure their IdP to point at WorkOS. Coordinate a change window, make the switch, verify login works. If something breaks, pointing the connection back to the legacy provider is a configuration change, not a code deployment.
15 or more connections: transparent proxy. At this scale, coordinating individual IdP reconfigurations with IT admins across dozens of enterprise customers dominates the actual technical work. The alternative is a transparent proxy: your existing callback URL stays in place and routes SAML responses and OIDC callbacks to WorkOS behind the scenes. Customers don't change anything in their IdP. You opt connections in one at a time behind a feature flag, and if anything regresses, you flip the flag and traffic returns to the previous path on the next request. For a detailed walkthrough of the proxy architecture and a worked feature-flag rollout example, see our post on migrating auth at scale.
In both cases, sequence by risk: start with your most flexible customers or internal SSO connections and save the largest enterprise customers for after you've built confidence. Because each connection is independent, you can spread cutovers over days or weeks. One customer at a time. No flag day.
Putting it all together
The full migration sequence looks like this:
- Stand up WorkOS alongside your legacy provider (shadow authentication).
- Route new signups to WorkOS immediately.
- Migrate active users via JIT provisioning on login.
- Import password hashes for inactive users (or trigger password resets for providers like Cognito that don't support hash export).
- Cut over SSO connections one at a time.
- Decommission the legacy provider.
Each phase reduces the number of users still on the old system. By the time you reach step six, there's nothing left to migrate. You're just turning off something that's already idle.
The flag day temptation is real. Parallel systems are more operational overhead. The routing logic isn't glamorous. But the alternative is rolling the dice on a single cutover for every user in your system. That's not a plan. It's a bet.
Run both systems. Migrate gradually. Cut over SSO connections individually. Decommission when there's nothing left to decommission. That's how you switch identity providers without waking anyone up at 2 AM.