Paid Feature
This is a paid feature.
For self hosted users, Sign up to get a license key and follow the instructions sent to you by email. Using the dev license key is free. We only start charging you once you enable the feature in production using the provided production license key.
For managed service users, you can click on the "enable paid features" button on our dashboard, and follow the steps from there on. Once enabled, this feature is free on the provided development environment.
Migration to SuperTokens from another auth solution
This section covers migration of secondary factors from your existing auth solution to SuperTokens. To see how to migrate the first factor, refer to our migration docs for the recipe you have setup as the first factor:
After you have completed the first factor migration, you can focus on the secondary factor migration for the user. We will cover migration steps for each of the support secondary factors we have.
important
Before reading further, make sure you have understood how MFA works with SuperTokens based on the docs above in this guide.
#
TOTPFrom the migration step above (for the first factor), you should have the user's user ID generated by SuperTokens. Call the following API to create a new TOTP device for the user with their user ID and extisting TOTP secret:
- Single app setup
- Multi app setup
curl --location --request POST '/recipe/totp/device/import' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"userId": "<FROM FIRST FACTOR MIGRATION>",
"skew": 1,
"period": 30,
"secretKey": "...."
}'
curl --location --request POST '/recipe/totp/device/import' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"userId": "<FROM FIRST FACTOR MIGRATION>",
"skew": 1,
"period": 30,
"secretKey": "...."
}'
The above API call will create a new TOTP device for the user. The secretKey
field should be fetched from your existing auth provider (we expect it to be in base32 format). You can also provide an optional deviceName
as a prop in the body which will help identify the TOTP device for future operations like if you want to delete this device. If you do not provide a deviceName
, SuperTokens will generate a device name for this user like TOTP Device X
, where X
is 1, 2, 3 and so on depending on how many devices this user already has.
caution
- If you have mapped the SuperTokens user ID to an external user ID, then you should use the external user ID in the above API call instead of the SuperTokens user ID.
- The
secretKey
field must bebase32
encoded. If your existing auth provider provides the key encoded in any other format, you must decode their key and then encode it in base32 format. For example, if the provided key is in base64 format, then you must dobase32encode(base64decode(secretKeyFromAuthProvider))
#
Email / SMS OTPSuperTokens relies on the passwordless recipe to complete this type of factor. The overall process here is to:
- Create a new passwordless user with the email of the first factor, or with a phone number.
- Make the first factor user a primary user for account linking purposes.
- Link the passwordless user to the first factor user.
- Single tenant / app setup
- Multi tenant / app setup
#
Generate passwordless codeWith Email
curl --location --request POST '/recipe/signinup/code' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"email": "johndoe@example.com"
}'
With Phone Number
curl --location --request POST '/recipe/signinup/code' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"phoneNumber": "+14155552671"
}'
On successfully generating the passwordless code you should see the following response
{
"status": "OK",
"preAuthSessionId": "d3Zpa9eoyV2Wr7uN5DLr6H1clzbwwGTc_0wIIXJT55M=",
"codeId": "4fe93f8e-a5da-4588-82e2-314c6993b345",
"deviceId": "+cWm1Y2EFxEPyHM7CAwYyAdkakBeoEDm6IOGT3xfa1U=",
"userInputCode": "463152",
"linkCode": "UlEb3-gbIYow61ce6RNzghkGN8qcHkpRwbhHbvMEjxY=",
"timeCreated": 1664283193059,
"codeLifetime": 900000
}
#
Consume the passwordless code to create the passwordless userRetrieve the preAuthSessionId
and linkCode
from the previous response and set them as request body parameters for the consume code request.
curl --location --request POST '/recipe/signinup/code/consume' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"preAuthSessionId": "d3Zpa9eoyV2Wr7uN5DLr6H1clzbwwGTc_0wIIXJT55M=",
"linkCode": "UlEb3-gbIYow61ce6RNzghkGN8qcHkpRwbhHbvMEjxY="
}'
If the user has both email and password associated with them, then you can call the update user API to associate the missing information
curl --location --request PUT '/recipe/user' \
--header 'api-key: ' \
--header 'rid: passwordless' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"userId": "fa7a0841-b533-4478-95533-0fde890c3483",
"email": "johndoe@gmail.com",
"phoneNumber": "+14155552671"
}'
#
Generate passwordless codeWith Email
curl --location --request POST '/recipe/signinup/code' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"email": "johndoe@example.com"
}'
With Phone Number
curl --location --request POST '/recipe/signinup/code' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"phoneNumber": "+14155552671"
}'
On successfully generating the passwordless code you should see the following response
{
"status": "OK",
"preAuthSessionId": "d3Zpa9eoyV2Wr7uN5DLr6H1clzbwwGTc_0wIIXJT55M=",
"codeId": "4fe93f8e-a5da-4588-82e2-314c6993b345",
"deviceId": "+cWm1Y2EFxEPyHM7CAwYyAdkakBeoEDm6IOGT3xfa1U=",
"userInputCode": "463152",
"linkCode": "UlEb3-gbIYow61ce6RNzghkGN8qcHkpRwbhHbvMEjxY=",
"timeCreated": 1664283193059,
"codeLifetime": 900000
}
#
Consume the passwordless code to create the passwordless userRetrieve the preAuthSessionId
and linkCode
from the previous response and set them as request body parameters for the consume code request.
curl --location --request POST '/recipe/signinup/code/consume' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"preAuthSessionId": "d3Zpa9eoyV2Wr7uN5DLr6H1clzbwwGTc_0wIIXJT55M=",
"linkCode": "UlEb3-gbIYow61ce6RNzghkGN8qcHkpRwbhHbvMEjxY="
}'
If the user has both email and password associated with them, then you can call the update user API to associate the missing information
curl --location --request PUT '/recipe/user' \
--header 'api-key: ' \
--header 'rid: passwordless' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"userId": "fa7a0841-b533-4478-95533-0fde890c3483",
"email": "johndoe@gmail.com",
"phoneNumber": "+14155552671"
}'
#
Make the first factor user a primary userThe user ID returned from the first factor migration should be made a primary user with the following API call:
- Single app setup
- Multi app setup
curl --location --request POST '/recipe/accountlinking/user/primary' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"recipeUserId": "<FROM FIRST FACTOR MIGRATION>"
}'
curl --location --request POST '/recipe/accountlinking/user/primary' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"recipeUserId": "<FROM FIRST FACTOR MIGRATION>"
}'
This will allow another recipe user to be linked to this user.
caution
If you have mapped the first factor's SuperTokens user ID to an external user ID, then you should use the external user ID in the above API call (for the recipeUserId
field) instead of the SuperTokens user ID.
#
Link the passwordless user to the first factor userThen we link the newly created passwordless user to the first factor user with the following API call:
- Single app setup
- Multi app setup
curl --location --request POST '/recipe/accountlinking/user/link' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"primaryUserId": "<FROM FIRST FACTOR MIGRATION>",
"recipeUserId": "<FROM PASSWORDLESS API CALL ABOVE>"
}'
curl --location --request POST '/recipe/accountlinking/user/link' \
--header 'api-key: ' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"primaryUserId": "<FROM FIRST FACTOR MIGRATION>",
"recipeUserId": "<FROM PASSWORDLESS API CALL ABOVE>"
}'
caution
If you have mapped the first factor's SuperTokens user ID to an external user ID, then you should use the external user ID in the above API call (for the primaryUserId
field) instead of the SuperTokens user ID.