Migrating Users from Supabase

Migrating your users from one auth source to another can seem scary. Fortunately, we're here to help! See our Migrating Users to PropelAuth guide for more general information or stay here for an advanced guide, including code snippets, on how to migrate your users from Supabase to PropelAuth.

Exporting Users from Supabase

You can easily export a CSV of your user data and password hashes from Supabase by using Supabase's SQL Editor. To do so, navigate to your Supabase instance and click SQL Editor on the left navbar.

Next, type the following into the SQL Editor, replacing auth.users with your user table and adding additional fields to export if you'd like.

-- Also include first_name, last_name, etc.
SELECT id, email, encrypted_password
FROM auth.users;

Make sure to select Limit results to: No Limit in the bottom right. Then, select Export followed by Download CSV.

supabase export

Importing Users into PropelAuth

We can then use the CSV to import your user data to PropelAuth. PropelAuth offers an API endpoint to make migrating users as simple as possible. Before we run the script, let's install PropelAuth's Python library:

pip install propelauth_py

This script will attempt to import each of the users found in your CSV file into PropelAuth. If any of the imports fail, it will automatically generate a ./unmigrated_users.csv file for you.

To run the script we need to get your PROPELAUTH_AUTH_URL and PROPELAUTH_AUTH_API_KEY, both of which can be found in your Backend Integration page in PropelAuth.

from propelauth_py import init_base_auth
import csv

# Replace with your PropelAuth Auth URL and API key
PROPELAUTH_AUTH_URL = "https://auth.yourdomain.com"
PROPELAUTH_AUTH_API_KEY = "b1988511..."

# replace with your CSV path
user_csv_filename = "./user_data.csv"

# If we fail to migrate any users, we'll output them here
unmigrated_users_filename = "./unmigrated_users.csv"

auth = init_base_auth(PROPELAUTH_AUTH_URL, PROPELAUTH_AUTH_API_KEY)

def create_propelauth_user(user_data):
    auth.migrate_user_from_external_source(
        email = user_data.get('email'),
        email_confirmed = user_data.get('email_confirmed'),
        existing_user_id = user_data.get('id'),
        existing_password_hash = user_data.get('password_hash'),
        ask_user_to_update_password_on_login = False,
        enabled = True,
        username = user_data.get('username'),
        first_name = user_data.get('first_name'),
        last_name = user_data.get('last_name'),
        properties = user_data.get('properties'),
    )
    print(f"API call successful for user: {user_data.get('id')}")

def process_users():
    users_with_errors = []
    
    with open(user_csv_filename, "r", newline='', encoding='utf-8') as user_csv_file:
        csv_reader = csv.DictReader(user_csv_file)
        
        # Iterate through each user row
        for row in csv_reader:
            try:
                user_migrate_data = {
                    'id': row.get('id'),
                    'email': row.get('email'),
                    'email_confirmed': True,  # Assuming emails are confirmed, adjust as needed
                    'password_hash': row.get('encrypted_password'),
                    
                    # Fields below this are dependent on your setup, you may need to modify
                    #'username': row.get('username'),
                    #'first_name': row.get('first_name'),
                    #'last_name': row.get('last_name'),
                    # Any custom user properties that aren't first_name, last_name, or username go here
                    #'properties': {
                    #    'sample_custom_property': row['sample']
                    #}
                }                
                create_propelauth_user(user_migrate_data)
            except Exception as e:
                users_with_errors.append(dict(row))
                print(f"Error during processing user: {row.get('id', 'Unknown ID')}")
                print(f"Error: {e}")
    
    if len(users_with_errors) > 0:
        with open(unmigrated_users_filename, "w", newline='', encoding='utf-8') as unmigrated_users_file:
            if users_with_errors:  # Only proceed if there are errors
                fieldnames = users_with_errors[0].keys()  # Get column names from first error
                csv_writer = csv.DictWriter(unmigrated_users_file, fieldnames=fieldnames)
                csv_writer.writeheader()
                csv_writer.writerows(users_with_errors)
        print(f"We couldn't migrate all users - the users we failed to migrate are here {unmigrated_users_filename}")

# Run the script
if __name__ == "__main__":
    process_users()

Need a do over? Here's a script to quickly delete all of your users so you can try again.

import requests
from propelauth_py import init_base_auth

# Replace with your PropelAuth Auth URL and API key
PROPELAUTH_AUTH_URL = "https://auth.yourdomain.com"
PROPELAUTH_AUTH_API_KEY = "b1988511..."

auth = init_base_auth(PROPELAUTH_AUTH_URL, PROPELAUTH_AUTH_API_KEY)

def get_users():
    try:
        response = auth.fetch_users_by_query(
            page_size = 10,
            page_number = 0
        )
        return response
    except requests.exceptions.RequestException as e:
        print(f"Error making API call to retrieve users")
        print(f"Error: {e}")

def delete_user(user_id):
    try:
        auth.delete_user(user_id)
    except requests.exceptions.RequestException as e:
        print(f"Error making API call to delete user {user_id}")
        print(f"Error: {e}")

def delete_users():
    user_response = get_users()
    while len(user_response['users']) > 0:
        for user in user_response['users']:
            delete_user(user['user_id'])
        user_response = get_users()

# Run the script
if __name__ == "__main__"
    delete_users()

All of your users are now imported! If you run into any problems, do not hesitate to reach out to support@propelauth.com.