Skip to content

Auth0 Custom Database — Migrate Existing Users to Auth0

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Auth0 Custom Database. We cover key concepts, practical examples, and best practices to help you master this topic.

Auth0 custom database connections let you connect to your existing user database, using custom scripts for authentication, so you can migrate users to Auth0 gradually or keep them in your legacy database.

What You'll Learn

By the end of this lesson you will configure a custom database connection, write login and get-user scripts, implement progressive Migration, handle legacy password hashing, and decommission the legacy database.

Why It Matters

Migrating existing users to a new auth system is risky -- password hashes cannot be reversed, and forcing all users to reset passwords causes churn. Custom database connections solve this with progressive migration.

Real-World Use

DodaZIP had 50,000 existing users with bcrypt-hashed passwords when migrating to Auth0. A custom database connection authenticated users against the legacy database and progressively copied them to Auth0.

flowchart LR
    U[User] -->|Login| A[Auth0]
    A -->|Get User| CDB[Custom Database]
    CDB -->|Script: Login| DB[(Legacy Database)]
    DB -->|Password verified| CDB
    CDB -->|Create user| A
    A -->|Next login| A[(Auth0 Database)]
    U -->|Login again| A
    style A fill:#eb5424,color:#fff

Creating a Custom Database Connection

Set up a connection to your legacy user database.

# custom_db_setup.py
# Custom database connection setup

def custom_db_config():
    print("Custom Database Connection Setup:")
    print()
    print("1. Auth0 Dashboard > Authentication > Database")
    print("2. Create a new database connection")
    print("3. Name: Legacy-Users-DB")
    print("4. Disable 'Auth0 will manage user identities'")
    print("5. Enable 'Use my own database'")
    print()
    print("Connection settings:")
    print("  - Database name in Auth0: Legacy-Users-DB")
    print("  - Your database type: MySQL/PostgreSQL/MongoDB")
    print("  - Connection method: Custom scripts")
    print("  - Import mode: Disabled (we use custom scripts)")

custom_db_config()

Writing the Login Script

Authenticate users against your legacy database.

// Custom Database Script: Login
// This script runs when a user tries to log in

function login(email, password, callback) {
  const mysql = require('mysql2');
  
  const connection = mysql.createConnection({
    host: configuration.DB_HOST,
    user: configuration.DB_USER,
    password: configuration.DB_PASSWORD,
    database: configuration.DB_NAME
  });
  
  connection.query(
    'SELECT id, email, password_hash, name FROM users WHERE email = ?',
    [email],
    function(err, results) {
      if (err) {
        callback(new Error('Database error'));
        return;
      }
      
      if (results.length === 0) {
        callback(new WrongUsernameOrPasswordError(email));
        return;
      }
      
      const user = results[0];
      
      // Verify password (using bcrypt)
      const bcrypt = require('bcrypt');
      bcrypt.compare(password, user.password_hash, function(err, isValid) {
        if (err || !isValid) {
          callback(new WrongUsernameOrPasswordError(email));
          return;
        }
        
        callback(null, {
          user_id: user.id.toString(),
          email: user.email,
          name: user.name,
          app_metadata: {
            migrated_from: 'legacy'
          }
        });
      });
      
      connection.end();
    }
  );
}
# login_script.py
# Understanding the login script

def login_script_requirements():
    print("Login Script Requirements:")
    print()
    print("Input:")
    print("  - email (string): User's email address")
    print("  - password (string): User's password (plaintext)")
    print()
    print("On success, callback with user profile:")
    print("  { user_id, email, name, app_metadata, ... }")
    print()
    print("On failure:")
    print("  callback(new WrongUsernameOrPasswordError(email))")
    print("  or callback(new Error('reason'))")
    print()
    print("Available variables:")
    print("  - configuration: Custom settings from Auth0")
    print("  - global: shared global object")
    print("  - require(): Node.js modules")

login_script_requirements()

Progressive Migration Strategy

Migrate users gradually without downtime.

// Custom Database Script: Login (with progressive migration)

function login(email, password, callback) {
  // First, try Auth0's managed database
  const db = connection;
  
  db.query('SELECT * FROM users WHERE email = ?', [email], (err, results) => {
    if (err) {
      callback(new Error('Database error'));
      return;
    }
    
    if (results.length === 0) {
      // User not in legacy database
      callback(new WrongUsernameOrPasswordError(email));
      return;
    }
    
    const user = results[0];
    // Verify password...
    
    // After successful verification:
    // Import the user to Auth0's managed database
    callback(null, {
      user_id: user.id.toString(),
      email: user.email,
      name: user.name,
      // Mark as migrated
      app_metadata: { migrated: true }
    });
    
    // On next login, Auth0 finds the user in its own database
    // and skips the custom database script
  });
}
# progressive_migration.py
# Progressive migration strategy

