If you are currently using Firebase Authentication, this guide will help you migrate to Nile Auth. Migrating production applications from one auth provider to another is a non-trivial task, and we recommend you do thorough testing.

Before you begin, you’ll need a Nile account. If you don’t have one, you can sign up here.

Migration Steps

1

Export User Data from Auth0

This step is necessary if you use Firebase’s email/password or passwordless login methods. In this case, your users’ data is stored in Firebase, and you will need to export it.

Firebase has a friendly CLI tool for exporting user data, but unfortunately, it does not contain the organizations that users belong to.

Therefore, we need to use the Firebase Admin SDK to first list all the organizations, and then for each organization, list all the users.

This requires installing and configuring the Firebase Admin SDK. Follow the Firebase Admin SDK setup guide for more details.

Here is an example script that exports the tenants and users:

import * as admin from 'firebase-admin';
import * as fs from 'fs';

async function listAllTenants(nextPageToken, allTenants = []) {
  const result = await admin.auth().tenantManager().listTenants(100, nextPageToken);
  
  allTenants.push(...result.tenants.map(tenant => tenant.toJSON()));
  
  if (result.pageToken) {
    return await listAllTenants(result.pageToken, allTenants);
  }
  
  return allTenants;
}

async function listAllUsers(tenantId, nextPageToken, allUsers = []) {
  const tenantAuth = admin.auth().tenantManager().authForTenant(tenantId);
  
  try {
    const listUsersResult = await tenantAuth.listUsers(1000, nextPageToken);
    
    // Add users with their tenant ID
    allUsers.push(...listUsersResult.users.map(user => ({
      ...user.toJSON(),
      tenantId: tenantId
    })));
    
    if (listUsersResult.pageToken) {
      return await listAllUsers(tenantId, listUsersResult.pageToken, allUsers);
    }
    
    return allUsers;
  } catch (error) {
    console.error(`Error listing users for tenant ${tenantId}:`, error);
    return allUsers;
  }
}

async function exportAllTenantsAndUsers() {
  try {
    // Get all tenants first
    const tenants = await listAllTenants();
    const allUsers = [];

    // For each tenant, get all users
    for (const tenant of tenants) {
      console.log(`Fetching users for tenant: ${tenant.tenantId}`);
      const tenantUsers = await listAllUsers(tenant.tenantId, null);
      allUsers.push(...tenantUsers);
    }

    // Write tenants and users to separate files
    fs.writeFileSync('firebase-tenants.json', JSON.stringify(tenants, null, 2));
    fs.writeFileSync('firebase-users.json', JSON.stringify(allUsers, null, 2));
    
    console.log('Export completed! Data written to firebase-tenants.json and firebase-users.json');
  } catch (error) {
    console.error('Export failed:', error);
  }
}

// Start the export
exportAllTenantsAndUsers();

Firebase supports exporting password hashes, but note that you can only load them into Nile Auth if you are using bcrypt. To determine the password hash parameters used for your project navigate to the Authentication > Users section of the Firebase console and click the three dots icon above the list of users.

2

Import User Data into Nile Auth

You’ll need a small script to import the user data into Nile Auth. The script will iterate over the exported user data and create users in Nile Auth.

The following example script shows how to import user data from a JSON file:

import { Nile } from "@niledatabase/server";
import * as fs from 'fs';

interface FirebaseUser {
    tenantId: string;
    uid: string;
    email: string;
    emailVerified: boolean;
    disabled: boolean;
    metadata: {
        lastSignInTime: string;
        lastSignInIp: string;
        lastLoginAt: string;
        lastLoginIp: string;
    };
    password: string;
    "Updated At": string;
}

interface FirebaseOrganization {
    id: string;
    name: string;
    display_name: string;
    metadata?: Record<string, any>;
}

