Introduction
In today's digital landscape, identity management is critical to application security and user experience. As organizations grow and evolve, they often need to consolidate or modernize their identity infrastructure. Auth0, a leading Identity-as-a-Service (IDaaS) platform, provides a robust and flexible solution for managing authentication and authorization. Migrating existing users to a new platform can be a complex undertaking. A poorly executed migration can lead to user frustration, security vulnerabilities, and potential business disruption. Auth0 aims to make this process as simple as possible.
This article provides a comprehensive guide to migrating your user base to Auth0. We will delve into the primary strategies offered by Auth0: Automatic Migration (also known as Trickle or Lazy Migration), and Bulk Migration using the Management API. It is also common to combine both methods, utilizing a trickle migration for a set period of time, and then bulk importing the rest of the user base. Each method comes with its own advantages and considerations, and choosing the right approach depends heavily on your specific requirements, technical constraints, and user impact tolerance. By understanding these methods and planning carefully, you can ensure a smooth and secure transition for your users to the Auth0 platform.
Choosing Your Migration Strategy
Before diving into the technical implementation, selecting the appropriate migration strategy is paramount. Auth0 offers distinct approaches, primarily Automatic (Trickle/Lazy) Migration and Bulk Migration, each suited to different scenarios. The optimal choice depends on carefully evaluating several key factors specific to your organization and user base.
Key Factors to Consider:
- Downtime Tolerance: Can your application afford any downtime during the migration? Bulk migrations might require a maintenance window, especially if the legacy system needs to be taken offline, whereas automatic migration is designed to happen transparently during user logins, minimizing disruption.
- User Experience Impact: How sensitive are your users to changes? Automatic migration typically offers the most seamless experience, as users often don't even realize they are being migrated and don't need to reset their password. Bulk migration, especially with incompatible password hashes, might necessitate a password reset flow for users upon their first login post-migration, which could cause friction.
- Data Complexity: Consider the richness of your user profiles. Are you migrating just usernames and passwords, or do you have complex custom attributes, roles, permissions, and Multi-Factor Authentication (MFA) enrollments? Both methods support migrating additional data, but the complexity might influence the effort required for data mapping and scripting (automatic) or data transformation (bulk).
- Security Requirements: How are user passwords currently stored? If your legacy system uses hashing algorithms incompatible with Auth0, the automatic migration's password verification script needs careful implementation. Bulk migration requires exporting password hashes, demanding strict security protocols during the export and import process. If hashes cannot be exported or are incompatible, a password reset strategy is unavoidable.
- Legacy System Availability: Automatic migration requires the legacy user database to remain accessible and responsive to authentication requests from Auth0 throughout the migration period. Bulk migration allows for the legacy system to be decommissioned sooner after the data export.
- Migration Timeline: Bulk migration generally allows for a faster overall transition, moving all users in a defined period. Automatic migration happens gradually as users log in, potentially taking much longer to complete for the entire user base.
- Technical Resources: Evaluate your team's skills and availability. Automatic migration requires proficiency in writing custom database scripts (typically JavaScript within Auth0). Bulk migration requires data extraction, transformation, and potentially scripting interactions with the Auth0 Management API.
High-Level Comparison:
By carefully weighing these factors against your specific context, you can decide whether Automatic Migration, Bulk Migration, or a strategy using both methods is the best fit for your transition to Auth0.
Automatic User Migration (Trickle/Lazy Migration)
Automatic User Migration, often called Trickle or Lazy Migration, is a strategy designed to move users from your legacy identity system to Auth0 transparently, typically without requiring them to reset their passwords or even notice that a migration is occurring. This method leverages Auth0's Custom Database Connections feature, migrating users individually as they attempt to log in to your application via Auth0.
Concept:
The core idea is simple: when a user tries to log in, Auth0 first checks if that user already exists in an Auth0 database for that connection. If the user exists (meaning they have already been migrated), Auth0 authenticates them directly. If the user does not exist in Auth0, Auth0 invokes custom DB scripts to begin migrating the user. These scripts are responsible for contacting your legacy database, verifying the user's entered credentials against the legacy store, and, upon successful verification, returning the user's profile information to Auth0. Auth0 then automatically creates a profile for the user in its own database using the returned data (including hashing the provided password using modern standards) and completes the login. Subsequent logins for that user will then be handled directly by Auth0, bypassing the legacy database entirely. It is common to use this method for a defined duration of time, and then bulk migrate users that were not automatically migrated over.
Pros:
- Seamless User Experience: For users whose password hashes are compatible or can be verified by your script, the migration is invisible. They log in with their existing credentials without interruption or needing to reset their password.
- Gradual Rollout: Migration happens organically as users interact with your application, reducing the risk associated with a large-scale, single-event migration.
- Reduced Upfront Effort: Unlike bulk migration, the initial data handling effort might be lower as you don't need to export and transform the entire user base simultaneously.
Cons:
- Legacy System Dependency: Your legacy user database must remain operational, accessible, and performant throughout the migration period, as Auth0 needs to query it for non-migrated users.
- Extended Migration Timeline: It can take a significant amount of time for all active users to log in and be migrated, and inactive users may never be migrated via this method.
- Slightly Slower First Login: The very first login attempt for a user undergoing migration involves an extra step (calling the custom script and legacy DB), which might introduce minor latency compared to subsequent logins.
- Script Complexity: Implementing the custom scripts requires careful handling of password verification, error conditions, and data mapping.
Prerequisites:
- An Auth0 plan that includes the Custom Database feature.
- Access credentials and connection details for your legacy user database.
- Network path allowing Auth0's servers to reach your legacy database (ensure firewalls are configured appropriately).
- Understand how passwords are hashed in your legacy system.
Configuration Steps:
Configuring automatic migration involves setting up a specific type of database connection within the Auth0 Dashboard and implementing the necessary action scripts.
- Create Custom Database Connection: Navigate to
Auth0 Dashboard > Authentication > Database
. ClickCreate DB Connection
. Give it a name (e.g.,legacy-users-migration
) and clickCreate
. - Enable Custom Database: In the connection's settings, go to the
Custom Database
tab. Toggle the switch forUse my own database
to ON. This reveals the Database Action Scripts section. - Enable Import to Auth0: Go to the
Settings
tab for the connection. Scroll down and toggle the switch forImport Users to Auth0
to ON. This crucial step tells Auth0 to store users in its own database after successful authentication via the custom scripts. ClickSave
. - Implement Database Action Scripts: Return to the
Custom Database
tab. You need to implement at least theLogin
script.Login
Script (Mandatory): This script is the heart of the automatic migration. It receives theemail
(or username) andpassword
entered by the user, along with acallback
function.- Purpose: Verify the provided
password
against the user's record in your legacy database. Handle password hashing comparison correctly (e.g., usingbcrypt.compare
if your legacy DB uses bcrypt). - On Success: If credentials are valid, retrieve the user's profile data from the legacy database (must include at least
user_id
andemail
; includeemail_verified
and any other relevant attributes likename
, custom fields, etc.). Call thecallback
function withnull
for the error and the user profile object:callback(null, userProfile);
. - On Failure: If credentials are invalid or the user doesn't exist in the legacy DB, call the
callback
with an error object (e.g.,callback(new Error('Invalid credentials'));
) or simplycallback(null, null);
if you want Auth0 to handle it as user not found. - Example Snippet (Conceptual - Node.js style):
- Purpose: Verify the provided
function login (email, password, callback) { // 1. Connect to your legacy database legacyDb.connect(function(err, client) { if (err) return callback(err); // 2. Find user by email in the legacy DB client.query('SELECT id, email, hashed_password, name FROM users WHERE email = $1', [email], function(err, result) { if (err || result.rows.length === 0) { // User not found or DB error return callback(null, null); } const user = result.rows[0]; // 3. Compare the provided password with the stored hash bcrypt.compare(password, user.hashed_password, function(err, isValid) { if (err || !isValid) { // Password mismatch or bcrypt error return callback(new Error('Invalid credentials')); } // 4. Construct Auth0 user profile const userProfile = { user_id: user.id.toString(), // Must be a string email: user.email, name: user.name, // Add other custom attributes from your legacy DB here email_verified: true // Assuming email is verified in legacy DB }; // 5. Return profile to Auth0 for migration callback(null, userProfile); }); }); }); }
Get User
Script (Optional but Recommended): This script runs in scenarios like signup attempts (to check if user already exists in legacy DB), password changes initiated via Auth0, or certain Management API calls. It receives theemail
(or username) and acallback
.- Purpose: Retrieve a user's profile from the legacy database without verifying a password. This helps keep Auth0's profile synchronized if changes happen outside the login flow or prevents duplicate signups.
- Implementation: Similar to the
Login
script but only fetches the user profile based on email/username and callscallback(null, userProfile)
if found, orcallback(null, null)
if not.
- Test Connection: Use the "Try" button within the Action Scripts editor to test your scripts with sample credentials before enabling the connection for your applications.
Post-Migration:
Once you are confident that most or all active users have been migrated (you can monitor this by checking the user count in your Auth0 database connection versus your legacy system), you might want to disconnect the reliance on the legacy database.
- Update Action Scripts: Modify both the
Login
andGet User
scripts to simply returncallback(null, null);
. This effectively stops Auth0 from querying your legacy database. - Keep "Import Users to Auth0" Enabled: Crucially, do not disable the
Import Users to Auth0
toggle in the connection settings. If you disable this after users have been imported, Auth0 will revert to always calling your (now non-functional) scripts, effectively locking users out. Keeping it enabled ensures Auth0 continues to use its internal store for already-migrated users. - Decommission Legacy Database: At this point, you can safely plan the decommissioning of your legacy user database infrastructure.
Bulk User Migration
Bulk User Migration offers a different approach than the gradual, login-triggered automatic method. It involves exporting user data from your legacy system and importing it into Auth0 in batches, typically as a planned, offline process. This strategy is often preferred when a faster overall migration timeline is desired or when the legacy system needs to be decommissioned relatively quickly.
Concept:
The fundamental process involves extracting user profile information, including critically important password hashes, from your existing identity store. This data is then transformed into a specific format (usually JSON) that Auth0 understands. Finally, you use Auth0's tools – primarily the Management API or the User Import/Export Extension – to upload this data and create the corresponding user profiles within your Auth0 tenant's database connection.
Pros:
- Faster Migration Timeline: Allows for migrating large numbers of users in a relatively short, defined period.
- Legacy System Decommissioning: Enables the legacy identity system to be taken offline sooner, potentially reducing operational costs and complexity.
- Predictable Process: The migration occurs within a planned window, offering more predictability than waiting for users to log in individually.
Cons:
- Potential User Disruption: If legacy password hashes are incompatible with Auth0 or cannot be exported securely, users will likely need to reset their passwords upon their first login after migration, which can impact user experience.
- Data Handling Complexity: Requires careful extraction, validation, and transformation of user data into the required format. Errors in the data can cause import failures.
- Security Considerations: Exporting user data, especially password hashes, requires stringent security measures to protect the data in transit and at rest.
- Possible Downtime: Depending on the coordination needed and whether the legacy system is taken offline during the import, a maintenance window might be required.
Methods Overview:
Auth0 provides two main mechanisms for bulk importing users:
- Management API (
POST /api/v2/jobs/users-imports
): This is the recommended approach for most scenarios, especially for large user bases or when automation is desired. It offers greater control, flexibility, and scalability through programmatic interaction. - User Import/Export Extension: A tool available in the Auth0 Dashboard that provides a user interface for uploading user files (JSON or CSV). It's simpler for smaller, manual imports, but less suitable for large-scale or automated migrations.
This guide focuses on the Management API method due to its robustness for typical migration projects.
Using the Management API (/api/v2/jobs/users-imports
):
This asynchronous endpoint allows you to submit a user data file and initiate an import job.
Prerequisites:
- Target Auth0 Connection: You need an existing Auth0 Database Connection where the users will be imported. Note its
connection_id
. - Management API Token: Obtain an API token from your Auth0 Dashboard (
Applications -> APIs -> Auth0 Management API -> API Explorer
tab). This token requires thecreate:users
permission (scope). - User Data Extract: A file containing the user data extracted from your legacy system.
Password Hashing - A Critical Consideration:
Successfully migrating passwords without forcing a reset hinges on whether your legacy system's password hashing algorithm is compatible with Auth0. Auth0 supports several standard hashing algorithms (e.g., bcrypt, PBKDF2, scrypt - check Auth0 documentation for the current list and specific parameters).
- Compatible Hashes: If your legacy hashes are compatible, you can include them directly in the import file. Auth0 will recognize the format and store them securely, allowing users to log in with their existing passwords.
- Incompatible Hashes: If your legacy algorithm is unsupported or if you cannot securely export the hashes, you cannot directly migrate the passwords. In this case:
- Omit the password hash from the import file.
- Users will need to perform a password reset upon their first login attempt after being imported.
- Communicate this requirement clearly to your users beforehand.
- Security: Never import plain-text passwords. Always work with hashes.
Preparing the User Data File:
- Format: The API endpoint expects user data in a JSON file. Each entry in the JSON array represents a user.
- Schema: The structure of each user object must adhere to Auth0's requirements. Key fields include:
email
(string, required)email_verified
(boolean, required)user_id
(string, optional but recommended for linking to legacy ID)password_hash
(object, required if migrating passwords - see Auth0 docs for structure based on algorithm)app_metadata
(object, optional)user_metadata
(object, optional)custom_password_hash
(object, for unsupported hashes if using custom script - less common for bulk import)- Refer to the official Bulk Import Database Schema and Examples documentation for precise details on all fields and password hash structures.
- File Size Limit: Each import job accepts a JSON file up to 500KB. For larger user bases, split your data into multiple files and submit multiple import jobs.
- Streaming: The API endpoint is designed to handle streamed data. When using tools like
curl
or Node.js, ensure you are streaming the file content rather than loading the entire file into memory.
Initiating the Import Job:
You trigger the import by making a POST
request to the /api/v2/jobs/users-imports
endpoint. This is a multipart/form-data
request.
- Headers: Include
Authorization: Bearer YOUR_MGMT_API_TOKEN
. - Form Data Parameters:
users
: The JSON file containing user data.connection_id
: The ID of the target Auth0 database connection.upsert
(boolean, optional, default:false
): Iftrue
, the job attempts to update existing Auth0 users matching on email, user_id, etc. Use with caution, as it only updates certain fields. Iffalse
, the job will fail if duplicate users are found.external_id
(string, optional): A custom identifier you can assign to the job for tracking purposes.send_completion_email
(boolean, optional, default:true
): Set tofalse
if you don't want Auth0 to email tenant administrators upon job completion or failure.
Example (curl
):
curl --request POST \ --url https://YOUR_DOMAIN/api/v2/jobs/users-imports \ --header 'Authorization: Bearer YOUR_MGMT_API_TOKEN' \ --form 'users=@"/path/to/your_users.json"' \ --form 'connection_id="con_xxxxxxxxxxxxxxxx"' \ --form 'send_completion_email=false'
Monitoring the Job:
The initial POST
request returns immediately with a job ID and a pending
status.
{ "status": "pending", "type": "users_import", "created_at": "2025-04-30T19:00:00.000Z", "id": "job_xxxxxxxxxxxxxxxx" }
Bulk import is an asynchronous process. You need to poll the status of the job using its ID:
- Endpoint:
GET /api/v2/jobs/{id}
(where{id}
is thejob_id
from the response above). - Request: Include the
Authorization
header with your API token. - Response: The response will show the current status (
pending
,processing
,completed
,failed
) and, upon completion, a summary including counts of created, updated, and failed users, along with specific error details if the job failed.
Concurrency Limits:
Be aware that Auth0 typically limits concurrent bulk import jobs (often to two). If you submit too many jobs simultaneously, you will receive a 429 Too Many Requests
error. Wait for existing jobs to complete before submitting new ones.
Bulk migration using the Management API provides a powerful and scalable way to transition users to Auth0, especially when automation and speed are key requirements. Careful planning around data extraction, transformation, password hashing, and job monitoring is essential for success.
Ready to modernise your identity stack with Auth0?
Whether you want a seamless user experience through automatic migration or prefer the control of a bulk migration script, the right approach lets you upgrade without disrupting your users. Check out the full guide to dive into implementation details and choose the migration path that fits your architecture.
References
- Auth0 Documentation - Import and Export Users
- Auth0 Documentation - Configure Automatic Migration from Your Database
- Auth0 Documentation - Bulk User Imports
- Auth0 Documentation - Bulk Import Database Schema and Examples (Inferred link, mentioned in bulk import docs)
- Auth0 Documentation - Configure Cloudflare as Reverse Proxy
- Auth0 Management API v2 (General reference for API endpoints mentioned)
About the author

David Bolton
Senior Engineer, Office of the Field CTO
David started in the technical field with Apple nearly a decade ago. He then held various positions in the industry, from software engineering to founding ambassador. David developed a passion for cybersecurity, leading to his joining Auth0 four years ago. He has since worked pre- and post-sales, specializing in designing scalable AuthZ and AuthN solutions for large enterprises and on the Okta for Good leadership council.
When not working, he is likely playing tennis, tinkering with a computer, or searching for a new restaurant to try.