Skip to content

Commit b747669

Browse files
authored
Merge pull request #1044 from supertokens/improv/migration-instructions
Fix instructions
2 parents 6c3b859 + 4fc5274 commit b747669

File tree

1 file changed

+122
-91
lines changed

1 file changed

+122
-91
lines changed

docs/migration/account-migration.mdx

Lines changed: 122 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -61,84 +61,91 @@ Call the endpoint from the authentication flow used by your legacy provider.
6161

6262

6363
:::warning no-title
64-
Auth0 does not export password hashes by default. You will have to contact their support and request them.
64+
Auth0 does not expose password hashes or `TOTP` device information.
65+
You will have to contact their support separately if you need this type of data.
6566
:::
6667

6768
##### 1. Access the Auth0 Dashboard
6869
##### 2. From the navigation menu go to *Actions* > *Library*
6970
##### 3. Click *Create Action* > *Create custom action*
70-
##### 4. Specify a custom name for your action and then select **Login/Post Login** as the trigger
71+
##### 4. Specify a custom name for your action and then select *Login/Post Login* as the trigger
7172
##### 5. After the action has been created, click the *Add Secret* button and save your `SUPERTOKENS_CORE_API_KEY` as a secret
7273
##### 5. Paste the following code in the editor
7374

7475
```typescript
7576
exports.onExecutePostLogin = async (event, api) => {
7677
const SUPERTOKENS_CORE_URL = "<YOUR_SUPERTOKENS_CORE_URL>";
7778
const SUPERTOKENS_API_KEY = event.secrets.SUPERTOKENS_API_KEY;
79+
const auth0User = event.user;
7880

7981
try {
80-
// Check if user already migrated
81-
if (event.user.app_metadata?.migrated_to_supertokens) {
82-
console.log(`User ${event.user.user_id} already migrated`);
82+
if (auth0User.app_metadata?.migrated_to_supertokens) {
83+
console.log(`User ${auth0User.user_id} already migrated`);
8384
return;
8485
}
8586

86-
const connectionStrategy = event.connection.strategy;
87-
8887
const userPayload = {
89-
externalUserId: event.user.user_id,
88+
externalUserId: auth0User.user_id,
9089
userMetadata: {
91-
auth0_user_id: event.user.user_id,
92-
auth0_connection: event.connection.name,
93-
name: event.user.name,
94-
nickname: event.user.nickname,
95-
picture: event.user.picture
90+
auth0_user_id: auth0User.user_id,
91+
name: auth0User.name,
92+
nickname: auth0User.nickname,
93+
picture: auth0User.picture
94+
auth0_user_metadata: auth0User.user_metadata,
95+
auth0_app_metadata: auth0User.app_metadata
9696
},
97+
roles: auth0User.app_metadata?.roles || [],
9798
loginMethods: []
9899
};
100+
101+
const ThirdPartyProviders = ['google-oauth2', 'facebook', 'github', 'apple'];
99102

100-
// Handle different login methods
101-
if (connectionStrategy === 'google-oauth2' || connectionStrategy === 'facebook' ||
102-
connectionStrategy === 'github' || connectionStrategy === 'apple' ||
103-
connectionStrategy.includes('oauth')) {
104-
const provider = mapProvider(connectionStrategy);
105-
const thirdPartyUserId = event.user.user_id.split('|')[1];
106-
107-
userPayload.loginMethods.push({
108-
recipeId: "thirdparty",
109-
thirdPartyId: provider,
110-
thirdPartyUserId: thirdPartyUserId,
111-
email: event.user.email,
112-
isVerified: event.user.email_verified || false,
113-
isPrimary: true,
114-
timeJoinedInMSSinceEpoch: new Date(event.user.created_at).getTime()
115-
});
116-
117-
} else if (connectionStrategy === 'auth0' || connectionStrategy === 'Username-Password-Authentication') {
118-
// Auth0 does not export passworded hashes by default
119-
// You will have to contact their support and request them
120-
userPayload.loginMethods.push({
121-
recipeId: "emailpassword",
122-
email: event.user.email,
123-
passwordHash: getPasswordHash(event.user.email),
124-
hashingAlgorithm: "bcrypt",
125-
isVerified: event.user.email_verified || false,
126-
isPrimary: true,
127-
timeJoinedInMSSinceEpoch: new Date(event.user.created_at).getTime()
128-
});
129-
} else if (connectionStrategy === 'sms' || connectionStrategy === 'email') {
130-
const isEmail = connectionStrategy === 'email';
131-
userPayload.loginMethods.push({
132-
recipeId: "passwordless",
133-
email: isEmail ? event.user.email : undefined,
134-
phoneNumber: !isEmail ? event.user.phone_number : undefined,
135-
isVerified: true,
136-
isPrimary: true,
137-
timeJoinedInMSSinceEpoch: new Date(event.user.created_at).getTime()
138-
});
139-
}
103+
auth0User.identities.forEach((identity, index) => {
104+
if(ThirdPartyProviders.includes(identity.provider)) {
105+
userPayload.loginMethods.push({
106+
recipeId: "thirdparty",
107+
thirdPartyId: mapProvider(identity.provider),
108+
thirdPartyUserId: identity.user_id,
109+
email: identity.profileData?.email ?? auth0User.email,
110+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
111+
isPrimary: index === 0,
112+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
113+
});
114+
}
115+
} else if (identity.provider === 'auth0' || identity.provider === 'Username-Password-Authentication') {
116+
// Auth0 does not export passworded hashes by default
117+
// You will have to contact their support and request them
118+
userPayload.loginMethods.push({
119+
recipeId: "emailpassword",
120+
email: identity.profileData?.email ?? auth0User.email,
121+
// Request the password hash from Auth0 and then implement the function to retrieve the values
122+
passwordHash: getPasswordHash(identity.profileData?.email),
123+
hashingAlgorithm: "bcrypt",
124+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
125+
isPrimary: index === 0,
126+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
127+
});
128+
} else if (identity.provider === 'sms') {
129+
userPayload.loginMethods.push({
130+
recipeId: "passwordless",
131+
phoneNumber: identity.profileData?.phone_number ?? auth0User.phone_number,
132+
isVerified: identity.profileData?.phone_verified ?? auth0User.phone_verified ?? false,
133+
isPrimary: index === 0,
134+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
135+
});
136+
} else if (identity.provider === 'email') {
137+
userPayload.loginMethods.push({
138+
recipeId: "passwordless",
139+
email: identity.profileData?.email || auth0User.email,
140+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
141+
isPrimary: index === 0,
142+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
143+
});
144+
} else {
145+
throw new Error(`Uknown provider: ${identity.provider}`);
146+
}
147+
});
140148

141-
// Import user to SuperTokens
142149
const response = await fetch(`${SUPERTOKENS_CORE_URL}/bulk-import/import`, {
143150
method: 'POST',
144151
headers: {
@@ -169,8 +176,6 @@ function mapProvider(strategy) {
169176
'facebook': 'facebook',
170177
'github': 'github',
171178
'apple': 'apple',
172-
'windowslive': 'microsoft',
173-
'linkedin': 'linkedin'
174179
};
175180
return mapping[strategy] || strategy;
176181
}
@@ -484,59 +489,85 @@ cat auth0_users.json | jq -s '.' > auth0_users_array.json
484489

485490
#### 6. Transform the data to the SuperTokens format
486491

492+
:::warning no-title
493+
Auth0 does not expose password hashes or `TOTP` device information.
494+
You will have to contact their support separately if you need this type of data.
495+
:::
496+
487497
```typescript
488498
const fs = require('fs');
489499