async function migrateUsers(nile: any) {
    try {
        // Read and parse the JSON file
        const rawData = fs.readFileSync('firebase-users.json', 'utf8');
        const users: FirebaseUser[] = JSON.parse(rawData);
        let newUser;

        // Process each user
        for (const user of users) {
            try {
                // Generate a temporary password (you might want to adjust this strategy)
                const tempPassword = `temp-${Math.random().toString(36).slice(-8)}`;

                // Get the tenant id from the organization name
                const tenant = await nile.db.query(`SELECT id FROM tenants WHERE name = $1`, 
                    [user.tenantId]);

                if (!tenant.rows[0]) {
                    console.error(`Tenant ${user.tenantId} not found`);
                    continue;
                }
                nile.tenant_id = tenant.rows[0].id; // Set the tenant id so the user is created in the correct tenant
                newUser = await nile.api.users.createUser({
                    email: user.email,
                    password: tempPassword,
                    preferredName: user.Nickname || user.Name,
                });

                // uncomment this to update the password hash in the credentials table
                // only do this if you exported the password hashes in bcrypt format
                // await nile.db.query(
                //   `UPDATE auth.credentials SET payload = jsonb_build_object('crypt', 'crypt-bf/8', 'hash', $1) 
                //    WHERE user_id = $2 AND method = 'PASSWORD';`,
                //    [user.Password, newUser.Id]
                // )

                console.log(`Successfully migrated user: ${user.email}`);
                // You might want to store the temporary passwords to communicate them to users
                console.log(`Temporary password for ${user.email}: ${tempPassword}`);
            } catch (error) {
                console.error(`Failed to migrate user ${user.email}:`, error);
                // Continue with next user even if one fails
                continue;
            }
        }
        console.log('Migration completed');
    } catch (error) {
        console.error('Migration failed:', error);
    }
}

// This is necessary only if the organizations don't already exist in Nile
async function migrateTenants(nile: any) {
    try {
        // Read and parse the organizations JSON file
        const rawData = fs.readFileSync('firebase-tenants.json', 'utf8');
        const organizations: FirebaseOrganization[] = JSON.parse(rawData);

        // Process each organization
        for (const org of organizations) {
            try {
                const tenant = await nile.api.tenants.createTenant({
                    name: org.name,
                });

                console.log(`Successfully created tenant: ${org.name}`);
            } catch (error) {
                console.error(`Failed to create tenant ${org.name}:`, error);
                // Continue with next organization even if one fails
                continue;
            }
        }

        console.log('Tenant migration completed');
    } catch (error) {
        console.error('Tenant migration failed:', error);
    }
}

// Single initialization of Nile
async function migrateAll() {
    const nile = await Nile({});
    await migrateTenants(nile);
    await migrateUsers(nile);
}

migrateAll();

If you did not have the password hashes imported to Nile, your users will need to reset their passwords after the migration (unless you used passwordless authentication) - you can read how to do this in our password reset docs.

For staged migration, or for edge cases, you can implement the fallback strategy in your application. Attempt authentication with Nile Auth, and in the failure handler, attempt the Auth0 authentication. If Auth0 authentication succeeds, you can call nile.api.users.createUser to create the user in Nile Auth.

3

Migrate Social Providers

If you used social identity providers with , you will need to configure the same providers in Nile Auth.

You can configure social providers by going to “Tenants and Users” page in Nile Console and selecting the “Providers” tab.

Simply click on the provider you want to configure and you’ll see the provider configuration page. All providers use OAuth, so you’ll need to provide the OAuth client ID and secret. It is recommended to use the same client ID and secret as the one used in . And to use the same redirect URI in the social provider configuration.

You can see more details on how to configure and use each provider in the Single Sign-On section of the documentation.

Testing

We recommend you do thorough testing in a staging environment before migrating to production.

Before starting a migration, both in staging and in production, we recommend you create a few test users and organizations in your existing auth provider. Make sure you have a mix of email/password, passwordless and social providers users.

Then, after migrating to Nile Auth, test the following (both in test and in production):

  • Sign up and sign in with email/password, passwordless and social providers.
  • Sign in with email/password, passwordless and social providers.
  • Sign out
  • Reset password
  • Email verification
  • Validate that the users have access to the correct organizations.

It is also important to test negative flows. For example, trying to sign in with an invalid password, or an invalid email/password combination. You want to be sure that unauthorized users are not able to sign in.

Finally, use the “Tenant and Users” page in Nile Console to verify that the all the expected user data and metadata was migrated, and that the users have access to the correct organizations.

Best Practices

Here are some recommended practices for a smooth migration from Auth0 to Nile Auth:

  1. Staged Migration: Consider migrating users in batches rather than all at once. Start with a small subset of non-critical users or test accounts. Monitor for issues and gather feedback before proceeding with larger groups. This will allow you to test the migration and make sure it works as expected before migrating all users. The “fallback strategy” described in the migration steps can be used to support both Auth0 and Nile Auth during the migration.

  2. Communication Plan: Notify users well in advance of the migration. Provide clear instructions for any actions they n eed to take (like password resets). Set up support channels for users who encounter issues during the transition.

  3. Backup and Rollback Plan: Keep backups of all Auth0 data before starting the migration. Maintain the Auth0 configuration until the migration is complete and verified. Have a clear rollback plan in case of critical issues.

  4. Monitoring and Validation: Set up monitoring for authentication failures during and after migration. Implement logging to track successful and failed migrations. Validate user counts and access permissions after migration.

Was this page helpful?