Skip to main content

Modifications to Login

In this section we will be modifying login to enable the migrating of users with Passwordless accounts from Auth0 to SuperTokens.

info

The following code is implemented for Nodejs, but, you can apply the same logic in your own tech stack.

Flow#

tip

If you have not stored your user's Auth0 userId in your database you can ignore the steps mentioned in Box 6 and Box 8 of the flow diagram.

Flow of login to migrate users with passwordless accounts in Auth0 to SuperTokens

Implementation#

We will be overriding SuperToken's login functions to enable account migration

Modifying the consumeCode function on the backend:#

Nodejs
import Passwordless from "supertokens-node/recipe/passwordless";
async function getUserInfoFromAuth0(input :{    email: string | undefined,     phoneNumber: string | undefined }){    // Box 2: Get userInfo from Auth0 with phone number or email.    // refer to the next section to define this function    return {        user_id: "auth0UserId"    }}
async function setUserIdMapping(input: {    auth0UserId: string,    supertokensUserId: string}){    // refer to UserId mapping guide to see how to define this function}
Passwordless.init({    flowType: "USER_INPUT_CODE_AND_MAGIC_LINK",    contactMethod: "EMAIL_OR_PHONE",    createAndSendCustomEmail: async (input, context) => {        // Handle sending email    },    createAndSendCustomTextMessage: async (input, context) => {        // Handle sending sms    },    override: {        functions: (originalImplementation) => {            return {                ...originalImplementation,                consumeCode: async (input) => {
                    // use listCodesByPreAuthSessionId to retrieve details about the current user                    let codeResponse = await Passwordless.listCodesByPreAuthSessionId({                        preAuthSessionId: input.preAuthSessionId                    })
                    // if both email and phoneNumber are undefined throw RESTART_FLOW_ERROR                    if (codeResponse?.email === undefined && codeResponse?.phoneNumber === undefined) {
                        return {                            status: "RESTART_FLOW_ERROR"                        }                    }                                        // Box 2: retrieve the auth0 user data with the input email and phone number if they exist                    let auth0UserInfo = await getUserInfoFromAuth0({                        email: codeResponse?.email,                        phoneNumber: codeResponse?.phoneNumber                    })
                    // Box 3: check if a user with the email or phoneNumber exists in auth0                    if (auth0UserInfo !== undefined) {
                        // Box 4: call the SuperTokens consumeCode implementation                        let response = await originalImplementation.consumeCode(input);
                        if (response.status != "OK") {                            return response                        }
                        // Box 5: is the user in the response a new Supertokens user                        if (response.createdNewUser) {
                            // Box 6: map the Auth0 userId to the SuperTokens userId, to learn more about user mapping please check the User Id  Mapping section.                            // If you have not stored the users Auth0 userId in your tables, you can ignore this step                             await setUserIdMapping({ auth0UserId: auth0UserInfo.user_id, supertokensUserId: response.user.id });
                            // Box 7: Set the newly created flag value to false in the response                            response.createdNewUser = false                        }                        // Box 8: Set the userId in the response to use the Auth0 userId, to learn more about user mapping please check the User Id  Mapping section.                        // If you have not stored the users Auth0 userId, you can ignore this step                        response.user.id = auth0UserInfo.user_id
                        return response
                    } else {                        return originalImplementation.consumeCode(input)                    }                }            }        }    }})

Implementing the getUserInfoFromAuth0 function#

In order to make the changes mentioned above, you have to decide how you would like to retrieve your user's data.

There are two methods :

  • Using Auth0's APIs
  • Using the exported user data from Auth0

Using Auth0's APIs#

In this method we will be using Auth0's APIs to check if the user signing in is an Auth0 user.

Prerequisites#

  • This method assumes you have access to your Auth0 account and database for the complete duration of user migration.

Implementation#

Implementation for the getUserInfoFromAuth0 function :

Nodejs
import axios from "axios";
// generate an access_token so you can use the Auth0 management APIlet generateAccessToken = async () => {    let response = (await axios({        method: 'POST',        // TODO: add your Auth0 app domain        url: "https://YOUR_DOMAIN/oauth/token",        headers: { 'content-type': 'application/json' },        data: {            // TODO: add your Auth0 app client id and secret            client_id: "CLIENT_ID",            client_secret: "CLIENT_SECRET",            // TODO: add you Auth0 app domain            audience: "https://YOUR_DOMAIN/api/v2/",            grant_type: "client_credentials"        }    }))
    return response.data.access_token}
let access_token = generateAccessToken()
// Get the Passwordless user info from Auth0 with the input thirdPartyId.let getUserInfoFromAuth0 = async (input: {    email: string | undefined,    phoneNumber: string | undefined}) => {
    if (input.phoneNumber !== undefined) {        // send a request to Auth0's get user API with the input thirdPartyId as the search criteria.        let response = await axios({            method: 'GET',            // TODO: add your Auth0 app domain            url: "https://YOUR_DOMAIN/api/v2/users",            params: { q: `identities.provider:sms AND phone_number:${input.phoneNumber}` },            headers: { authorization: `Bearer ${access_token}` }        })
        // check if user information exists in response.        if (response.data[0] !== undefined) {            // return the user's Auth0 userInfo            return response.data[0];        }    } else if (input.email !== undefined) {        // send a request to Auth0's get user API with the input thirdPartyId as the search criteria.        let response = await axios({            method: 'GET',            // TODO: add your Auth0 app domain            url: "https://YOUR_DOMAIN/api/v2/users",            params: { q: `identities.provider:email AND email:${input.email}` },            headers: { authorization: `Bearer ${access_token}` }        })
        // check if user information exists in response.        if (response.data[0] !== undefined) {            // return the user's Auth0 userInfo            return response.data[0];        }    }
    return undefined}

We can now move on to the next section discussing the setUserIdMapping function and how userId's should be mapped.