Migration From Stytch

Overview

This document will help you migrate users from Stytch to FusionAuth.

There are a number of different ways applications can be integrated with Stych, and it would be difficult to cover them all. This guide is a low-level, technical tutorial focusing on transferring password hashes, calling APIs, and preparing data when migrating users from a Consumer Authentication project. The steps outlined here have not been tested with the Stytch B2B SaaS Authentication project type.

This guide explains how to import passwords into FusionAuth, but does not deal with other Stytch authentication types like magic links, passkeys, passcodes, mobile biometrics, two-factor authentication, and social logins such as Google OAuth.

For an explanation of the high-level process of a migration strategy, see the migration overview article. Be aware of all laws regarding the protection and transfer of personal information in your country.

Prerequisites

If you want to import user passwords in addition to user personal details, you need a basic understanding of how password hashing and salts work. FusionAuth has a hashing article that is a good starting point.

To follow this tutorial, you need:

  • Node.js to run the migration scripts, and npm.
  • FusionAuth. The easiest way to run it locally is to use Docker with the configuration file provided later in this tutorial.

Stytch also has SDKs for Go, Java, Python, and Ruby if you prefer to convert the JavaScript scripts accompanying this tutorial to another language for your migration in production.

Planning Considerations

Obtaining User Data

You can use the Stytch API to export user data, but you cannot export password hashes via the API. To get password hashes, you will need to email Stytch support as described in the Request User Passwords From Stytch section.

Mapping User Attributes

The attributes of the User object in FusionAuth are well documented.

If there is an attribute in your user which cannot be directly mapped to a FusionAuth attribute, you can place it in the user.data field. This field can store arbitrary JSON values and will be indexed and searchable.

Social Logins

Stytch also provides integrations with other social login providers such as Twitter, Google or Facebook. Review the supported FusionAuth Identity Providers to ensure your social providers are supported.

If not supported explicitly, a provider may work with an OIDC or SAML connection. Otherwise, please open a feature request.

When migrating social logins, you may need to modify the switches of the Stytch import script. See Use the Script for more.

Migrating users with social logins such as Apple or Facebook requires that you have an existing user Id for that provider. What this unique user Id looks like depends on the particular social identity provider. The unique Id may be an email address, an integer, UUID, or a random string.

Configure the appropriate FusionAuth Identity Provider with the same values (client_id, etc) as the original user management system you are migrating away from.

Import users with the Import API, assigning each user with a social login a random password such as a UUID.

Your next step depends on whether the social login provider’s unique identifier is available as part of your migration data. If you have the social login provider’s unique identifier, for each user, use the Link API to create a link with the appropriate User Id, Identity Provider Id and Identity Provider User Id.

  • The User Id is the Id of the recently created FusionAuth User.
  • The Identity Provider Id is found on the corresponding Identity Provider API documentation. Look for identityProvider.id .
  • The Identity Provider User Id is the existing social provider user identifier exported or otherwise extracted from the original system.

You do not need to migrate the social network token, which may or may not be accessible. During the first login of a newly migrated user, FusionAuth finds the unique user in the social login provider based on the migrated Identity Provider User Id, and completes the login. During this process, FusionAuth stores a token on the Link, if the social provider returns one. Depending on the configuration of the social provider, users may see a prompt asking if they want to allow FusionAuth to have access to user data such as email address.

IdP Linking Strategies are available since version 1.28.0. Before that version, users were linked on email.

If you do not have the social login provider’s identifier, you need to decide if you want to transparently link the two accounts, which is easier for the end user, or if you want to ask the user to manually link the accounts, which is more accurate, but may be confusing.

To transparently link the accounts, choose a linking strategy of Link On Email or Link On Username, which will create the user if they don’t exist. However, if the user has an email address at their social provider which differs from the email address that was used to sign up for your application and which you imported to FusionAuth, then two accounts will be created.

For example, if the user has a Google account richard@gmail.com, but signed up for your application with richard@fusionauth.io, then if you use the Link On Email strategy, two different accounts will be created, since FusionAuth is trying to match on email address and they don’t. The same holds true for usernames with the Link on Username strategy.

To prompt the user to link the accounts, choose a linking strategy of Pending, which will prompt the end user to sign into FusionAuth after they sign into the social provider, authoritatively linking the two accounts.

Here’s more information about IdP Linking Strategies.

Other Entities

There are often other important entities, such as connections or roles, that need to be migrated. There are usually fewer of these, so an automated migration may not make sense, but plan to move this configuration somehow.

Be aware that functionality may not be the same between Stytch and FusionAuth. This is different from user data; as long as you can somehow migrate a login identifier (a username or email) and a password hash, a user will be authenticated and successfully migrated. You can download FusionAuth before you begin a migration and build a proof of concept to learn more about the differences.

A partial list of what may need to be migrated for your application to work properly includes the following:

  • In Stytch, sign-in providers are a source of data for users. FusionAuth calls these Identity Providers.

Identifiers

When creating an object with the FusionAuth API, you can specify the Id. It must be a UUID.

This works for users, applications, and tenants, among others.

If you have external dependencies on an Id stored in Stytch, port the same Id over to FusionAuth.

Exporting Users

To export users from Stytch, you need to request the user password hashes from Stytch via email, get all user details via the API, and then combine the user details with the hashes.

Create Stytch Users

You probably already have a Stytch account with users you want to export to FusionAuth. Even so, it is a good idea to create a separate test project in Stytch with only a few users. You can use the test project to run an end-to-end migration between Stytch and FusionAuth quickly, without having to handle millions of real users and the privacy risks of handling their data when encountering errors.

Create a new Stytch project:

  • Create a Stytch account or sign in to your existing account at https://stytch.com/dashboard.
  • Locate the project button next to the workspace button in the header at the top left and use the project button to create a new project you’ll use for test users.
  • Under Configuration on the sidebar, select API Keys.
  • Note the project Id and secret.

Create three example users:

  • Download this tutorial’s scripts repository from https://github.com/fusionauth/fusionauth-import-scripts and unzip it, or if you have Git, run the code below.

    git clone https://github.com/fusionauth/fusionauth-import-scripts.git
    
  • In the file fusionauth-import-scripts/stytch/js/1_makeStytchUsers.mjs, change the project Id and secret to match your project.

  • In a terminal, run the code below to create three new users in Stytch using the Stytch JavaScript API.

    cd fusionauth-import-scripts/stytch/js
    npm update
    node 1_makeStytchUsers.mjs
    
  • In the Stytch web dashboard, check that the users now exist.

  • If any errors occur and you need to delete the users, uncomment the lines with client.users.delete, set the user Ids from the dashboard, and rerun the script.

The 1_makeStytchUsers.mjs file uses the Stytch JavaScript SDK to create users with three different hashing algorithms. However, the line client.passwords.authenticate provides the cleartext password to Stytch, which Stytch uses to rehash the user password using the Scrypt hashing algorithm. At the end of this script, all your users’ passwords will be hashed with Scrypt in the Stytch database.

Even though you can create passwords for Stytch users with different hashing algorithms (bcrypt, scrypt, Argon2, MD5, SHA-1, or PBKDF2), scrypt is the Stytch-preferred algorithm. Whenever a user logs in, Stytch will verify their password hash using the algorithm it is currently stored with, and then rehash their password using scrypt and discard the old hash. Password rehashing is a common technique used to upgrade security as hashing algorithms evolve.

Once you have completed this tutorial and understand how to import hashes into FusionAuth, you might want to go back and comment out the authenticate line to test other hashing algorithms. Note that this is necessary only in the rare case that you previously imported users into Stytch from another system, and some of those users have not yet logged in again.

Request User Passwords From Stytch

You cannot download your users’ password hashes from Stytch using their API. To get the password hashes, email support@stytch.com from the email address you used to sign up with Stytch and ask for your users’ hashes to be sent to you.

Stytch will encrypt the hashes with your public key. Attach this key in a .pem file to the email you send Stytch.

To create a private and public key pair, use the commands below in a terminal.

openssl genpkey -algorithm RSA -out private_key.pem &&
openssl rsa -pubout -in private_key.pem -out public_key.pem

Email Stytch only public_key.pem. Keep private_key.pem secret and secure.

An example of what these keys look like is in the directory fusionauth-import-scripts/stytch/exampleData/1_emailRequest. You can use the keys to request test users from Stytch but do not use these keys for real users, as they are publicly available on GitHub.

If you don’t want to send Stytch an email, you can use the example response files for the rest of this tutorial. The two files Stytch emailed are in exampleData/2_emailResponse. Remember to change the sample user Ids to your actual user Ids as you proceed.

Decrypt The Reply

Stytch will reply with two files: An encrypted password hash file (stytch-project-test-36510638-652a-4d3d-9a94-f0a7106582fc-hashes-2021-01-11.enc) and the key to decrypt it (key.bin.enc).

Decrypt the password file with the commands below.