def migration_phases():
    print("Progressive Migration Phases:")
    print()
    print("Phase 1: Custom Database Only")
    print("  - Auth0 delegates all authentication to legacy DB")
    print("  - No users in Auth0's managed database")
    print("  - Login script reads from legacy DB")
    print()
    print("Phase 2: Progressive Migration Active")
    print("  - Login script returns user profile")
    print("  - Auth0 automatically copies user to managed DB")
    print("  - After first successful login, user is migrated")
    print()
    print("Phase 3: Import Complete")
    print("  - Most users have been migrated")
    print("  - Disable custom database (make it read-only)")
    print("  - Only remaining users use the custom DB")
    print()
    print("Phase 4: Legacy Decommission")
    print("  - All users migrated to Auth0")
    print("  - Remaining non-migrated users receive password reset")
    print("  - Legacy database can be archived")

migration_phases()

Handling Legacy Password Hashes

Support different hashing algorithms during migration.

# password_handling.py
# Legacy password hash handling

def password_strategies():
    strategies = {
        "bcrypt": "Supported natively by Auth0. Use bcrypt.compare in login script.",
        "pbkdf2": "Use Node.js crypto.pbkdf2Sync in login script.",
        "MD5/SHA1": "Wrapped hashes: convert legacy hash + salt to bcrypt format.",
        "Argon2": "Use argon2 npm package in login script.",
        "Custom algorithm": "Implement your verification function in the login script.",
        "Plaintext": "Strongly discouraged. Upgrade to bcrypt during migration.",
    }
    
    print("Password Hash Strategy:")
    for hash_type, strategy in strategies.items():
        print(f"  {hash_type:20s} | {strategy}")

password_strategies()

Common Mistakes

  1. Not handling progressive migration correctly: The login script must return the user profile to trigger Auth0's automatic import. Missing fields prevent migration.

  2. Connecting to production database from scripts: Use a read-replica or staging database. Login scripts connect from Auth0's servers and should not hit production directly.

  3. Not handling database connection failures: Database timeouts or connection issues cause login failures for all users. Implement retry logic and monitoring.

  4. Exposing database credentials: Store database credentials in Auth0 configuration variables, not in the script code.

  5. Forgetting about the get_user script: The get_user script is used for passwordless flows and account linking. Implement it alongside the login script.

Practice Questions

  1. What is a custom database connection? A connection that authenticates users against your own existing database instead of Auth0's managed user store.

  2. How does progressive migration work? After a successful login against the legacy database, Auth0 automatically copies the user to its managed database. On next login, the user is found in Auth0's database.

  3. What scripts are required for a custom database connection? Login (required), Get User (recommended), Verify Password, Change Password, Delete User, Create User.

  4. How should you store database credentials for custom scripts? In Auth0 configuration variables, accessible via the configuration object in scripts.

  5. Challenge: Create a complete custom database setup with a login script, get_user script, progressive migration, and a plan to decommission the legacy database.

FAQ

Does the custom database connection work with all databases?

Yes. Use any database accessible via Node.js drivers: MySQL, PostgreSQL, MongoDB, MSSQL, Oracle.

Can I test custom scripts without deploying?

Yes. Use the 'Try this script' button in the Auth0 Dashboard with test credentials.

What happens if the legacy database is down?

Users cannot log in. Implement monitoring and backup authentication strategies.

Can I keep using the custom database indefinitely?

Yes. Progressive migration is optional. You can keep users in your legacy database forever.

Does Auth0 cache the custom scripts?

Scripts are loaded on first execution and cached. Changes require redeployment.

Mini Project

Create a complete custom database migration plan for an application with 100,000 existing users: configure the connection, write login and get_user scripts, implement progressive migration, and design the legacy decommission Process.

def migration_project_plan():
    print("Migration Project Plan:")
    print()
    print("Phase 1: Setup (Week 1)")
    print("  - Create custom database connection")
    print("  - Write login script with bcrypt verification")
    print("  - Write get_user script")
    print("  - Test with development database")
    print()
    print("Phase 2: Progressive Migration (Week 2-3)")
    print("  - Deploy to production")
    print("  - Monitor migration progress")
    print("  - Handle edge cases (duplicate emails, missing profiles)")
    print()
    print("Phase 3: Decommission (Week 4)")
    print("  - Verify all active users migrated")
    print("  - Send password reset emails to inactive users")
    print("  - Archive legacy database")

migration_project_plan()

What's Next

Next: Multi-Tenant for multi-tenant architecture.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro