Authentication-provider migrations often look like configuration work.
Change the issuer, replace a frontend library, update the backend token validation, and deploy.
That was only a small part of this migration.
The application had roughly 1,000 production users, a microservice backend, a separate frontend, several user roles, administrative account-management workflows, and existing password hashes stored in PostgreSQL.
The real requirement was:
Move from Okta to Auth0 without forcing users to recreate accounts or reset their passwords.
The migration also had to preserve roles, keep administrative workflows working, and avoid changing production identity references until the new provider had been tested.
Why we moved away from Okta
The application had been using Okta Developer Edition.
It had fit the project well and had been inexpensive enough for the stage of the product. Later, Okta changed the conditions around that offering, and continuing with the existing setup was no longer practical.
That forced us to choose a new authentication provider.
Auth0 was selected as the replacement, but changing providers meant migrating much more than login configuration.
The existing setup included:
- approximately 1,000 users;
- regular users and administrators;
- internal system users;
- Okta user IDs stored in PostgreSQL;
- password hashes stored in PostgreSQL;
- role information stored both as Okta groups and in the application database;
- backend operations for creating, updating, activating and deactivating users;
- a frontend and multiple backend services that depended on Okta-issued tokens.
The goal was to preserve the existing behaviour while replacing the identity platform underneath it.
The target architecture
After the migration, Auth0 became responsible for authentication and identity management.
User
|
v
Frontend application
|
| Auth0 login
v
Auth0
|
| access token
v
Spring Boot microservices
|
| token validation
| role-based authorization
v
Application functionality
Administrative services
|
| Auth0 Management API
v
Create, update, activate,
deactivate and assign roles
The frontend authenticated users through Auth0.
The backend services validated Auth0-issued tokens and continued using Spring Security authorization rules, including method-level checks such as @PreAuthorize.
Administrative operations remained server-side and used credentials configured through the Auth0 dashboard and Management API.
Identity data existed in two systems
Before the migration, identity data was split between Okta and PostgreSQL.
PostgreSQL contained information such as:
- user email;
- Okta identity ID;
- stored password hash;
- application role information;
- local user state.
Okta contained the external identity and group assignments used by the running application.
This meant that neither system could be migrated independently.
Creating Auth0 users from Okta alone would not have preserved all application data. Migrating only the PostgreSQL records would not have reproduced the external identity and authorization configuration.
The migration therefore had to combine data from both sources.
Roles needed explicit mapping
The main application roles included:
- administrator;
- regular user;
- system user.
Role information existed both in Okta groups and in the application database.
One Okta-specific concept was the Everyone group. That did not need to be reproduced as a separate Auth0 concept. In the new model, ordinary users received the regular-user role instead.
The migration therefore needed an explicit mapping:
Okta group + database role
|
v
Auth0 role
|
v
token authorization data
|
v
Spring Security permissions
This was important because successful authentication would not have been enough. A migrated user also needed the same effective permissions after the switch.
A staged migration using Python scripts
The migration was implemented as several Python scripts rather than one large all-or-nothing program.
Conceptually, the stages were:
PostgreSQL users
email + Okta ID + password hash
|
v
Export script
|
v
Auth0 import
users + existing credentials
|
v
Role-assignment script
|
v
Auth0 identity validation
|
v
Update PostgreSQL with Auth0 IDs
The scripts handled separate responsibilities:
- Read the required user data from PostgreSQL.
- Create or import the corresponding users into Auth0 while preserving their existing credentials.
- Assign the appropriate Auth0 roles.
- Validate the new identities.
- Update PostgreSQL with the new Auth0 user IDs only after the migration had been proven.
Keeping these stages separate made the process easier to test and easier to stop if one part produced unexpected results.
Preserving existing credentials
The application already stored password hashes in PostgreSQL.
Those hashes were used during the Auth0 migration so that users could continue signing in with the same passwords after the cutover.
This was one of the most important requirements. Forcing roughly 1,000 users through a password-reset process would have created unnecessary support work and made the migration much more disruptive.
The migration logic therefore treated password data carefully:
- raw passwords were never required;
- existing password hashes were read only for the migration;
- users were created in Auth0 using the preserved credential data;
- login was tested before production identity references were changed.
The exact password-hash import configuration depended on the source format supported by the target connection, but the business outcome was clear: users retained their existing credentials.
Why the process was safe to rerun
The migration did not immediately overwrite production identity references.
The existing PostgreSQL records remained intact while Auth0 users were created and tested.
This meant that during the migration:
- Okta remained the active provider;
- existing
okta_idvalues remained available; - failed Auth0 imports could be corrected without breaking current users;
- role assignments could be rerun;
- production references were updated only after validation.
The process was therefore recoverable even though the scripts were not one atomic transaction across every system.
The important safety property was not simply technical idempotency. It was that the current production identity data remained untouched until the new state had been checked.
Testing before production cutover
The migration was tested in stages.
First, a small number of test users were migrated.
For those users, I checked:
- login with the existing password;
- correct role assignment;
- access to allowed functionality;
- rejection from restricted functionality;
- correct connection between the Auth0 identity and the local user record.
Once the test users behaved correctly, the migration scripts were run for the wider user population.
After the production migration, several trusted users were asked to verify that their existing credentials still worked in the live system.
This provided a practical check across real accounts and usage patterns, rather than relying only on administrative API responses.
Application changes
The provider migration required coordinated frontend and backend deployment.
Frontend
The frontend login integration was changed from Okta to Auth0.
The new deployment needed to handle:
- login;
- logout;
- token acquisition;
- session restoration;
- authenticated API calls;
- role-dependent interface behaviour.
Spring Boot microservices
The backend services were updated to trust Auth0-issued tokens instead of Okta-issued tokens.
Existing Spring Security authorization remained in place. Services continued to protect methods and endpoints using token-derived roles and checks such as @PreAuthorize.
The migration was therefore not a rewrite of the application’s authorization model. It was a replacement of the identity provider and token source while preserving the existing security behaviour.
Administrative workflows
The application continued supporting the same administrative operations:
- create user;
- update user;
- deactivate user;
- activate user;
- assign roles.
These operations were adapted to use Auth0 instead of Okta.
The former Okta Everyone group behaviour was replaced by assigning the regular-user role where appropriate.
Cutover
Before the final deployment, users continued authenticating through Okta.
The production cutover required deploying both the frontend and the affected backend services so that the entire application switched providers consistently.
The sequence was approximately:
- Complete the Auth0 user import.
- Assign and verify roles.
- Confirm test-user login.
- Update local identity references.
- Deploy the Auth0-enabled backend services.
- Deploy the Auth0-enabled frontend.
- Validate production login and authorization.
- Ask selected users to confirm access with their existing credentials.
The service deployment and verification resulted in roughly 20 to 30 minutes of downtime.
Before the deployment, the application used Okta. After it, the application used Auth0.
Because the user data and credentials had already been migrated and validated, the cutover itself was mostly about switching the running application to the new provider.
Result
Approximately 1,000 users were migrated successfully.
Users were able to continue signing in with their existing credentials.
The main roles and permissions were preserved, and the application retained its existing administrative workflows for user creation, updates, activation, deactivation and role assignment.
The migration required a short maintenance window of roughly 20 to 30 minutes for the coordinated frontend and microservice deployment.
No major production issues were discovered after the switch.
The visible outcome was simple: users opened the application and continued logging in as before.
Behind that result was a staged migration across two identity systems, the application database, the frontend and multiple backend services.
What made the migration safe
Three decisions mattered most.
The old identity references remained intact during preparation
Okta continued serving production while Auth0 users and roles were created and tested.
The migration was split into stages
Exporting users, importing identities, assigning roles and updating local IDs were separate operations. A problem in one stage did not require restarting the entire migration blindly.
Real logins were tested before and after cutover
API responses confirmed that users existed, but successful login with existing credentials confirmed that the migration worked from the user’s perspective.
What I would improve next time
I would produce a formal reconciliation report automatically.
For every migrated user, it could record:
Email
Original Okta ID
New Auth0 ID
Expected role
Assigned role
Credential imported
Login validated
Local ID updated
That would make it easier to prove that all source users had been accounted for and that no role assignment was missed.
I would also automate representative authorization tests for each main role. Those tests could obtain a real Auth0 token and verify access to selected protected endpoints.
Finally, I would reduce the cutover downtime by preparing deployment sequencing and health checks more aggressively. The 20-to-30-minute maintenance window was acceptable, but a more automated release process could likely shorten it.
The main lesson
An authentication-provider migration is not just a change to the login screen.
It combines:
- identity-data migration;
- credential migration;
- role mapping;
- frontend integration;
- backend token validation;
- administrative API replacement;
- coordinated production deployment.
The migration was successful because the new identities were prepared and tested before the old provider was removed from the running application.
The most important result was not that Auth0 replaced Okta.
It was that roughly 1,000 users kept their accounts, passwords and permissions while the underlying authentication platform changed.