490500
const auth0Users = JSON.parse(fs.readFileSync('auth0_users_array.json', 'utf8'));
491501

492502
const superTokensUsers = auth0Users.map(auth0User => {
493-
const loginMethods = [];
503+
if(auth0User.user_metadata?.migrated_to_supertokens) {
504+
console.log(`User ${auth0User.user_id} already migrated`);
505+
return;
506+
}
494507

495-
auth0User.identities.forEach(identity => {
496-
if (identity.provider === 'auth0') {
508+
const userPayload = {
509+
externalUserId: event.user.user_id,
510+
userMetadata: {
511+
auth0_user_id: auth0User.user_id,
512+
name: auth0User.name,
513+
nickname: auth0User.nickname,
514+
picture: auth0User.picture,
515+
auth0_user_metadata: auth0User.user_metadata
516+
auth0_app_metadata: auth0User.app_metadata
517+
},
518+
roles: auth0User.app_metadata?.roles || [],
519+
loginMethods: [],
520+
};
521+
522+
const ThirdPartyProviders = ['google-oauth2', 'facebook', 'github', 'apple'];
523+
524+
auth0User.identities.forEach((identity, index) => {
525+
if(ThirdPartyProviders.includes(identity.provider)) {
526+
userPayload.loginMethods.push({
527+
recipeId: "thirdparty",
528+
thirdPartyId: mapProvider(identity.provider),
529+
thirdPartyUserId: identity.user_id,
530+
email: identity.profileData?.email ?? auth0User.email,
531+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
532+
isPrimary: index === 0,
533+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
534+
});
535+
}
536+
} else if (identity.provider === 'auth0' || identity.provider === 'Username-Password-Authentication') {
497537
// Auth0 does not export passworded hashes by default
498538
// You will have to contact their support and request them
499-
loginMethods.push({
539+
userPayload.loginMethods.push({
500540
recipeId: "emailpassword",
501-
email: auth0User.email,
502-
passwordHash: getPasswordHash(auth0User.email),
541+
email: identity.profileData?.email ?? auth0User.email,
542+
// Request the password hash from Auth0 and then implement the function to retrieve the values
543+
passwordHash: getPasswordHash(identity.profileData?.email),
503544
hashingAlgorithm: "bcrypt",
504-
isVerified: auth0User.email_verified || false,
505-
isPrimary: true,
506-
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
507-
});
508-
} else if (['google-oauth2', 'facebook', 'github', 'apple'].includes(identity.provider)) {
509-
loginMethods.push({
510-
recipeId: "thirdparty",
511-
thirdPartyId: mapProvider(identity.provider),
512-
thirdPartyUserId: identity.user_id,
513-
email: auth0User.email,
514-
isVerified: auth0User.email_verified || false,
515-
isPrimary: true,
545+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
546+
isPrimary: index === 0,
516547
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
517548
});
518549
} else if (identity.provider === 'sms') {
519-
loginMethods.push({
520-
recipeId: "passwordless",
521-
phoneNumber: auth0User.phone_number,
522-
isVerified: auth0User.phone_verified || false,
523-
isPrimary: true,
524-
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
550+
userPayload.loginMethods.push({
551+
recipeId: "passwordless",
552+
phoneNumber: identity.profileData?.phone_number || auth0User.phone_number,
553+
isVerified: identity.profileData?.phone_verified ?? auth0User.phone_verified ?? false,
554+
isPrimary: index === 0,
555+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
525556
});
557+
} else if (identity.provider === 'email') {
558+
userPayload.loginMethods.push({
559+
recipeId: "passwordless",
560+
email: identity.profileData?.email || auth0User.email,
561+
isVerified: identity.profileData?.email_verified ?? auth0User.email_verified ?? false,
562+
isPrimary: index === 0,
563+
timeJoinedInMSSinceEpoch: new Date(auth0User.created_at).getTime()
564+
});
565+
} else {
566+
throw new Error(`Uknown provider: ${identity.provider}`);
526567
}
527568
});
528569

529-
return {
530-
externalUserId: auth0User.user_id,
531-
userMetadata: {
532-
auth0_user_id: auth0User.user_id,
533-
name: auth0User.name,
534-
nickname: auth0User.nickname,
535-
picture: auth0User.picture,
536-
...auth0User.user_metadata
537-
},
538-
loginMethods: loginMethods
539-
};
570+
return userPayload;
540571
});
541572

542573
fs.writeFileSync('supertokens_users.json', JSON.stringify({ users: superTokensUsers }, null, 2));

0 commit comments

Comments
 (0)