@supertokens-plugins/rownd-nodejs 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/dist/bulkMigrate.js +24 -8
- package/dist/generateAppConfig.js +2 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +106 -53
- package/dist/index.mjs +106 -53
- package/dist/initConfig.js +2 -0
- package/dist/setupCoreInstance.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,6 +116,7 @@ import { createMagicLinkWithConfirmationBypass } from "@supertokens-plugins/rown
|
|
|
116
116
|
|
|
117
117
|
const magicLink = await createMagicLinkWithConfirmationBypass({
|
|
118
118
|
email: "user@example.com",
|
|
119
|
+
tenantId: "tenant-a",
|
|
119
120
|
clientDomain: "browser",
|
|
120
121
|
redirectToPath: "/profile",
|
|
121
122
|
displayContext: "browser",
|
|
@@ -126,7 +127,7 @@ const magicLink = await createMagicLinkWithConfirmationBypass({
|
|
|
126
127
|
|
|
127
128
|
`clientDomain` must be a configured `clientDomains` key, not a raw domain. Omit it to use the SuperTokens website domain.
|
|
128
129
|
|
|
129
|
-
Pass exactly one of `email` or `phoneNumber`. The helper returns the rewritten magic link with `bypassDeviceConfirmation=true`.
|
|
130
|
+
Pass exactly one of `email` or `phoneNumber`. `tenantId` defaults to `public`. The helper returns the rewritten magic link with `bypassDeviceConfirmation=true`.
|
|
130
131
|
|
|
131
132
|
Before skipping the cross-device confirmation prompt, the frontend should validate the callback against the plugin. Routes are mounted under your SuperTokens `apiBasePath`, which defaults to `/auth`.
|
|
132
133
|
|
|
@@ -141,16 +142,25 @@ If validation fails, the frontend should show the normal cross-device confirmati
|
|
|
141
142
|
|
|
142
143
|
Routes are mounted under your SuperTokens `apiBasePath`, which defaults to `/auth`. The migration endpoint is the main Rownd-to-SuperTokens session handoff endpoint; the plugin also exposes Rownd-compatible app config, guest, user, metadata, field, and sign-out endpoints under `{apiBasePath}/plugin/rownd/...`.
|
|
143
144
|
|
|
145
|
+
Unauthenticated migration and guest routes accept an optional `tenantId` query parameter. It defaults to `public`. SuperTokens Core validates the tenant when the operation runs. Authenticated identity-field and sign-out operations use the tenant from the current session; custom Rownd metadata remains global to the SuperTokens user.
|
|
146
|
+
|
|
144
147
|
> [!IMPORTANT]
|
|
145
|
-
> The plugin always migrates users and sessions into the `public` tenant.
|
|
146
148
|
> Rownd users with multiple supported login methods are rejected unless SuperTokens account linking is enabled in the target environment.
|
|
147
149
|
|
|
148
150
|
### Migrate
|
|
149
151
|
|
|
150
152
|
- **POST** `{apiBasePath}/plugin/rownd/migrate`
|
|
151
153
|
- **Default**: `POST /auth/plugin/rownd/migrate`
|
|
154
|
+
- **Non-public tenant**: `POST /auth/plugin/rownd/migrate?tenantId=tenant-a`
|
|
152
155
|
- **Headers**: `Authorization: Bearer <Rownd_JWT>`. Header-token clients should also send `rid: session`, `fdi-version: 1.18`, and `st-auth-mode: header`.
|
|
153
|
-
- **Description**: Validates the Rownd JWT,
|
|
156
|
+
- **Description**: Validates the Rownd JWT, imports new users with their Rownd profile data, ensures the selected login method is associated with the requested SuperTokens tenant, and then creates a new SuperTokens session in that tenant. Header-token clients must receive `st-access-token`, `st-refresh-token`, and `front-token` response headers.
|
|
157
|
+
|
|
158
|
+
### Guest
|
|
159
|
+
|
|
160
|
+
- **POST** `{apiBasePath}/plugin/rownd/guest`
|
|
161
|
+
- **Default**: `POST /auth/plugin/rownd/guest`
|
|
162
|
+
- **Non-public tenant**: `POST /auth/plugin/rownd/guest?tenantId=tenant-a`
|
|
163
|
+
- **Description**: Creates a guest or instant user and session in the requested tenant.
|
|
154
164
|
|
|
155
165
|
## Debug Logging
|
|
156
166
|
|
|
@@ -228,6 +238,8 @@ RowndMigrationPlugin.init({
|
|
|
228
238
|
|
|
229
239
|
The package includes a bulk migration script for importing Rownd users into SuperTokens.
|
|
230
240
|
|
|
241
|
+
Set `supertokens.tenantId` in the generated configuration to associate every imported login method with a non-public tenant. It defaults to `public` when omitted. Resuming from a checkpoint with a different tenant is rejected.
|
|
242
|
+
|
|
231
243
|
The script runs from a YAML config file generated from the included template.
|
|
232
244
|
|
|
233
245
|
### Usage
|
package/dist/bulkMigrate.js
CHANGED
|
@@ -64,7 +64,7 @@ function buildSuperTokensFakeEmail(thirdPartyUserId, thirdPartyId) {
|
|
|
64
64
|
function isIdentityField(field) {
|
|
65
65
|
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
66
66
|
}
|
|
67
|
-
function mapRowndUserToSuperTokens(rowndUser) {
|
|
67
|
+
function mapRowndUserToSuperTokens(rowndUser, tenantId) {
|
|
68
68
|
const loginMethods = [];
|
|
69
69
|
const rowndUserData = rowndUser.data || {};
|
|
70
70
|
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
@@ -78,7 +78,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
78
78
|
thirdPartyId: "google",
|
|
79
79
|
thirdPartyUserId: rowndUserData.google_id,
|
|
80
80
|
email: googleEmail,
|
|
81
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
81
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id,
|
|
82
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
if (rowndUserData.apple_id) {
|
|
@@ -88,21 +89,24 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
88
89
|
thirdPartyId: "apple",
|
|
89
90
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
90
91
|
email: appleEmail,
|
|
91
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
92
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id,
|
|
93
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
92
94
|
});
|
|
93
95
|
}
|
|
94
96
|
if (rowndUserData.phone_number) {
|
|
95
97
|
loginMethods.push({
|
|
96
98
|
recipeId: "passwordless",
|
|
97
99
|
phoneNumber: rowndUserData.phone_number,
|
|
98
|
-
isVerified: !!rowndUserVerifiedData.phone_number
|
|
100
|
+
isVerified: !!rowndUserVerifiedData.phone_number,
|
|
101
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
102
105
|
loginMethods.push({
|
|
103
106
|
recipeId: "passwordless",
|
|
104
107
|
email: rowndUserData.email,
|
|
105
|
-
isVerified: !!rowndUserVerifiedData.email
|
|
108
|
+
isVerified: !!rowndUserVerifiedData.email,
|
|
109
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
106
110
|
});
|
|
107
111
|
}
|
|
108
112
|
let authLevel = rowndUser.auth_level;
|
|
@@ -114,7 +118,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
114
118
|
thirdPartyId,
|
|
115
119
|
thirdPartyUserId: rowndUserData.user_id,
|
|
116
120
|
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
117
|
-
isVerified: false
|
|
121
|
+
isVerified: false,
|
|
122
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
118
123
|
});
|
|
119
124
|
}
|
|
120
125
|
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
@@ -168,7 +173,8 @@ var ConfigSchema = import_zod.z.object({
|
|
|
168
173
|
supertokens: import_zod.z.object({
|
|
169
174
|
connectionURI: import_zod.z.string(),
|
|
170
175
|
apiKey: import_zod.z.string().optional(),
|
|
171
|
-
batchSize: import_zod.z.number().int().positive()
|
|
176
|
+
batchSize: import_zod.z.number().int().positive(),
|
|
177
|
+
tenantId: import_zod.z.string().min(1).default("public")
|
|
172
178
|
}),
|
|
173
179
|
thirdPartyProviders: import_zod.z.array(
|
|
174
180
|
import_zod.z.object({
|
|
@@ -366,6 +372,7 @@ Options:
|
|
|
366
372
|
var CheckpointSchema = import_zod2.z.object({
|
|
367
373
|
cursor: import_zod2.z.string().optional(),
|
|
368
374
|
importedCount: import_zod2.z.number().int().nonnegative().optional(),
|
|
375
|
+
tenantId: import_zod2.z.string().optional(),
|
|
369
376
|
updatedAt: import_zod2.z.string()
|
|
370
377
|
});
|
|
371
378
|
async function loadCheckpoint(checkpointFile) {
|
|
@@ -456,6 +463,11 @@ async function importUsersBatch(config, users, totalImportedBeforeBatch) {
|
|
|
456
463
|
}
|
|
457
464
|
async function migrateRowndUsersToSuperTokens(config) {
|
|
458
465
|
const checkpoint = config.checkpoint.resume ? await loadCheckpoint(config.checkpoint.file) : null;
|
|
466
|
+
if (checkpoint && (checkpoint.tenantId ?? "public") !== config.supertokens.tenantId) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`Checkpoint tenant ${checkpoint.tenantId ?? "public"} does not match configured tenant ${config.supertokens.tenantId}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
459
471
|
const failedMappingsFile = getFailedMappingsFilePath(config.checkpoint.file);
|
|
460
472
|
let cursor = checkpoint?.cursor;
|
|
461
473
|
let totalProcessed = 0;
|
|
@@ -489,7 +501,10 @@ async function migrateRowndUsersToSuperTokens(config) {
|
|
|
489
501
|
try {
|
|
490
502
|
mappedUsers.push({
|
|
491
503
|
rowndUserId,
|
|
492
|
-
user: mapRowndUserToSuperTokens(
|
|
504
|
+
user: mapRowndUserToSuperTokens(
|
|
505
|
+
rowndUser,
|
|
506
|
+
config.supertokens.tenantId === "public" ? void 0 : config.supertokens.tenantId
|
|
507
|
+
)
|
|
493
508
|
});
|
|
494
509
|
} catch (error) {
|
|
495
510
|
const errorMessage = error instanceof Error ? error.message : "Unknown mapping error";
|
|
@@ -511,6 +526,7 @@ async function migrateRowndUsersToSuperTokens(config) {
|
|
|
511
526
|
await saveCheckpoint(config.checkpoint.file, {
|
|
512
527
|
cursor,
|
|
513
528
|
importedCount: totalImported,
|
|
529
|
+
tenantId: config.supertokens.tenantId,
|
|
514
530
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
515
531
|
});
|
|
516
532
|
console.log(`Processed ${totalProcessed} users so far`);
|
|
@@ -68,7 +68,8 @@ var ConfigSchema = import_zod.z.object({
|
|
|
68
68
|
supertokens: import_zod.z.object({
|
|
69
69
|
connectionURI: import_zod.z.string(),
|
|
70
70
|
apiKey: import_zod.z.string().optional(),
|
|
71
|
-
batchSize: import_zod.z.number().int().positive()
|
|
71
|
+
batchSize: import_zod.z.number().int().positive(),
|
|
72
|
+
tenantId: import_zod.z.string().min(1).default("public")
|
|
72
73
|
}),
|
|
73
74
|
thirdPartyProviders: import_zod.z.array(
|
|
74
75
|
import_zod.z.object({
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -866,6 +866,9 @@ var RowndPluginError = class extends Error {
|
|
|
866
866
|
|
|
867
867
|
// src/utils.ts
|
|
868
868
|
var ROWND_OAUTH_LOGIN_CHALLENGE_PARAM = "rownd_oauth_login_challenge";
|
|
869
|
+
function resolveTenantId(req) {
|
|
870
|
+
return req.getKeyValueFromQuery("tenantId") || PUBLIC_TENANT_ID;
|
|
871
|
+
}
|
|
869
872
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
870
873
|
try {
|
|
871
874
|
const url = new URL(inputUrl);
|
|
@@ -1497,7 +1500,7 @@ function isIdentityField(field) {
|
|
|
1497
1500
|
function isInternalMetadataField(field) {
|
|
1498
1501
|
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1499
1502
|
}
|
|
1500
|
-
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1503
|
+
function mapRowndUserToSuperTokens(rowndUser, tenantId) {
|
|
1501
1504
|
const loginMethods = [];
|
|
1502
1505
|
const rowndUserData = rowndUser.data || {};
|
|
1503
1506
|
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
@@ -1511,7 +1514,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1511
1514
|
thirdPartyId: "google",
|
|
1512
1515
|
thirdPartyUserId: rowndUserData.google_id,
|
|
1513
1516
|
email: googleEmail,
|
|
1514
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
1517
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id,
|
|
1518
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1515
1519
|
});
|
|
1516
1520
|
}
|
|
1517
1521
|
if (rowndUserData.apple_id) {
|
|
@@ -1521,21 +1525,24 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1521
1525
|
thirdPartyId: "apple",
|
|
1522
1526
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
1523
1527
|
email: appleEmail,
|
|
1524
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
1528
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id,
|
|
1529
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1525
1530
|
});
|
|
1526
1531
|
}
|
|
1527
1532
|
if (rowndUserData.phone_number) {
|
|
1528
1533
|
loginMethods.push({
|
|
1529
1534
|
recipeId: "passwordless",
|
|
1530
1535
|
phoneNumber: rowndUserData.phone_number,
|
|
1531
|
-
isVerified: !!rowndUserVerifiedData.phone_number
|
|
1536
|
+
isVerified: !!rowndUserVerifiedData.phone_number,
|
|
1537
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1532
1538
|
});
|
|
1533
1539
|
}
|
|
1534
1540
|
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
1535
1541
|
loginMethods.push({
|
|
1536
1542
|
recipeId: "passwordless",
|
|
1537
1543
|
email: rowndUserData.email,
|
|
1538
|
-
isVerified: !!rowndUserVerifiedData.email
|
|
1544
|
+
isVerified: !!rowndUserVerifiedData.email,
|
|
1545
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1539
1546
|
});
|
|
1540
1547
|
}
|
|
1541
1548
|
let authLevel = rowndUser.auth_level;
|
|
@@ -1547,7 +1554,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1547
1554
|
thirdPartyId,
|
|
1548
1555
|
thirdPartyUserId: rowndUserData.user_id,
|
|
1549
1556
|
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1550
|
-
isVerified: false
|
|
1557
|
+
isVerified: false,
|
|
1558
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1551
1559
|
});
|
|
1552
1560
|
}
|
|
1553
1561
|
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
@@ -2093,7 +2101,7 @@ function getPendingVerifications(metadata) {
|
|
|
2093
2101
|
function isPendingVerification(value) {
|
|
2094
2102
|
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
2095
2103
|
}
|
|
2096
|
-
async function getUserById(userId) {
|
|
2104
|
+
async function getUserById(userId, tenantId = PUBLIC_TENANT_ID) {
|
|
2097
2105
|
const metadata = await getUserMetadata(userId);
|
|
2098
2106
|
const stUser = await import_supertokens_node2.default.getUser(userId);
|
|
2099
2107
|
if (!stUser) {
|
|
@@ -2122,7 +2130,10 @@ async function getUserById(userId) {
|
|
|
2122
2130
|
const verifiedData = {
|
|
2123
2131
|
...originalRowndUser?.verified_data || {}
|
|
2124
2132
|
};
|
|
2125
|
-
|
|
2133
|
+
const tenantLoginMethods = stUser.loginMethods.filter(
|
|
2134
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2135
|
+
);
|
|
2136
|
+
for (const method of tenantLoginMethods) {
|
|
2126
2137
|
if (method.recipeId === "passwordless") {
|
|
2127
2138
|
if (method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2128
2139
|
verifiedData.email = method.email;
|
|
@@ -2162,12 +2173,16 @@ async function getUserById(userId) {
|
|
|
2162
2173
|
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
2163
2174
|
verifiedData.phone_number = data.phone_number;
|
|
2164
2175
|
}
|
|
2165
|
-
const
|
|
2176
|
+
const tenantUser = {
|
|
2177
|
+
...stUser,
|
|
2178
|
+
loginMethods: tenantLoginMethods
|
|
2179
|
+
};
|
|
2180
|
+
const anonymousId = getAnonymousId(stUser.id, tenantUser, metadata);
|
|
2166
2181
|
if (anonymousId && data.anonymous_id === void 0) {
|
|
2167
2182
|
data.anonymous_id = anonymousId;
|
|
2168
2183
|
}
|
|
2169
2184
|
const authLevel = getEffectiveAuthLevel(
|
|
2170
|
-
|
|
2185
|
+
tenantUser,
|
|
2171
2186
|
originalRowndUser?.auth_level,
|
|
2172
2187
|
verifiedData
|
|
2173
2188
|
);
|
|
@@ -2176,15 +2191,15 @@ async function getUserById(userId) {
|
|
|
2176
2191
|
data[key] = "";
|
|
2177
2192
|
}
|
|
2178
2193
|
}
|
|
2179
|
-
const sortedByJoined = [...
|
|
2194
|
+
const sortedByJoined = [...tenantLoginMethods].sort(
|
|
2180
2195
|
(a, b) => a.timeJoined - b.timeJoined
|
|
2181
2196
|
);
|
|
2182
|
-
const latestSessionInfo = await getLatestSessionInfo(stUser.id);
|
|
2197
|
+
const latestSessionInfo = await getLatestSessionInfo(stUser.id, tenantId);
|
|
2183
2198
|
const firstMethod = sortedByJoined[0];
|
|
2184
2199
|
const latestSessionRecipeUserId = latestSessionInfo?.recipeUserId.getAsString();
|
|
2185
2200
|
const lastMethod = latestSessionRecipeUserId ? stUser.loginMethods.find(
|
|
2186
2201
|
(method) => method.recipeUserId.getAsString() === latestSessionRecipeUserId
|
|
2187
|
-
) : [...
|
|
2202
|
+
) : [...tenantLoginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
|
|
2188
2203
|
const lastSignInAt = latestSessionInfo?.timeCreated ?? stUser.timeJoined;
|
|
2189
2204
|
const metadataMeta = Object.fromEntries(
|
|
2190
2205
|
Object.entries(metadata).filter(
|
|
@@ -2212,11 +2227,11 @@ async function getUserById(userId) {
|
|
|
2212
2227
|
attributes: originalRowndUser?.attributes || {}
|
|
2213
2228
|
};
|
|
2214
2229
|
}
|
|
2215
|
-
async function getLatestSessionInfo(userId) {
|
|
2230
|
+
async function getLatestSessionInfo(userId, tenantId) {
|
|
2216
2231
|
const sessionHandles = await import_session.default.getAllSessionHandlesForUser(
|
|
2217
2232
|
userId,
|
|
2218
2233
|
true,
|
|
2219
|
-
|
|
2234
|
+
tenantId
|
|
2220
2235
|
);
|
|
2221
2236
|
const sessionInfos = await Promise.all(
|
|
2222
2237
|
sessionHandles.map(
|
|
@@ -2231,21 +2246,21 @@ async function getLatestSessionInfo(userId) {
|
|
|
2231
2246
|
}
|
|
2232
2247
|
return latestSessionInfo;
|
|
2233
2248
|
}
|
|
2234
|
-
async function updateUserData(userId, inputData) {
|
|
2249
|
+
async function updateUserData(userId, inputData, tenantId = PUBLIC_TENANT_ID) {
|
|
2235
2250
|
const metadata = await getUserMetadata(userId);
|
|
2236
2251
|
const updatedMetadata = {
|
|
2237
2252
|
...metadata,
|
|
2238
2253
|
...inputData
|
|
2239
2254
|
};
|
|
2240
2255
|
await import_usermetadata3.default.updateUserMetadata(userId, updatedMetadata);
|
|
2241
|
-
return getUserById(userId);
|
|
2256
|
+
return getUserById(userId, tenantId);
|
|
2242
2257
|
}
|
|
2243
2258
|
async function startPendingEmailVerification(input) {
|
|
2244
2259
|
const metadata = await getUserMetadata(input.userId);
|
|
2245
|
-
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
2260
|
+
const currentEmail = (await getUserById(input.userId, input.tenantId)).data.email;
|
|
2246
2261
|
const pendingVerifications = getPendingVerifications(metadata);
|
|
2247
2262
|
const pendingEmailVerifications = pendingVerifications.filter(
|
|
2248
|
-
(pendingVerification2) => pendingVerification2.field === "email"
|
|
2263
|
+
(pendingVerification2) => pendingVerification2.field === "email" && (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) === input.tenantId
|
|
2249
2264
|
);
|
|
2250
2265
|
if (currentEmail === input.email) {
|
|
2251
2266
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
@@ -2260,11 +2275,11 @@ async function startPendingEmailVerification(input) {
|
|
|
2260
2275
|
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
2261
2276
|
...metadata,
|
|
2262
2277
|
rownd_pending_verification: pendingVerifications.filter(
|
|
2263
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2278
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2264
2279
|
)
|
|
2265
2280
|
});
|
|
2266
2281
|
}
|
|
2267
|
-
return getUserById(input.userId);
|
|
2282
|
+
return getUserById(input.userId, input.tenantId);
|
|
2268
2283
|
}
|
|
2269
2284
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
2270
2285
|
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
@@ -2278,13 +2293,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2278
2293
|
id: input.pendingVerificationId,
|
|
2279
2294
|
field: "email",
|
|
2280
2295
|
value: input.email,
|
|
2281
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2296
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2297
|
+
tenantId: input.tenantId
|
|
2282
2298
|
};
|
|
2283
2299
|
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
2284
2300
|
...metadata,
|
|
2285
2301
|
rownd_pending_verification: [
|
|
2286
2302
|
...pendingVerifications.filter(
|
|
2287
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2303
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2288
2304
|
),
|
|
2289
2305
|
pendingVerification
|
|
2290
2306
|
]
|
|
@@ -2303,12 +2319,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2303
2319
|
await completePendingEmailVerification({
|
|
2304
2320
|
recipeUserId: input.recipeUserId,
|
|
2305
2321
|
email: input.email,
|
|
2322
|
+
tenantId: input.tenantId,
|
|
2306
2323
|
userContext: input.userContext
|
|
2307
2324
|
});
|
|
2308
2325
|
}
|
|
2309
|
-
return getUserById(input.userId);
|
|
2326
|
+
return getUserById(input.userId, input.tenantId);
|
|
2310
2327
|
}
|
|
2311
2328
|
async function completePendingEmailVerification(input) {
|
|
2329
|
+
const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
|
|
2312
2330
|
const user = await import_supertokens_node2.default.getUser(
|
|
2313
2331
|
input.recipeUserId.getAsString(),
|
|
2314
2332
|
input.userContext
|
|
@@ -2319,7 +2337,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2319
2337
|
const pendingVerification = pendingVerifications.find(
|
|
2320
2338
|
(pendingVerification2) => isMatchingPendingEmailVerification(
|
|
2321
2339
|
pendingVerification2,
|
|
2322
|
-
input.email
|
|
2340
|
+
input.email,
|
|
2341
|
+
tenantId
|
|
2323
2342
|
)
|
|
2324
2343
|
);
|
|
2325
2344
|
if (!pendingVerification) {
|
|
@@ -2327,7 +2346,12 @@ async function completePendingEmailVerification(input) {
|
|
|
2327
2346
|
}
|
|
2328
2347
|
let metadataUserId = userId;
|
|
2329
2348
|
let verifiedRecipeUserId = input.recipeUserId;
|
|
2330
|
-
const
|
|
2349
|
+
const tenantLoginMethods = user?.loginMethods.filter(
|
|
2350
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2351
|
+
) ?? [];
|
|
2352
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(
|
|
2353
|
+
tenantLoginMethods
|
|
2354
|
+
);
|
|
2331
2355
|
if (passwordlessEmailMethod) {
|
|
2332
2356
|
const updateResult = await import_passwordless.default.updateUser({
|
|
2333
2357
|
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
@@ -2340,9 +2364,9 @@ async function completePendingEmailVerification(input) {
|
|
|
2340
2364
|
);
|
|
2341
2365
|
}
|
|
2342
2366
|
verifiedRecipeUserId = passwordlessEmailMethod.recipeUserId;
|
|
2343
|
-
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
2367
|
+
} else if (hasOnlyGuestLoginMethods(user ? { ...user, loginMethods: tenantLoginMethods } : user)) {
|
|
2344
2368
|
const isPasswordlessSignUpAllowed = await import_accountlinking.default.isSignUpAllowed(
|
|
2345
|
-
|
|
2369
|
+
tenantId,
|
|
2346
2370
|
{
|
|
2347
2371
|
recipeId: "passwordless",
|
|
2348
2372
|
email: input.email
|
|
@@ -2356,7 +2380,7 @@ async function completePendingEmailVerification(input) {
|
|
|
2356
2380
|
}
|
|
2357
2381
|
const passwordlessUser = await import_passwordless.default.signInUp({
|
|
2358
2382
|
email: input.email,
|
|
2359
|
-
tenantId
|
|
2383
|
+
tenantId,
|
|
2360
2384
|
userContext: input.userContext
|
|
2361
2385
|
});
|
|
2362
2386
|
verifiedRecipeUserId = passwordlessUser.recipeUserId;
|
|
@@ -2400,7 +2424,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2400
2424
|
rownd_pending_verification: targetPendingVerifications.filter(
|
|
2401
2425
|
(verification) => !isMatchingPendingEmailVerification(
|
|
2402
2426
|
verification,
|
|
2403
|
-
input.email
|
|
2427
|
+
input.email,
|
|
2428
|
+
tenantId
|
|
2404
2429
|
)
|
|
2405
2430
|
)
|
|
2406
2431
|
};
|
|
@@ -2410,8 +2435,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2410
2435
|
recipeUserId: verifiedRecipeUserId
|
|
2411
2436
|
};
|
|
2412
2437
|
}
|
|
2413
|
-
function isMatchingPendingEmailVerification(verification, email) {
|
|
2414
|
-
return verification.field === "email" && verification.value === email;
|
|
2438
|
+
function isMatchingPendingEmailVerification(verification, email, tenantId) {
|
|
2439
|
+
return verification.field === "email" && verification.value === email && (verification.tenantId ?? PUBLIC_TENANT_ID) === tenantId;
|
|
2415
2440
|
}
|
|
2416
2441
|
async function updateUserMetadata(userId, inputMeta) {
|
|
2417
2442
|
const metadata = await getUserMetadata(userId);
|
|
@@ -2429,8 +2454,8 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
2429
2454
|
)
|
|
2430
2455
|
};
|
|
2431
2456
|
}
|
|
2432
|
-
function getPasswordlessEmailLoginMethod(
|
|
2433
|
-
return
|
|
2457
|
+
function getPasswordlessEmailLoginMethod(loginMethods) {
|
|
2458
|
+
return loginMethods.find((method) => {
|
|
2434
2459
|
return method.recipeId === "passwordless" && !!method.email;
|
|
2435
2460
|
});
|
|
2436
2461
|
}
|
|
@@ -2440,6 +2465,7 @@ var import_crypto = require("crypto");
|
|
|
2440
2465
|
var import_supertokens_node3 = __toESM(require("supertokens-node"));
|
|
2441
2466
|
var import_session2 = __toESM(require("supertokens-node/recipe/session"));
|
|
2442
2467
|
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
2468
|
+
var import_multitenancy = __toESM(require("supertokens-node/recipe/multitenancy"));
|
|
2443
2469
|
function isBodyString(body, key) {
|
|
2444
2470
|
return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
|
|
2445
2471
|
}
|
|
@@ -2504,7 +2530,9 @@ function handleGuestLogin(deps) {
|
|
|
2504
2530
|
return async (req, res, _session, userContext) => {
|
|
2505
2531
|
const startedAt = Date.now();
|
|
2506
2532
|
const guestId = `guest_${(0, import_crypto.randomUUID)()}`;
|
|
2533
|
+
let tenantId;
|
|
2507
2534
|
try {
|
|
2535
|
+
tenantId = resolveTenantId(req);
|
|
2508
2536
|
const body = parseGuestBody(await getJsonBody(req));
|
|
2509
2537
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2510
2538
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2512,7 +2540,7 @@ function handleGuestLogin(deps) {
|
|
|
2512
2540
|
const thirdPartyUserId = thirdPartyId === INSTANT_AUTH_METHOD_ID ? `anon_${(0, import_crypto.randomUUID)()}` : guestId;
|
|
2513
2541
|
const authLevel = thirdPartyId === INSTANT_AUTH_METHOD_ID ? INSTANT_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
2514
2542
|
const response = await import_thirdparty.default.manuallyCreateOrUpdateUser(
|
|
2515
|
-
|
|
2543
|
+
tenantId,
|
|
2516
2544
|
thirdPartyId,
|
|
2517
2545
|
thirdPartyUserId,
|
|
2518
2546
|
`${thirdPartyUserId}@anonymous.local`,
|
|
@@ -2528,7 +2556,7 @@ function handleGuestLogin(deps) {
|
|
|
2528
2556
|
await import_session2.default.createNewSession(
|
|
2529
2557
|
req,
|
|
2530
2558
|
res,
|
|
2531
|
-
|
|
2559
|
+
tenantId,
|
|
2532
2560
|
response.recipeUserId,
|
|
2533
2561
|
{
|
|
2534
2562
|
...buildRowndAudience({}, appVariantId),
|
|
@@ -2543,7 +2571,7 @@ function handleGuestLogin(deps) {
|
|
|
2543
2571
|
deps.telemetryClient.recordSuccess({
|
|
2544
2572
|
outcome: "success",
|
|
2545
2573
|
durationMs: Date.now() - startedAt,
|
|
2546
|
-
tenantId
|
|
2574
|
+
tenantId,
|
|
2547
2575
|
superTokensUserId: response.user.id
|
|
2548
2576
|
});
|
|
2549
2577
|
return {
|
|
@@ -2555,7 +2583,7 @@ function handleGuestLogin(deps) {
|
|
|
2555
2583
|
deps.telemetryClient.recordError({
|
|
2556
2584
|
error,
|
|
2557
2585
|
startedAt,
|
|
2558
|
-
tenantId
|
|
2586
|
+
tenantId
|
|
2559
2587
|
});
|
|
2560
2588
|
return {
|
|
2561
2589
|
status: "ERROR",
|
|
@@ -2567,7 +2595,7 @@ function handleGuestLogin(deps) {
|
|
|
2567
2595
|
function handleMigrate(deps) {
|
|
2568
2596
|
return async (req, res, _session, userContext) => {
|
|
2569
2597
|
const startedAt = Date.now();
|
|
2570
|
-
let tenantId
|
|
2598
|
+
let tenantId;
|
|
2571
2599
|
let rowndUserId;
|
|
2572
2600
|
let superTokensUserId;
|
|
2573
2601
|
let user;
|
|
@@ -2576,6 +2604,7 @@ function handleMigrate(deps) {
|
|
|
2576
2604
|
if (!deps.stConfig.supertokens) {
|
|
2577
2605
|
throw new Error("Supertokens config not found");
|
|
2578
2606
|
}
|
|
2607
|
+
tenantId = resolveTenantId(req);
|
|
2579
2608
|
const parsed = await parseRequest(req);
|
|
2580
2609
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2581
2610
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2583,13 +2612,16 @@ function handleMigrate(deps) {
|
|
|
2583
2612
|
const rowndUser = await fetchOptionalRowndUserInfo(rowndUserId);
|
|
2584
2613
|
if (!rowndUser) {
|
|
2585
2614
|
logDebugMessage(
|
|
2586
|
-
`Skipping migration because user does not exist in Rownd. tenantId: ${
|
|
2615
|
+
`Skipping migration because user does not exist in Rownd. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2587
2616
|
);
|
|
2588
2617
|
return { status: "OK" };
|
|
2589
2618
|
}
|
|
2590
2619
|
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2591
2620
|
if (!user) {
|
|
2592
|
-
const stUserImport = mapRowndUserToSuperTokens(
|
|
2621
|
+
const stUserImport = mapRowndUserToSuperTokens(
|
|
2622
|
+
rowndUser,
|
|
2623
|
+
tenantId === PUBLIC_TENANT_ID ? void 0 : tenantId
|
|
2624
|
+
);
|
|
2593
2625
|
try {
|
|
2594
2626
|
await importUser(stUserImport, deps.stConfig.supertokens);
|
|
2595
2627
|
clearSuperTokensCoreCallCache(userContext);
|
|
@@ -2607,29 +2639,43 @@ function handleMigrate(deps) {
|
|
|
2607
2639
|
superTokensUserId = user.id;
|
|
2608
2640
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2609
2641
|
logDebugMessage(
|
|
2610
|
-
`User already migrated (race condition). tenantId: ${
|
|
2642
|
+
`User already migrated (race condition). tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2611
2643
|
);
|
|
2612
2644
|
}
|
|
2613
2645
|
logDebugMessage(
|
|
2614
|
-
`User migrated successfully. tenantId: ${
|
|
2646
|
+
`User migrated successfully. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2615
2647
|
);
|
|
2616
2648
|
} else {
|
|
2617
2649
|
superTokensUserId = user.id;
|
|
2618
2650
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2619
2651
|
logDebugMessage(
|
|
2620
|
-
`User already migrated. tenantId: ${
|
|
2652
|
+
`User already migrated. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2621
2653
|
);
|
|
2622
2654
|
}
|
|
2623
2655
|
if (superTokensUserId) {
|
|
2624
2656
|
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
2625
2657
|
}
|
|
2658
|
+
const tenantLoginMethod = user?.loginMethods.find(
|
|
2659
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2660
|
+
);
|
|
2661
|
+
recipeUserId = tenantLoginMethod?.recipeUserId ?? recipeUserId;
|
|
2626
2662
|
if (!recipeUserId) {
|
|
2627
2663
|
throw new Error("User not found or has no login methods");
|
|
2628
2664
|
}
|
|
2665
|
+
if (!tenantLoginMethod) {
|
|
2666
|
+
const associationResult = await import_multitenancy.default.associateUserToTenant(
|
|
2667
|
+
tenantId,
|
|
2668
|
+
recipeUserId,
|
|
2669
|
+
userContext
|
|
2670
|
+
);
|
|
2671
|
+
if (associationResult.status !== "OK") {
|
|
2672
|
+
throw new Error(`Failed to associate migrated user with tenant: ${associationResult.status}`);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2629
2675
|
await import_session2.default.createNewSession(
|
|
2630
2676
|
req,
|
|
2631
2677
|
res,
|
|
2632
|
-
|
|
2678
|
+
tenantId,
|
|
2633
2679
|
recipeUserId,
|
|
2634
2680
|
{
|
|
2635
2681
|
...buildRowndAudience({}, appVariantId)
|
|
@@ -2638,7 +2684,7 @@ function handleMigrate(deps) {
|
|
|
2638
2684
|
userContext
|
|
2639
2685
|
);
|
|
2640
2686
|
logDebugMessage(
|
|
2641
|
-
`Session migrated successfully. tenantId: ${
|
|
2687
|
+
`Session migrated successfully. tenantId: ${tenantId}, userId: ${superTokensUserId}`
|
|
2642
2688
|
);
|
|
2643
2689
|
deps.telemetryClient.recordSuccess({
|
|
2644
2690
|
outcome: "success",
|
|
@@ -2672,7 +2718,7 @@ function clearSuperTokensCoreCallCache(userContext) {
|
|
|
2672
2718
|
}
|
|
2673
2719
|
function handleGetUser() {
|
|
2674
2720
|
return async (_req, _res, session) => {
|
|
2675
|
-
const user = await getUserById(session.getUserId());
|
|
2721
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2676
2722
|
return {
|
|
2677
2723
|
status: "OK",
|
|
2678
2724
|
...user
|
|
@@ -2698,7 +2744,11 @@ function handleUpdateUser() {
|
|
|
2698
2744
|
return permissionError;
|
|
2699
2745
|
}
|
|
2700
2746
|
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
2701
|
-
await updateUserData(
|
|
2747
|
+
await updateUserData(
|
|
2748
|
+
session.getUserId(),
|
|
2749
|
+
dataWithoutEmail,
|
|
2750
|
+
session.getTenantId()
|
|
2751
|
+
);
|
|
2702
2752
|
}
|
|
2703
2753
|
if (hasEmailUpdate) {
|
|
2704
2754
|
const pendingVerificationResult = await startPendingEmailVerification({
|
|
@@ -2714,7 +2764,7 @@ function handleUpdateUser() {
|
|
|
2714
2764
|
...pendingVerificationResult
|
|
2715
2765
|
};
|
|
2716
2766
|
}
|
|
2717
|
-
const user = await getUserById(session.getUserId());
|
|
2767
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2718
2768
|
return {
|
|
2719
2769
|
status: "OK",
|
|
2720
2770
|
...user
|
|
@@ -2781,7 +2831,7 @@ function handleGetUserField() {
|
|
|
2781
2831
|
if (!field) {
|
|
2782
2832
|
return missingFieldResponse();
|
|
2783
2833
|
}
|
|
2784
|
-
const user = await getUserById(session.getUserId());
|
|
2834
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2785
2835
|
return {
|
|
2786
2836
|
status: "OK",
|
|
2787
2837
|
value: user.data[field]
|
|
@@ -2815,9 +2865,11 @@ function handleUpdateUserField() {
|
|
|
2815
2865
|
if (permissionError) {
|
|
2816
2866
|
return permissionError;
|
|
2817
2867
|
}
|
|
2818
|
-
const updateUserDataResult = await updateUserData(
|
|
2819
|
-
|
|
2820
|
-
|
|
2868
|
+
const updateUserDataResult = await updateUserData(
|
|
2869
|
+
session.getUserId(),
|
|
2870
|
+
{ [field]: payload.value },
|
|
2871
|
+
session.getTenantId()
|
|
2872
|
+
);
|
|
2821
2873
|
return {
|
|
2822
2874
|
status: "OK",
|
|
2823
2875
|
...updateUserDataResult
|
|
@@ -3300,6 +3352,7 @@ var init = createPluginInitFunction(
|
|
|
3300
3352
|
const verificationResult = await completePendingEmailVerification({
|
|
3301
3353
|
recipeUserId: response.user.recipeUserId,
|
|
3302
3354
|
email: response.user.email,
|
|
3355
|
+
tenantId: input.tenantId,
|
|
3303
3356
|
userContext: input.userContext
|
|
3304
3357
|
});
|
|
3305
3358
|
const session = input.session;
|
package/dist/index.mjs
CHANGED
|
@@ -843,6 +843,9 @@ var RowndPluginError = class extends Error {
|
|
|
843
843
|
|
|
844
844
|
// src/utils.ts
|
|
845
845
|
var ROWND_OAUTH_LOGIN_CHALLENGE_PARAM = "rownd_oauth_login_challenge";
|
|
846
|
+
function resolveTenantId(req) {
|
|
847
|
+
return req.getKeyValueFromQuery("tenantId") || PUBLIC_TENANT_ID;
|
|
848
|
+
}
|
|
846
849
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
847
850
|
try {
|
|
848
851
|
const url = new URL(inputUrl);
|
|
@@ -1474,7 +1477,7 @@ function isIdentityField(field) {
|
|
|
1474
1477
|
function isInternalMetadataField(field) {
|
|
1475
1478
|
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1476
1479
|
}
|
|
1477
|
-
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1480
|
+
function mapRowndUserToSuperTokens(rowndUser, tenantId) {
|
|
1478
1481
|
const loginMethods = [];
|
|
1479
1482
|
const rowndUserData = rowndUser.data || {};
|
|
1480
1483
|
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
@@ -1488,7 +1491,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1488
1491
|
thirdPartyId: "google",
|
|
1489
1492
|
thirdPartyUserId: rowndUserData.google_id,
|
|
1490
1493
|
email: googleEmail,
|
|
1491
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
1494
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id,
|
|
1495
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1492
1496
|
});
|
|
1493
1497
|
}
|
|
1494
1498
|
if (rowndUserData.apple_id) {
|
|
@@ -1498,21 +1502,24 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1498
1502
|
thirdPartyId: "apple",
|
|
1499
1503
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
1500
1504
|
email: appleEmail,
|
|
1501
|
-
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
1505
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id,
|
|
1506
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1502
1507
|
});
|
|
1503
1508
|
}
|
|
1504
1509
|
if (rowndUserData.phone_number) {
|
|
1505
1510
|
loginMethods.push({
|
|
1506
1511
|
recipeId: "passwordless",
|
|
1507
1512
|
phoneNumber: rowndUserData.phone_number,
|
|
1508
|
-
isVerified: !!rowndUserVerifiedData.phone_number
|
|
1513
|
+
isVerified: !!rowndUserVerifiedData.phone_number,
|
|
1514
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1509
1515
|
});
|
|
1510
1516
|
}
|
|
1511
1517
|
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
1512
1518
|
loginMethods.push({
|
|
1513
1519
|
recipeId: "passwordless",
|
|
1514
1520
|
email: rowndUserData.email,
|
|
1515
|
-
isVerified: !!rowndUserVerifiedData.email
|
|
1521
|
+
isVerified: !!rowndUserVerifiedData.email,
|
|
1522
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1516
1523
|
});
|
|
1517
1524
|
}
|
|
1518
1525
|
let authLevel = rowndUser.auth_level;
|
|
@@ -1524,7 +1531,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1524
1531
|
thirdPartyId,
|
|
1525
1532
|
thirdPartyUserId: rowndUserData.user_id,
|
|
1526
1533
|
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1527
|
-
isVerified: false
|
|
1534
|
+
isVerified: false,
|
|
1535
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1528
1536
|
});
|
|
1529
1537
|
}
|
|
1530
1538
|
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
@@ -2070,7 +2078,7 @@ function getPendingVerifications(metadata) {
|
|
|
2070
2078
|
function isPendingVerification(value) {
|
|
2071
2079
|
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
2072
2080
|
}
|
|
2073
|
-
async function getUserById(userId) {
|
|
2081
|
+
async function getUserById(userId, tenantId = PUBLIC_TENANT_ID) {
|
|
2074
2082
|
const metadata = await getUserMetadata(userId);
|
|
2075
2083
|
const stUser = await SuperTokens2.getUser(userId);
|
|
2076
2084
|
if (!stUser) {
|
|
@@ -2099,7 +2107,10 @@ async function getUserById(userId) {
|
|
|
2099
2107
|
const verifiedData = {
|
|
2100
2108
|
...originalRowndUser?.verified_data || {}
|
|
2101
2109
|
};
|
|
2102
|
-
|
|
2110
|
+
const tenantLoginMethods = stUser.loginMethods.filter(
|
|
2111
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2112
|
+
);
|
|
2113
|
+
for (const method of tenantLoginMethods) {
|
|
2103
2114
|
if (method.recipeId === "passwordless") {
|
|
2104
2115
|
if (method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2105
2116
|
verifiedData.email = method.email;
|
|
@@ -2139,12 +2150,16 @@ async function getUserById(userId) {
|
|
|
2139
2150
|
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
2140
2151
|
verifiedData.phone_number = data.phone_number;
|
|
2141
2152
|
}
|
|
2142
|
-
const
|
|
2153
|
+
const tenantUser = {
|
|
2154
|
+
...stUser,
|
|
2155
|
+
loginMethods: tenantLoginMethods
|
|
2156
|
+
};
|
|
2157
|
+
const anonymousId = getAnonymousId(stUser.id, tenantUser, metadata);
|
|
2143
2158
|
if (anonymousId && data.anonymous_id === void 0) {
|
|
2144
2159
|
data.anonymous_id = anonymousId;
|
|
2145
2160
|
}
|
|
2146
2161
|
const authLevel = getEffectiveAuthLevel(
|
|
2147
|
-
|
|
2162
|
+
tenantUser,
|
|
2148
2163
|
originalRowndUser?.auth_level,
|
|
2149
2164
|
verifiedData
|
|
2150
2165
|
);
|
|
@@ -2153,15 +2168,15 @@ async function getUserById(userId) {
|
|
|
2153
2168
|
data[key] = "";
|
|
2154
2169
|
}
|
|
2155
2170
|
}
|
|
2156
|
-
const sortedByJoined = [...
|
|
2171
|
+
const sortedByJoined = [...tenantLoginMethods].sort(
|
|
2157
2172
|
(a, b) => a.timeJoined - b.timeJoined
|
|
2158
2173
|
);
|
|
2159
|
-
const latestSessionInfo = await getLatestSessionInfo(stUser.id);
|
|
2174
|
+
const latestSessionInfo = await getLatestSessionInfo(stUser.id, tenantId);
|
|
2160
2175
|
const firstMethod = sortedByJoined[0];
|
|
2161
2176
|
const latestSessionRecipeUserId = latestSessionInfo?.recipeUserId.getAsString();
|
|
2162
2177
|
const lastMethod = latestSessionRecipeUserId ? stUser.loginMethods.find(
|
|
2163
2178
|
(method) => method.recipeUserId.getAsString() === latestSessionRecipeUserId
|
|
2164
|
-
) : [...
|
|
2179
|
+
) : [...tenantLoginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
|
|
2165
2180
|
const lastSignInAt = latestSessionInfo?.timeCreated ?? stUser.timeJoined;
|
|
2166
2181
|
const metadataMeta = Object.fromEntries(
|
|
2167
2182
|
Object.entries(metadata).filter(
|
|
@@ -2189,11 +2204,11 @@ async function getUserById(userId) {
|
|
|
2189
2204
|
attributes: originalRowndUser?.attributes || {}
|
|
2190
2205
|
};
|
|
2191
2206
|
}
|
|
2192
|
-
async function getLatestSessionInfo(userId) {
|
|
2207
|
+
async function getLatestSessionInfo(userId, tenantId) {
|
|
2193
2208
|
const sessionHandles = await Session.getAllSessionHandlesForUser(
|
|
2194
2209
|
userId,
|
|
2195
2210
|
true,
|
|
2196
|
-
|
|
2211
|
+
tenantId
|
|
2197
2212
|
);
|
|
2198
2213
|
const sessionInfos = await Promise.all(
|
|
2199
2214
|
sessionHandles.map(
|
|
@@ -2208,21 +2223,21 @@ async function getLatestSessionInfo(userId) {
|
|
|
2208
2223
|
}
|
|
2209
2224
|
return latestSessionInfo;
|
|
2210
2225
|
}
|
|
2211
|
-
async function updateUserData(userId, inputData) {
|
|
2226
|
+
async function updateUserData(userId, inputData, tenantId = PUBLIC_TENANT_ID) {
|
|
2212
2227
|
const metadata = await getUserMetadata(userId);
|
|
2213
2228
|
const updatedMetadata = {
|
|
2214
2229
|
...metadata,
|
|
2215
2230
|
...inputData
|
|
2216
2231
|
};
|
|
2217
2232
|
await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
|
|
2218
|
-
return getUserById(userId);
|
|
2233
|
+
return getUserById(userId, tenantId);
|
|
2219
2234
|
}
|
|
2220
2235
|
async function startPendingEmailVerification(input) {
|
|
2221
2236
|
const metadata = await getUserMetadata(input.userId);
|
|
2222
|
-
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
2237
|
+
const currentEmail = (await getUserById(input.userId, input.tenantId)).data.email;
|
|
2223
2238
|
const pendingVerifications = getPendingVerifications(metadata);
|
|
2224
2239
|
const pendingEmailVerifications = pendingVerifications.filter(
|
|
2225
|
-
(pendingVerification2) => pendingVerification2.field === "email"
|
|
2240
|
+
(pendingVerification2) => pendingVerification2.field === "email" && (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) === input.tenantId
|
|
2226
2241
|
);
|
|
2227
2242
|
if (currentEmail === input.email) {
|
|
2228
2243
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
@@ -2237,11 +2252,11 @@ async function startPendingEmailVerification(input) {
|
|
|
2237
2252
|
await UserMetadata3.updateUserMetadata(input.userId, {
|
|
2238
2253
|
...metadata,
|
|
2239
2254
|
rownd_pending_verification: pendingVerifications.filter(
|
|
2240
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2255
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2241
2256
|
)
|
|
2242
2257
|
});
|
|
2243
2258
|
}
|
|
2244
|
-
return getUserById(input.userId);
|
|
2259
|
+
return getUserById(input.userId, input.tenantId);
|
|
2245
2260
|
}
|
|
2246
2261
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
2247
2262
|
await EmailVerification.revokeEmailVerificationTokens(
|
|
@@ -2255,13 +2270,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2255
2270
|
id: input.pendingVerificationId,
|
|
2256
2271
|
field: "email",
|
|
2257
2272
|
value: input.email,
|
|
2258
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2273
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2274
|
+
tenantId: input.tenantId
|
|
2259
2275
|
};
|
|
2260
2276
|
await UserMetadata3.updateUserMetadata(input.userId, {
|
|
2261
2277
|
...metadata,
|
|
2262
2278
|
rownd_pending_verification: [
|
|
2263
2279
|
...pendingVerifications.filter(
|
|
2264
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2280
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2265
2281
|
),
|
|
2266
2282
|
pendingVerification
|
|
2267
2283
|
]
|
|
@@ -2280,12 +2296,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2280
2296
|
await completePendingEmailVerification({
|
|
2281
2297
|
recipeUserId: input.recipeUserId,
|
|
2282
2298
|
email: input.email,
|
|
2299
|
+
tenantId: input.tenantId,
|
|
2283
2300
|
userContext: input.userContext
|
|
2284
2301
|
});
|
|
2285
2302
|
}
|
|
2286
|
-
return getUserById(input.userId);
|
|
2303
|
+
return getUserById(input.userId, input.tenantId);
|
|
2287
2304
|
}
|
|
2288
2305
|
async function completePendingEmailVerification(input) {
|
|
2306
|
+
const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
|
|
2289
2307
|
const user = await SuperTokens2.getUser(
|
|
2290
2308
|
input.recipeUserId.getAsString(),
|
|
2291
2309
|
input.userContext
|
|
@@ -2296,7 +2314,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2296
2314
|
const pendingVerification = pendingVerifications.find(
|
|
2297
2315
|
(pendingVerification2) => isMatchingPendingEmailVerification(
|
|
2298
2316
|
pendingVerification2,
|
|
2299
|
-
input.email
|
|
2317
|
+
input.email,
|
|
2318
|
+
tenantId
|
|
2300
2319
|
)
|
|
2301
2320
|
);
|
|
2302
2321
|
if (!pendingVerification) {
|
|
@@ -2304,7 +2323,12 @@ async function completePendingEmailVerification(input) {
|
|
|
2304
2323
|
}
|
|
2305
2324
|
let metadataUserId = userId;
|
|
2306
2325
|
let verifiedRecipeUserId = input.recipeUserId;
|
|
2307
|
-
const
|
|
2326
|
+
const tenantLoginMethods = user?.loginMethods.filter(
|
|
2327
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2328
|
+
) ?? [];
|
|
2329
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(
|
|
2330
|
+
tenantLoginMethods
|
|
2331
|
+
);
|
|
2308
2332
|
if (passwordlessEmailMethod) {
|
|
2309
2333
|
const updateResult = await Passwordless.updateUser({
|
|
2310
2334
|
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
@@ -2317,9 +2341,9 @@ async function completePendingEmailVerification(input) {
|
|
|
2317
2341
|
);
|
|
2318
2342
|
}
|
|
2319
2343
|
verifiedRecipeUserId = passwordlessEmailMethod.recipeUserId;
|
|
2320
|
-
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
2344
|
+
} else if (hasOnlyGuestLoginMethods(user ? { ...user, loginMethods: tenantLoginMethods } : user)) {
|
|
2321
2345
|
const isPasswordlessSignUpAllowed = await AccountLinking.isSignUpAllowed(
|
|
2322
|
-
|
|
2346
|
+
tenantId,
|
|
2323
2347
|
{
|
|
2324
2348
|
recipeId: "passwordless",
|
|
2325
2349
|
email: input.email
|
|
@@ -2333,7 +2357,7 @@ async function completePendingEmailVerification(input) {
|
|
|
2333
2357
|
}
|
|
2334
2358
|
const passwordlessUser = await Passwordless.signInUp({
|
|
2335
2359
|
email: input.email,
|
|
2336
|
-
tenantId
|
|
2360
|
+
tenantId,
|
|
2337
2361
|
userContext: input.userContext
|
|
2338
2362
|
});
|
|
2339
2363
|
verifiedRecipeUserId = passwordlessUser.recipeUserId;
|
|
@@ -2377,7 +2401,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2377
2401
|
rownd_pending_verification: targetPendingVerifications.filter(
|
|
2378
2402
|
(verification) => !isMatchingPendingEmailVerification(
|
|
2379
2403
|
verification,
|
|
2380
|
-
input.email
|
|
2404
|
+
input.email,
|
|
2405
|
+
tenantId
|
|
2381
2406
|
)
|
|
2382
2407
|
)
|
|
2383
2408
|
};
|
|
@@ -2387,8 +2412,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2387
2412
|
recipeUserId: verifiedRecipeUserId
|
|
2388
2413
|
};
|
|
2389
2414
|
}
|
|
2390
|
-
function isMatchingPendingEmailVerification(verification, email) {
|
|
2391
|
-
return verification.field === "email" && verification.value === email;
|
|
2415
|
+
function isMatchingPendingEmailVerification(verification, email, tenantId) {
|
|
2416
|
+
return verification.field === "email" && verification.value === email && (verification.tenantId ?? PUBLIC_TENANT_ID) === tenantId;
|
|
2392
2417
|
}
|
|
2393
2418
|
async function updateUserMetadata(userId, inputMeta) {
|
|
2394
2419
|
const metadata = await getUserMetadata(userId);
|
|
@@ -2406,8 +2431,8 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
2406
2431
|
)
|
|
2407
2432
|
};
|
|
2408
2433
|
}
|
|
2409
|
-
function getPasswordlessEmailLoginMethod(
|
|
2410
|
-
return
|
|
2434
|
+
function getPasswordlessEmailLoginMethod(loginMethods) {
|
|
2435
|
+
return loginMethods.find((method) => {
|
|
2411
2436
|
return method.recipeId === "passwordless" && !!method.email;
|
|
2412
2437
|
});
|
|
2413
2438
|
}
|
|
@@ -2417,6 +2442,7 @@ import { randomUUID } from "crypto";
|
|
|
2417
2442
|
import SuperTokens3 from "supertokens-node";
|
|
2418
2443
|
import Session2 from "supertokens-node/recipe/session";
|
|
2419
2444
|
import ThirdParty from "supertokens-node/recipe/thirdparty";
|
|
2445
|
+
import MultiTenancy from "supertokens-node/recipe/multitenancy";
|
|
2420
2446
|
function isBodyString(body, key) {
|
|
2421
2447
|
return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
|
|
2422
2448
|
}
|
|
@@ -2481,7 +2507,9 @@ function handleGuestLogin(deps) {
|
|
|
2481
2507
|
return async (req, res, _session, userContext) => {
|
|
2482
2508
|
const startedAt = Date.now();
|
|
2483
2509
|
const guestId = `guest_${randomUUID()}`;
|
|
2510
|
+
let tenantId;
|
|
2484
2511
|
try {
|
|
2512
|
+
tenantId = resolveTenantId(req);
|
|
2485
2513
|
const body = parseGuestBody(await getJsonBody(req));
|
|
2486
2514
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2487
2515
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2489,7 +2517,7 @@ function handleGuestLogin(deps) {
|
|
|
2489
2517
|
const thirdPartyUserId = thirdPartyId === INSTANT_AUTH_METHOD_ID ? `anon_${randomUUID()}` : guestId;
|
|
2490
2518
|
const authLevel = thirdPartyId === INSTANT_AUTH_METHOD_ID ? INSTANT_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
2491
2519
|
const response = await ThirdParty.manuallyCreateOrUpdateUser(
|
|
2492
|
-
|
|
2520
|
+
tenantId,
|
|
2493
2521
|
thirdPartyId,
|
|
2494
2522
|
thirdPartyUserId,
|
|
2495
2523
|
`${thirdPartyUserId}@anonymous.local`,
|
|
@@ -2505,7 +2533,7 @@ function handleGuestLogin(deps) {
|
|
|
2505
2533
|
await Session2.createNewSession(
|
|
2506
2534
|
req,
|
|
2507
2535
|
res,
|
|
2508
|
-
|
|
2536
|
+
tenantId,
|
|
2509
2537
|
response.recipeUserId,
|
|
2510
2538
|
{
|
|
2511
2539
|
...buildRowndAudience({}, appVariantId),
|
|
@@ -2520,7 +2548,7 @@ function handleGuestLogin(deps) {
|
|
|
2520
2548
|
deps.telemetryClient.recordSuccess({
|
|
2521
2549
|
outcome: "success",
|
|
2522
2550
|
durationMs: Date.now() - startedAt,
|
|
2523
|
-
tenantId
|
|
2551
|
+
tenantId,
|
|
2524
2552
|
superTokensUserId: response.user.id
|
|
2525
2553
|
});
|
|
2526
2554
|
return {
|
|
@@ -2532,7 +2560,7 @@ function handleGuestLogin(deps) {
|
|
|
2532
2560
|
deps.telemetryClient.recordError({
|
|
2533
2561
|
error,
|
|
2534
2562
|
startedAt,
|
|
2535
|
-
tenantId
|
|
2563
|
+
tenantId
|
|
2536
2564
|
});
|
|
2537
2565
|
return {
|
|
2538
2566
|
status: "ERROR",
|
|
@@ -2544,7 +2572,7 @@ function handleGuestLogin(deps) {
|
|
|
2544
2572
|
function handleMigrate(deps) {
|
|
2545
2573
|
return async (req, res, _session, userContext) => {
|
|
2546
2574
|
const startedAt = Date.now();
|
|
2547
|
-
let tenantId
|
|
2575
|
+
let tenantId;
|
|
2548
2576
|
let rowndUserId;
|
|
2549
2577
|
let superTokensUserId;
|
|
2550
2578
|
let user;
|
|
@@ -2553,6 +2581,7 @@ function handleMigrate(deps) {
|
|
|
2553
2581
|
if (!deps.stConfig.supertokens) {
|
|
2554
2582
|
throw new Error("Supertokens config not found");
|
|
2555
2583
|
}
|
|
2584
|
+
tenantId = resolveTenantId(req);
|
|
2556
2585
|
const parsed = await parseRequest(req);
|
|
2557
2586
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2558
2587
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2560,13 +2589,16 @@ function handleMigrate(deps) {
|
|
|
2560
2589
|
const rowndUser = await fetchOptionalRowndUserInfo(rowndUserId);
|
|
2561
2590
|
if (!rowndUser) {
|
|
2562
2591
|
logDebugMessage(
|
|
2563
|
-
`Skipping migration because user does not exist in Rownd. tenantId: ${
|
|
2592
|
+
`Skipping migration because user does not exist in Rownd. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2564
2593
|
);
|
|
2565
2594
|
return { status: "OK" };
|
|
2566
2595
|
}
|
|
2567
2596
|
user = await SuperTokens3.getUser(rowndUserId, userContext);
|
|
2568
2597
|
if (!user) {
|
|
2569
|
-
const stUserImport = mapRowndUserToSuperTokens(
|
|
2598
|
+
const stUserImport = mapRowndUserToSuperTokens(
|
|
2599
|
+
rowndUser,
|
|
2600
|
+
tenantId === PUBLIC_TENANT_ID ? void 0 : tenantId
|
|
2601
|
+
);
|
|
2570
2602
|
try {
|
|
2571
2603
|
await importUser(stUserImport, deps.stConfig.supertokens);
|
|
2572
2604
|
clearSuperTokensCoreCallCache(userContext);
|
|
@@ -2584,29 +2616,43 @@ function handleMigrate(deps) {
|
|
|
2584
2616
|
superTokensUserId = user.id;
|
|
2585
2617
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2586
2618
|
logDebugMessage(
|
|
2587
|
-
`User already migrated (race condition). tenantId: ${
|
|
2619
|
+
`User already migrated (race condition). tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2588
2620
|
);
|
|
2589
2621
|
}
|
|
2590
2622
|
logDebugMessage(
|
|
2591
|
-
`User migrated successfully. tenantId: ${
|
|
2623
|
+
`User migrated successfully. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2592
2624
|
);
|
|
2593
2625
|
} else {
|
|
2594
2626
|
superTokensUserId = user.id;
|
|
2595
2627
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2596
2628
|
logDebugMessage(
|
|
2597
|
-
`User already migrated. tenantId: ${
|
|
2629
|
+
`User already migrated. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2598
2630
|
);
|
|
2599
2631
|
}
|
|
2600
2632
|
if (superTokensUserId) {
|
|
2601
2633
|
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
2602
2634
|
}
|
|
2635
|
+
const tenantLoginMethod = user?.loginMethods.find(
|
|
2636
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2637
|
+
);
|
|
2638
|
+
recipeUserId = tenantLoginMethod?.recipeUserId ?? recipeUserId;
|
|
2603
2639
|
if (!recipeUserId) {
|
|
2604
2640
|
throw new Error("User not found or has no login methods");
|
|
2605
2641
|
}
|
|
2642
|
+
if (!tenantLoginMethod) {
|
|
2643
|
+
const associationResult = await MultiTenancy.associateUserToTenant(
|
|
2644
|
+
tenantId,
|
|
2645
|
+
recipeUserId,
|
|
2646
|
+
userContext
|
|
2647
|
+
);
|
|
2648
|
+
if (associationResult.status !== "OK") {
|
|
2649
|
+
throw new Error(`Failed to associate migrated user with tenant: ${associationResult.status}`);
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2606
2652
|
await Session2.createNewSession(
|
|
2607
2653
|
req,
|
|
2608
2654
|
res,
|
|
2609
|
-
|
|
2655
|
+
tenantId,
|
|
2610
2656
|
recipeUserId,
|
|
2611
2657
|
{
|
|
2612
2658
|
...buildRowndAudience({}, appVariantId)
|
|
@@ -2615,7 +2661,7 @@ function handleMigrate(deps) {
|
|
|
2615
2661
|
userContext
|
|
2616
2662
|
);
|
|
2617
2663
|
logDebugMessage(
|
|
2618
|
-
`Session migrated successfully. tenantId: ${
|
|
2664
|
+
`Session migrated successfully. tenantId: ${tenantId}, userId: ${superTokensUserId}`
|
|
2619
2665
|
);
|
|
2620
2666
|
deps.telemetryClient.recordSuccess({
|
|
2621
2667
|
outcome: "success",
|
|
@@ -2649,7 +2695,7 @@ function clearSuperTokensCoreCallCache(userContext) {
|
|
|
2649
2695
|
}
|
|
2650
2696
|
function handleGetUser() {
|
|
2651
2697
|
return async (_req, _res, session) => {
|
|
2652
|
-
const user = await getUserById(session.getUserId());
|
|
2698
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2653
2699
|
return {
|
|
2654
2700
|
status: "OK",
|
|
2655
2701
|
...user
|
|
@@ -2675,7 +2721,11 @@ function handleUpdateUser() {
|
|
|
2675
2721
|
return permissionError;
|
|
2676
2722
|
}
|
|
2677
2723
|
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
2678
|
-
await updateUserData(
|
|
2724
|
+
await updateUserData(
|
|
2725
|
+
session.getUserId(),
|
|
2726
|
+
dataWithoutEmail,
|
|
2727
|
+
session.getTenantId()
|
|
2728
|
+
);
|
|
2679
2729
|
}
|
|
2680
2730
|
if (hasEmailUpdate) {
|
|
2681
2731
|
const pendingVerificationResult = await startPendingEmailVerification({
|
|
@@ -2691,7 +2741,7 @@ function handleUpdateUser() {
|
|
|
2691
2741
|
...pendingVerificationResult
|
|
2692
2742
|
};
|
|
2693
2743
|
}
|
|
2694
|
-
const user = await getUserById(session.getUserId());
|
|
2744
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2695
2745
|
return {
|
|
2696
2746
|
status: "OK",
|
|
2697
2747
|
...user
|
|
@@ -2758,7 +2808,7 @@ function handleGetUserField() {
|
|
|
2758
2808
|
if (!field) {
|
|
2759
2809
|
return missingFieldResponse();
|
|
2760
2810
|
}
|
|
2761
|
-
const user = await getUserById(session.getUserId());
|
|
2811
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2762
2812
|
return {
|
|
2763
2813
|
status: "OK",
|
|
2764
2814
|
value: user.data[field]
|
|
@@ -2792,9 +2842,11 @@ function handleUpdateUserField() {
|
|
|
2792
2842
|
if (permissionError) {
|
|
2793
2843
|
return permissionError;
|
|
2794
2844
|
}
|
|
2795
|
-
const updateUserDataResult = await updateUserData(
|
|
2796
|
-
|
|
2797
|
-
|
|
2845
|
+
const updateUserDataResult = await updateUserData(
|
|
2846
|
+
session.getUserId(),
|
|
2847
|
+
{ [field]: payload.value },
|
|
2848
|
+
session.getTenantId()
|
|
2849
|
+
);
|
|
2798
2850
|
return {
|
|
2799
2851
|
status: "OK",
|
|
2800
2852
|
...updateUserDataResult
|
|
@@ -3277,6 +3329,7 @@ var init = createPluginInitFunction(
|
|
|
3277
3329
|
const verificationResult = await completePendingEmailVerification({
|
|
3278
3330
|
recipeUserId: response.user.recipeUserId,
|
|
3279
3331
|
email: response.user.email,
|
|
3332
|
+
tenantId: input.tenantId,
|
|
3280
3333
|
userContext: input.userContext
|
|
3281
3334
|
});
|
|
3282
3335
|
const session = input.session;
|
package/dist/initConfig.js
CHANGED
|
@@ -69,7 +69,8 @@ var ConfigSchema = import_zod.z.object({
|
|
|
69
69
|
supertokens: import_zod.z.object({
|
|
70
70
|
connectionURI: import_zod.z.string(),
|
|
71
71
|
apiKey: import_zod.z.string().optional(),
|
|
72
|
-
batchSize: import_zod.z.number().int().positive()
|
|
72
|
+
batchSize: import_zod.z.number().int().positive(),
|
|
73
|
+
tenantId: import_zod.z.string().min(1).default("public")
|
|
73
74
|
}),
|
|
74
75
|
thirdPartyProviders: import_zod.z.array(
|
|
75
76
|
import_zod.z.object({
|