@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/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
- for (const method of stUser.loginMethods) {
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 anonymousId = getAnonymousId(stUser.id, stUser, metadata);
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
- stUser,
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 = [...stUser.loginMethods].sort(
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
- ) : [...stUser.loginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
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
- PUBLIC_TENANT_ID
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 passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
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
- PUBLIC_TENANT_ID,
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: PUBLIC_TENANT_ID,
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(user) {
2410
- return user?.loginMethods.find((method) => {
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
- PUBLIC_TENANT_ID,
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
- PUBLIC_TENANT_ID,
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: PUBLIC_TENANT_ID,
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: PUBLIC_TENANT_ID
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 = PUBLIC_TENANT_ID;
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: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
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(rowndUser);
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: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
2619
+ `User already migrated (race condition). tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
2588
2620
  );
2589
2621
  }
2590
2622
  logDebugMessage(
2591
- `User migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
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: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
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
- PUBLIC_TENANT_ID,
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: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
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(session.getUserId(), dataWithoutEmail);
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(session.getUserId(), {
2796
- [field]: payload.value
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 (!config2?.rowndAppKey || !config2?.rowndAppSecret) {
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,
@@ -63,6 +63,8 @@ rownd:
63
63
  supertokens:
64
64
  connectionURI: <CONNECTION_URI>
65
65
  apiKey: <API_KEY>
66
+ # Tenant that receives every imported login method.
67
+ tenantId: public
66
68
  # Number of users to send to SuperTokens per bulk import request.
67
69
  batchSize: 500
68
70
  `;
@@ -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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supertokens-plugins/rownd-nodejs",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Rownd User Migration Plugin for SuperTokens",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",