@supertokens-plugins/rownd-nodejs 0.5.1 → 0.6.1
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 +35 -3
- package/dist/bulkMigrate.js +24 -8
- package/dist/generateAppConfig.js +2 -1
- package/dist/index.d.mts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +127 -62
- package/dist/index.mjs +127 -62
- package/dist/initConfig.js +2 -0
- package/dist/setupCoreInstance.js +2 -1
- package/package.json +1 -1
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
|
|
@@ -2814,6 +2866,7 @@ function validateWritableFields(fields) {
|
|
|
2814
2866
|
}
|
|
2815
2867
|
|
|
2816
2868
|
// src/plugin.ts
|
|
2869
|
+
var DISABLED_MIGRATION_ROWND_APP_KEY = "migration-disabled";
|
|
2817
2870
|
var verifyRowndUserSessionOptions = {
|
|
2818
2871
|
sessionRequired: true,
|
|
2819
2872
|
overrideGlobalClaimValidators: (validators) => validators.filter((validator) => {
|
|
@@ -2822,10 +2875,10 @@ var verifyRowndUserSessionOptions = {
|
|
|
2822
2875
|
};
|
|
2823
2876
|
var init = createPluginInitFunction(
|
|
2824
2877
|
(pluginConfig2) => {
|
|
2825
|
-
const rowndClient2 = createInstance({
|
|
2878
|
+
const rowndClient2 = !pluginConfig2.disableRowndUserMigration && pluginConfig2.rowndAppSecret ? createInstance({
|
|
2826
2879
|
app_key: pluginConfig2.rowndAppKey,
|
|
2827
2880
|
app_secret: pluginConfig2.rowndAppSecret
|
|
2828
|
-
});
|
|
2881
|
+
}) : void 0;
|
|
2829
2882
|
const telemetryClient = createClient(pluginConfig2.telemetry);
|
|
2830
2883
|
let hubBootstrapParams;
|
|
2831
2884
|
const addHubBootstrapParams = (input, linkKey, targetPath) => {
|
|
@@ -2865,6 +2918,11 @@ var init = createPluginInitFunction(
|
|
|
2865
2918
|
id: PLUGIN_ID,
|
|
2866
2919
|
compatibleSDKVersions: PLUGIN_SDK_VERSION,
|
|
2867
2920
|
init: async () => {
|
|
2921
|
+
if (pluginConfig2.disableRowndUserMigration) {
|
|
2922
|
+
console.warn(
|
|
2923
|
+
"RowndMigrationPlugin: Rownd user and session migration is disabled."
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2868
2926
|
if (!supertokens.isRecipeInitialized("session")) {
|
|
2869
2927
|
console.warn(
|
|
2870
2928
|
"RowndMigrationPlugin: Session recipe is not initialized. Session migration will fail."
|
|
@@ -2908,11 +2966,11 @@ var init = createPluginInitFunction(
|
|
|
2908
2966
|
method: "post",
|
|
2909
2967
|
handler: withRequestHandler(handleGuestLogin(routeHandlerDeps))
|
|
2910
2968
|
},
|
|
2911
|
-
{
|
|
2969
|
+
...!pluginConfig2.disableRowndUserMigration ? [{
|
|
2912
2970
|
path: `${apiBasePath}${HANDLE_BASE_PATH}/migrate`,
|
|
2913
2971
|
method: "post",
|
|
2914
2972
|
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2915
|
-
},
|
|
2973
|
+
}] : [],
|
|
2916
2974
|
{
|
|
2917
2975
|
path: `${apiBasePath}/plugin/passwordless-cross-device-confirmation/validate`,
|
|
2918
2976
|
method: "post",
|
|
@@ -2920,11 +2978,11 @@ var init = createPluginInitFunction(
|
|
|
2920
2978
|
handleValidatePasswordlessConfirmationBypass(routeHandlerDeps)
|
|
2921
2979
|
)
|
|
2922
2980
|
},
|
|
2923
|
-
{
|
|
2981
|
+
...!pluginConfig2.disableRowndUserMigration ? [{
|
|
2924
2982
|
path: `${apiBasePath}/plugin/migrate-session`,
|
|
2925
2983
|
method: "post",
|
|
2926
2984
|
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2927
|
-
},
|
|
2985
|
+
}] : [],
|
|
2928
2986
|
{
|
|
2929
2987
|
path: `${apiBasePath}${HANDLE_BASE_PATH}/signout`,
|
|
2930
2988
|
method: "post",
|
|
@@ -3277,6 +3335,7 @@ var init = createPluginInitFunction(
|
|
|
3277
3335
|
const verificationResult = await completePendingEmailVerification({
|
|
3278
3336
|
recipeUserId: response.user.recipeUserId,
|
|
3279
3337
|
email: response.user.email,
|
|
3338
|
+
tenantId: input.tenantId,
|
|
3280
3339
|
userContext: input.userContext
|
|
3281
3340
|
});
|
|
3282
3341
|
const session = input.session;
|
|
@@ -3303,9 +3362,14 @@ var init = createPluginInitFunction(
|
|
|
3303
3362
|
},
|
|
3304
3363
|
() => ({}),
|
|
3305
3364
|
(config2) => {
|
|
3306
|
-
if (
|
|
3365
|
+
if (config2?.disableRowndUserMigration !== void 0 && typeof config2.disableRowndUserMigration !== "boolean") {
|
|
3366
|
+
throw new Error(
|
|
3367
|
+
"disableRowndUserMigration must be a boolean in plugin config"
|
|
3368
|
+
);
|
|
3369
|
+
}
|
|
3370
|
+
if (!config2?.disableRowndUserMigration && (!config2?.rowndAppKey || !config2?.rowndAppSecret)) {
|
|
3307
3371
|
throw new Error(
|
|
3308
|
-
"Missing rowndAppKey or rowndAppSecret in plugin config"
|
|
3372
|
+
"Missing rowndAppKey or rowndAppSecret in plugin config. Set disableRowndUserMigration to true to disable migration."
|
|
3309
3373
|
);
|
|
3310
3374
|
}
|
|
3311
3375
|
if (config2.telemetry?.provider === "axiom") {
|
|
@@ -3326,8 +3390,9 @@ var init = createPluginInitFunction(
|
|
|
3326
3390
|
validateClientDomainUrl(key, value);
|
|
3327
3391
|
}
|
|
3328
3392
|
return {
|
|
3329
|
-
rowndAppKey: config2.rowndAppKey,
|
|
3393
|
+
rowndAppKey: config2.rowndAppKey ?? DISABLED_MIGRATION_ROWND_APP_KEY,
|
|
3330
3394
|
rowndAppSecret: config2.rowndAppSecret,
|
|
3395
|
+
disableRowndUserMigration: config2.disableRowndUserMigration === true,
|
|
3331
3396
|
enableDebugLogs: config2.enableDebugLogs,
|
|
3332
3397
|
clientDomains: config2.clientDomains,
|
|
3333
3398
|
crossDeviceConfirmationBypass: config2.crossDeviceConfirmationBypass,
|
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({
|