openssl pkeyutl -decrypt -inkey private_key.pem -in key.bin.enc -out key.bin &&
openssl enc -d -aes-256-cbc -in stytch-project-test-36510638-652a-4d3d-9a94-f0a7106582fc-hashes-2021-01-11.enc -out stytch_password_hashes.csv -pass file:./key.bin

Now you have all your user hashes in the file stytch_password_hashes.csv. Below is the exampleData/3_responseDecryption/stytch_password_hashes.csv file content for three users.

scrypt hash parameters,,,,
scryptCostParameter,1 << 15,,,
scryptRParameter,8,,,
scryptPParameter,1,,,
scryptKeyLengthParamenter,32,,,
,,,,
id,hash,salt,hash_method,project_id
user-test-178d8ee5-c458-4f48-a482-8fefa30a1a87,uiOC_BwbKta9R9QL6Ss6KTDpCcULh9_zhObl5j4398M=,BbV-sGQqUIX1NwE6uqtSITv4fa1iMw==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc
user-test-799ea981-b2bb-417f-afe8-4ab6e79fcd2a,A6VzdsTsTHPqafORIb0GkGD6qFxdncqwA15YXRsVgvs=,XPapDAm6xdV5UhMpyPrSpy8FbfCDtA==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc
user-test-c1be12e4-fc61-4e7a-8831-0fc14cb87b65,8dg6AaIWPfcLTQU7lb4H-CI49dHeqaBXfFE1ogb2qRQ=,zKia-0BdIFKCzWbzXbj3qrhBnbiWNg==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc

The file header describes the parameters you need to use when you run the scrypt hashing algorithm to convert user passwords to match the hashes in this file.

Below the header are the column names, and then one row per user. In the example data, all hashes were made using scrypt.

Common Problems With Hashes

Not all hash algorithms use separate salts. For instance, bcrypt will create a salt automatically when hashing a password, and store the hash and salt concatenated in one string. You may have to deal with the peculiarities of different algorithms like this if you previously migrated users into Stytch from an authentication provider that did not use scrypt.

Even when using scrypt with the given parameters, you need to carefully check that how you hash passwords has the same result as Stytch. Hashes and salts are arrays of bytes (numbers) and therefore cannot be displayed directly as text. They are instead mapped to text using a conversion process called Base64. However, there are different ways to do this mapping. Since you can see the hashes from Stytch use - and _, they must use RFC 4648 (the URL-safe standard). This can cause miscommunications with other hash libraries that use + and /.

For instance, we can use the snippet of JavaScript Node.js code below in js/2_checkHash.mjs to create a hash for the last user in the example file.

import * as crypto from 'crypto';

const password = 'averylongandunguessablepasswordwithlotsofrandominfooofisjoafasnr;,n2';
const salt = 'zKia-0BdIFKCzWbzXbj3qrhBnbiWNg==';
const hash = '8dg6AaIWPfcLTQU7lb4H-CI49dHeqaBXfFE1ogb2qRQ=';

const cost = 1 << 15;
const blockSize = 8;
const parallelization = 1;
const keyLength = 32;

crypto.scrypt(password, salt, keyLength, { N: cost, r: blockSize, p: parallelization, maxmem: 1024 * 1024 * 1024*50 }, (err, derivedKey) => {
  if (err) throw err;
  const keyBase64 = derivedKey.toString('base64'); //  .replace(/\+/g, '-').replace(/\//g, '_')
  console.log('Hash:', keyBase64);
  console.log('Matches provided hash:', keyBase64 === hash);
});

Note that our hash algorithm looks correct, but the hashes differ by one character, +, in 8dg6AaIWPfcLTQU7lb4H-CI49dHeqaBXfFE1ogb2qRQ= and 8dg6AaIWPfcLTQU7lb4H+CI49dHeqaBXfFE1ogb2qRQ=.

To have the hashes match correctly, you need to replace the + and / characters in your hash:

const keyBase64 = derivedKey.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');

JavaScript and FusionAuth use the + and / symbols. Stytch and the Java scrypt plugin use - and _. If you encounter an error when verifying hashes, try converting the hash and salt to use the other character set.

Get All User Details And Combine With Hashes

Stytch emails you only the user hash and Id. You need to use the Stytch API to retrieve all user details from Stytch, then combine those with the hash from the email, and save the user to FusionAuth.

Open your hash file from Stytch in a text editor and remove all the header lines so only one user per row remains. Save this file to fusionauth-import-scripts/stytch/js/hash.csv. The file should look like exampleData/4_hashFilePreparation/hash.csv now.

user-test-178d8ee5-c458-4f48-a482-8fefa30a1a87,uiOC_BwbKta9R9QL6Ss6KTDpCcULh9_zhObl5j4398M=,BbV-sGQqUIX1NwE6uqtSITv4fa1iMw==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc
user-test-799ea981-b2bb-417f-afe8-4ab6e79fcd2a,A6VzdsTsTHPqafORIb0GkGD6qFxdncqwA15YXRsVgvs=,XPapDAm6xdV5UhMpyPrSpy8FbfCDtA==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc
user-test-c1be12e4-fc61-4e7a-8831-0fc14cb87b65,8dg6AaIWPfcLTQU7lb4H-CI49dHeqaBXfFE1ogb2qRQ=,zKia-0BdIFKCzWbzXbj3qrhBnbiWNg==,scrypt,project-test-36510638-652a-4d3d-9a94-f0a7106582fc

Check the fourth column to ensure that only scrypt users are included. The first four columns are the most important: id, hash, salt, and hash_method.

The 3_getUserDetails.mjs script loops through each user in the hash.csv file, uses the API to get the user details from Stytch, and saves them to js/users.json.

  • Open 3_getUserDetails.mjs and set your project Id and secret.
  • Make sure the hash.csv is the same directory fusionauth-import-scripts/stytch/js where the 3_getUserDetails.mjs script is located.
  • Run the commands below in a terminal in the fusionauth-import-scripts/stytch/js directory.
    npm update
    node 3_getUserDetails.mjs
    

Open js/users.json. It should look like the sample in exampleData/5_userDetailAndHashPreparation/users.json.

[
  {
    "biometric_registrations": [],
    "created_at": "2024-01-11T09:54:59Z",
    "crypto_wallets": [],
    "emails": [
      {
        "email": "user1@example.com",
        "email_id": "email-test-8af9b269-8f2f-44a6-8322-6f2f276cba5d",
        "verified": false
      }
    ],
    "name": {
      "first_name": "A",
      "last_name": "Example",
      "middle_name": ""
    },
    "password": {
      "password_id": "password-test-80e44428-9182-4635-8a08-9c4c5ccf6c72",
      "requires_reset": false
    },
    "phone_numbers": [
      {
        "phone_id": "phone-number-test-b4c5c640-3f30-46bc-8bdf-5379ad21367b",
        "phone_number": "+272223334444",
        "verified": false
      }
    ],
    "providers": [],
    "request_id": "request-id-test-d996f3a8-ba0c-4bff-9214-2451f42bceb0",
    "status": "active",
    "status_code": 200,
    "totps": [],
    "trusted_metadata": {},
    "untrusted_metadata": {},
    "user_id": "user-test-178d8ee5-c458-4f48-a482-8fefa30a1a87",
    "webauthn_registrations": [],
    "hash": "uiOC_BwbKta9R9QL6Ss6KTDpCcULh9_zhObl5j4398M=",
    "salt": "BbV-sGQqUIX1NwE6uqtSITv4fa1iMw==",
    "hashAlgorithm": "scrypt"
  }
]

In Stytch, a user can have multiple emails and phone numbers. FusionAuth allows for only one. You will store the extras in the user data JSON field when migrating the user, along with any other Stytch fields FusionAuth does not support.

The last lines of the object show the hash and salt that the script added from the hash file to the user details from the API.

Importing Users

First install a FusionAth plugin to handle the Stytch password hash algorithm, then import the users, and finally verify the import.

Build The Scrypt Password Hash Plugin For FusionAuth

The scrypt hashing algorithm is not natively supported by FusionAuth. However, FusionAuth allows custom plugins. There is a scrypt plugin accompanying this article in this GitHub repository.

The linked repository has multiple plugin projects. You need to build and install only the hashing project.

Download and unzip the repository, or use Git in a terminal with the code below.

git clone https://github.com/fusionauth/fusionauth-contrib.git

Open the file fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/ExampleStytchScryptPasswordEncryptor.java. The content of the file is shown below.

/*
 * Copyright (c) 2020, FusionAuth, All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package com.mycompany.fusionauth.plugins;

import io.fusionauth.plugin.spi.security.PasswordEncryptor;
import com.lambdaworks.crypto.SCrypt;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class ExampleStytchScryptPasswordEncryptor implements PasswordEncryptor {

// tag::scryptParameters[]
  /* Scrypt Parameters. You can find the correct settings for your Stytch project
    in the email they sent you containing your hashes. Copy them here. */
  private static final int N_CpuCost = 1 << 15;
  private static final int R_MemoryCost_BlockSize = 8;
  private static final int P_Parallelization = 1;
  private static final int KeyLength = 32;
