You can move users to a new login system without making anyone reset their password. To do it, you either import their existing password hashes or check each password once against the old system the next time that user signs in. Most legacy application modernization projects use both methods, with a short period where the old and new identity providers run side by side.
Picture your situation. Your user table is full of hashes from a framework nobody on the team chose. The board wants the new platform live without a wave of "I can't log in" tickets. This guide is written for that moment.
Why can't you just copy the passwords across?
You never had the passwords. You have hashes, and a hash only works with the exact algorithm, salt and settings that created it. The new identity provider can only check a password against an old hash if it understands that format.
Modern providers such as Auth0, Okta, AWS Cognito, Keycloak and Microsoft Entra all handle bcrypt, PBKDF2, scrypt or Argon2 in some form, either natively or through an import hook. The trouble starts with homegrown schemes: unsalted MD5, SHA-1 with a global pepper kept in a config file, or a custom hashing loop someone wrote years ago. Those need custom verification code or a lazy migration.
Then there is everything around the password. MFA secrets, social login links, "remember me" cookies, mobile refresh tokens and API keys all live in the old system too, and each one needs a plan.
What does getting the login switch wrong cost you?
The login page is the one screen every user touches.
The failures are predictable. A user who signed up as "John@Company.com" and later as "john@company.com" becomes two accounts. Mobile apps holding old refresh tokens log everyone out at once. MFA users are asked to enrol again with no warning and assume they have been hacked. A stricter password policy kicks in at first login and blocks people halfway through a task.
Each of those turns into support tickets, paying customers losing their sessions, and a rollback conversation nobody wants. A rushed import can also leave weak legacy hashes sitting in your new provider for years. That turns old security debt into permanent security debt.
Option one: bulk hash import
You export every user record with its hash, salt and algorithm settings, then load them all into the new provider at once. On cutover day, users sign in with their existing password and notice nothing.
When it fits: the old hashes use a standard algorithm the new provider supports, and you want a clean cutover date.
Risks to manage:
- Weak hashes come along for the ride. If the legacy store uses MD5 or SHA-1, have the team wrap each one during export (for example, bcrypt applied over the old hash) so nothing weak is stored at rest. Then upgrade to a native hash at each user's next login.
- Data drift between export and cutover. Anyone who changes their password after the export ends up with a stale hash. Plan a final delta sync during a short change freeze.
- Identity collisions. Normalise emails and resolve duplicates before the import, not after.
Option two: rehash each user at their next login
This is also called lazy or just-in-time migration. When the new provider gets a login from a user it doesn't recognise, it calls a small piece of code that checks the password against the old system. If the password matches, it creates the user with a fresh, modern hash. The Cognito user migration trigger and Auth0 custom database scripts are built for this.
When it fits: the hash format is unusual, you can't export the hashes safely, or you want every stored password upgraded to a modern algorithm.
Risks to manage:
- The long tail. Active users move over quickly. Dormant users may never log in, so the old system has to stay reachable until you decide what happens to them.
- The new system now depends on the old one. If the legacy database is slow or down, logins fail. Require a strict timeout and a clear error message.
- Brute force exposure. The migration code path needs the same rate limiting and lockout rules as the main login.
Plan for the stragglers from day one, not in month six.
Option three: run old and new identity providers side by side
Both systems stay live for a fixed window. A routing layer decides which provider handles each login, usually based on a "migrated" flag on the user. New features go on the new provider, and users move across in waves.
When it fits: you have enterprise customers with their own SAML or OIDC connections, or several apps share one login and can't all move on the same day.
Risks to manage:
- Two sources of truth. Profile edits, deactivations and password changes must sync both ways. Otherwise a deactivated employee keeps access through the old door.
- Session confusion. Every app must accept tokens from both issuers during the window, then stop accepting old tokens on a set date.
- The window never closes. If the plan has no end date, you end up paying for and securing two systems indefinitely.
Which approach should you choose?
Use these rules:
- If the hashes are standard and most users are active, do a bulk import with a final delta sync.
- If the hashes are weak or custom, use lazy migration with a fixed end date. After that date, send remaining users a friendly reset email.
- If you have enterprise SSO connections or several apps, run both providers side by side and migrate tenant by tenant.
- If you aren't sure, combine them. Bulk import the users you can, lazy migrate the rest, and keep the old provider read-only as a fallback.
The trade-off is simple. Bulk import is fast but carries old weaknesses forward. Lazy migration is cleaner but slow to finish. Side by side is the most flexible and needs the most discipline.
Who doesn't need any of this?
Some teams can skip all of it. Say your app is an internal tool used by one office, and everyone can walk over to IT. Then a planned reset with a clear email and a help desk on standby is the honest, cheap answer. Don't build a migration pipeline for a problem one well-written email solves.
What should you require from your team before the switch?
Get these in writing before anyone touches production:
- A hash inventory. List every algorithm in the user table with a count of users on each. Older apps often hold several generations of hashing, and one unknown format can break a bulk import.
- An identity cleanup report. Cover duplicates, case clashes and missing emails. Collisions cause most "wrong account" tickets.
- An MFA and session plan. Can TOTP secrets be exported? What happens to existing mobile sessions? Surprise logouts and forced MFA re-enrolment drive most support volume.
- A rollback path. Know how to point logins back at the old system within minutes. You want that decided calmly, not during an incident.
- A shared dashboard. Show login success rate, failure reasons and migration progress, and make it visible to support. That way the team spots a spike before the inbox does.
- A dated end state. Agree when the old provider gets switched off and what dormant users will receive. This stops the side-by-side window from becoming permanent.
One question tests any vendor quickly: what happens to a user with MFA turned on who hasn't logged in for two years? A good team answers right away. A weak one starts guessing.
Authentication is usually one part of a larger modernisation. For how to sequence the rest without breaking what users rely on, read this legacy system modernization guide.
If you'd like a second opinion on your login migration plan, ask Geminate Solutions for a free review.
Top comments (0)