// end::scryptParameters[]

  @Override
  public int defaultFactor() {
    return 0;
  }

  @Override
  public String encrypt(String password, String salt, int factor) {
    try {
        Charset Charset = StandardCharsets.US_ASCII;
        String urlSafeSalt = salt.replace('+', '-').replace('/', '_'); // because Stytch exports the latter type of Base 64, and Java/FusionAuth require the former
        byte[] hashedBytes = SCrypt.scrypt(password.getBytes(Charset), urlSafeSalt.getBytes(Charset), N_CpuCost, R_MemoryCost_BlockSize, P_Parallelization, KeyLength);
        return new String(Base64.encodeBase64(hashedBytes)); // Return Base64 with + and / symbols, not - and _ symbols
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

}

This program takes a password and salt and returns a hash. It accepts and returns the + form of Base64, but changes it to - to work with Java internally. The factor parameter is ignored. Scrypt instead has multiple parameters set at the top of the file. If any of these differ from the ones you received in your encrypted CSV file header, you need to change them in the Java file.

There are two other files linked to this scrypt plugin that you shouldn’t need to alter:

  • fusionauth-contrib/Password Hashing Plugins/src/test/java/com/mycompany/fusionauth/plugins/ExampleStytchScryptPasswordEncryptorTest.java, which holds unit tests for the hasher.
  • fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/guice/MyExampleFusionAuthPluginModule.java, which makes the hasher file known to FusionAuth.

If you make another hash plugin, you will need to make your own test and add your plugin to the Guice file.

Build the Java plugin and add it to FusionAuth:

  • Open a terminal and run the code below to build a Docker container for Java, with the repository shared as a volume into the container.
    cd fusionauth-contrib/.devcontainer
    docker build -t javaimage .
    cd ..
    docker run -it --name javabox -v .:/workspace javaimage
    
  • In the Docker container terminal that is now running, run the code below.
    cd "/workspace/Password Hashing Plugins"
    mvn clean install
    exit
    
  • If you have Java installed locally, you can run the Maven command without Docker.

All tests should pass, and the plugin file should be available in Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar.

Set Up FusionAuth And Deploy The Plugin

Now copy fusionauth-example-password-encryptor-0.1.0.jar to your FusionAuth plugins directory and restart FusionAuth.

If you are not already running FusionAuth or want to test this process on another instance, you can start FusionAuth in Docker.

  • Open a new terminal in the fusionauth-import-scripts directory and run the code below.
    cd stytch/fusionAuthDockerFiles
    docker compose up
    
  • FusionAuth will now be running and browsable at http://localhost:9011. You can log in with admin@example.com and password. The container is called fa.
  • Open a terminal in the fusionauth-contrib root directory, where you built the plugin. Run the commands below to copy the JAR file into the FusionAuth container plugins directory.
    docker exec fa mkdir /usr/local/fusionauth/plugins
    docker cp "Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar" fa:/usr/local/fusionauth/plugins/fusionauth-example-password-encryptor-0.1.0.jar
    
  • Finally, restart FusionAuth for it to detect the plugin. In the terminal where FusionAuth is running in Docker, press Ctrl + C to stop it, wait, and run docker compose up again.

Save The User Details And Hash To FusionAuth

Now you have the users file users.json and the scrypt plugin is installed. To import the users into FusionAuth, you need to run the Node.js import script.

Use the Script

Open a terminal in the fusionauth-import-scripts directory and run the code below.

cd stytch/js
npm update
node 4_import.mjs

This import script needs only FusionAuth to be running locally on port 9011 and the users.json file to exist in the fusionauth-import-scripts/stytch/js directory. The FusionAuth Kickstart file already set a sample application to match the Ids in 4_import.mjs. If your FusionAuth installation is different, please edit the Ids in 4_import.js.

You might want to set the user’s registration and tenant Id in your production migration.

faUser.registrations = Array<UserRegistration>;
faUser.tenantId = UUID;

The 4_import.mjs script is the code you will need to spend the most time editing if you want to customize your own migration. Let’s look at how it works.

The script has two dependencies:

  • stream-json — A JSON library that can read massive files with millions of users incrementally. It opens users.json for reading in the line new Chain([fs.createReadStream(filename), parser(), new StreamArray(),]). For more information, read https://github.com/uhop/stream-json.
  • @fusionauth/typescript-client — The FusionAuth SDK for Node.js. It’s only used for a single operation: fa.importUsers(importRequest);. For more information, read the FusionAuth Typescript Client Library documentation.

The main program loop is the code below.

for await (const { value: stytchUser } of stytchUsers) {
  const faUser = getFaUserFromStytchUser(stytchUser);
  await importUser(faUser, stytchUser);
}

The program takes a Stytch user from the JSON file, maps it to a FusionAuth user ready for import, and sends it to FusionAuth.

The getFaUserFromStytchUser() function does a few things:

  • Maps as many matching fields from Stytch to FusionAuth as possible.
  • Takes the first verified contact it can find or the first contact as the primary FusionAuth contact and saves additional contacts in the generic data JSON field. This is because FusionAuth allows only one email and phone number while Stytch has an array.
  • Stores all Stytch user details that don’t map to FusionAuth in the data field.
  • Ignores empty fields and arrays.
  • Uses the name we registered for the hashing algorithm in the Java plugin in faUser.encryptionScheme = 'example-salted-stytch-scrypt';.

Carefully read this function and make sure that the user information you need is imported correctly. If any information is not needed, you can comment out the related lines.

The function getNullOrUUIDFromUserId() extracts a standard UUID from the Stytch format to set a user Id in FusionAuth.

Finally, this script imports users individually. If this is too slow when running the production migration, wrap the importUsers() FusionAuth SDK call in a loop that bundles users in batches of 1000.

If you are uncertain what a user attribute in FusionAuth does, read more in the user guide, as linked in the general migration guide recommended earlier.

Import Roles

Stytch does not support user roles and applications as native properties of users. Instead, you can implement roles by adding them as values in the JSON trusted_metadata field of the user. This field is similar to the FusionAuth data field.

As discussed in the general migration guide, roles in FusionAuth are stored in a Registration object (a link between a User and an Application).

If you have roles in Stytch you want to import, add them to the faUser.registrations array in the import script. This is not included in the tutorial script because every Stytch client will have their own way of linking users and applications in Stytch using JSON.

Verify the Import

If the migration script ran successfully, you should be able to log in to FusionAuth with one of the imported users:

  • Browse to http://localhost:9011.
  • Enter username user1@example.com and password averylongandunguessablepasswordwithlotsofrandominfooofisjoafasnr;,n1.
  • Logging in should work, but your user will not be able to see anything as it has no administration rights.
  • Log out again and log in with admin@example.com.
  • Browse to Users and edit one of the imported users.
  • In the Source tab, you can see all the user details.

Debug With The FusionAuth Database

If you have errors logging in, use the FusionAuth database directly to see if your users were imported, and check their hashes manually.

You can use any PostgreSQL browser. DBeaver is free, cross-platform, and open source. The connection details are in fusionauth-import-scripts/stytch/fusionAuthDockerFiles/docker-compose.yml and .env.

In DBeaver or your database IDE, create a new PostgreSQL connection with the following details:

  • URL: jdbc:postgresql://localhost:6432/fusionauth
  • Host: localhost
  • Port: 6432
  • Database: fusionauth
  • Username: fusionauth
  • Password: hkaLBM3RVnyYeYeqE3WI1w2e4Avpy0Wd5O3s3

Log in to the database and browse to Databases/fusionauth/Schemas/public/Tables/identities and users. These two tables will show you the login credentials and user personal information.

What to Do Next

You now have your users migrated, or a plan to do so. Congratulations! What is next?

You need to migrate additional configurations, as mentioned in Other Entities . Since the type of configuration varies, it is hard to provide a full list of how to import these items, but the general pattern will be:

  • Identify corresponding FusionAuth functionality.
  • Configure it in your FusionAuth instance, either manually or by scripting it using the client libraries or API.
  • Update your application configuration to use the new FusionAuth functionality.

Make sure you assign your users to the appropriate FusionAuth applications. You can do this either:

  • As part of your import process by adding registrations at import time.
  • After users have been migrated with the Registrations API.

You’ll also need to modify and test each of your applications, whether custom, open source, or commercial, to ensure:

  • Users can successfully log in.
  • The authorization code redirect is handled correctly.
  • Users receive appropriate permissions and roles based on the JWT.
  • The look and feel of the hosted login pages matches each application’s look and feel.

If your application uses a standard OAuth, SAML or OIDC library to communicate with , the transition should be relatively painless.

Additional Support

If you need support in your migration beyond that provided in this guide, you may: