@supertokens-plugins/rownd-nodejs 0.3.0-beta.6 → 0.3.0-beta.8

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
@@ -264,6 +264,12 @@ var require_supports_color = __commonJS({
264
264
  }
265
265
  });
266
266
 
267
+ // src/plugin.ts
268
+ import {
269
+ EmailVerificationClaim
270
+ } from "supertokens-node/recipe/emailverification";
271
+ import Session3 from "supertokens-node/recipe/session";
272
+
267
273
  // ../../shared/js/src/createPluginInit.ts
268
274
  var import_supertokens_js_override = __toESM(require_build());
269
275
  var createPluginInitFunction = (init2, getImplementation, getNormalisedConfig = (config2) => config2) => {
@@ -701,6 +707,7 @@ var DEFAULT_ROWND_SCHEMA = {
701
707
  };
702
708
  var HUB_LOGIN_PAGE_PATH = "/account/login";
703
709
  var HUB_VERIFY_EMAIL_PAGE_PATH = "/account/verify-email";
710
+ var PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM = "bypassDeviceConfirmation";
704
711
 
705
712
  // src/logger.ts
706
713
  var { logDebugMessage, enableDebugLogs } = buildLogger(
@@ -943,6 +950,109 @@ function getStringList(value) {
943
950
  function getErrorMessage(error) {
944
951
  return error instanceof Error ? error.message : "Unknown error";
945
952
  }
953
+ function getAppInfoString(value) {
954
+ return value.getAsStringDangerous();
955
+ }
956
+ function getWebsiteDomain(input) {
957
+ return getAppInfoString(input.stConfig.appInfo.getOrigin({
958
+ request: input.request,
959
+ userContext: input.userContext ?? {}
960
+ }));
961
+ }
962
+ function normalizeClientDomain(value) {
963
+ if (value.endsWith("://")) {
964
+ return value;
965
+ }
966
+ const parsed = new URL(value);
967
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
968
+ return parsed.origin;
969
+ }
970
+ return `${parsed.protocol}//${parsed.host}`;
971
+ }
972
+ function resolveClientDomain(input) {
973
+ if (!input.clientDomain) {
974
+ return input.websiteDomain;
975
+ }
976
+ const resolved = input.pluginConfig.clientDomains?.[input.clientDomain];
977
+ if (!resolved) {
978
+ throw new Error(`Unknown clientDomain key: ${input.clientDomain}`);
979
+ }
980
+ return resolved;
981
+ }
982
+ function resolveAllowedClientDomain(input) {
983
+ const websiteDomain = getWebsiteDomain(input);
984
+ const resolvedClientDomain = resolveClientDomain({
985
+ clientDomain: input.clientDomain,
986
+ pluginConfig: input.pluginConfig,
987
+ websiteDomain
988
+ });
989
+ const normalizedClientDomain = normalizeClientDomain(resolvedClientDomain);
990
+ const allowed = [
991
+ websiteDomain,
992
+ ...Object.values(input.pluginConfig.clientDomains ?? {})
993
+ ].map(normalizeClientDomain);
994
+ if (!allowed.includes(normalizedClientDomain)) {
995
+ throw new Error(`clientDomain is not allowed: ${resolvedClientDomain}`);
996
+ }
997
+ return normalizedClientDomain;
998
+ }
999
+ function normalizeRedirectToPathForClientDomain(redirectToPath, clientDomain) {
1000
+ if (!redirectToPath) {
1001
+ return void 0;
1002
+ }
1003
+ if (redirectToPath === "NATIVE_APP") {
1004
+ return redirectToPath;
1005
+ }
1006
+ if (redirectToPath.startsWith("//")) {
1007
+ throw new Error("redirectToPath cannot be schemaless");
1008
+ }
1009
+ const normalizedClientDomain = normalizeClientDomain(clientDomain);
1010
+ const redirectUrl = new URL(
1011
+ redirectToPath,
1012
+ normalizedClientDomain.endsWith("://") ? "http://localhost" : normalizedClientDomain
1013
+ );
1014
+ const hasExplicitScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(redirectToPath);
1015
+ if (hasExplicitScheme) {
1016
+ if (redirectUrl.protocol !== "http:" && redirectUrl.protocol !== "https:") {
1017
+ throw new Error("redirectToPath must be http(s) or relative");
1018
+ }
1019
+ if (redirectUrl.origin !== normalizedClientDomain) {
1020
+ throw new Error("redirectToPath must match clientDomain");
1021
+ }
1022
+ }
1023
+ return `${redirectUrl.pathname}${redirectUrl.search}${redirectUrl.hash}`;
1024
+ }
1025
+ function assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath) {
1026
+ if (!redirectToPath) {
1027
+ throw new Error("redirectToPath is required for confirmation bypass magic links");
1028
+ }
1029
+ const allowedRedirectPaths = pluginConfig2.crossDeviceConfirmationBypass?.allowedRedirectPaths ?? [];
1030
+ if (allowedRedirectPaths.length === 0) {
1031
+ throw new Error("crossDeviceConfirmationBypass.allowedRedirectPaths must be configured");
1032
+ }
1033
+ if (!allowedRedirectPaths.includes(redirectToPath)) {
1034
+ throw new Error(`redirectToPath is not allowed for confirmation bypass: ${redirectToPath}`);
1035
+ }
1036
+ }
1037
+ function getMagicLinkBootstrapParams(input) {
1038
+ return {
1039
+ appKey: input.appKey,
1040
+ apiDomain: input.apiDomain,
1041
+ apiBasePath: input.apiBasePath,
1042
+ ...input.appVariantId ? { appVariantId: input.appVariantId } : {},
1043
+ ...input.displayContext ? { displayContext: input.displayContext } : {},
1044
+ ...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
1045
+ ...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
1046
+ };
1047
+ }
1048
+ function rewriteMagicLink(input) {
1049
+ return rewriteLinkToBaseUrl(
1050
+ input.magicLink,
1051
+ HUB_LOGIN_PAGE_PATH,
1052
+ input.clientDomain,
1053
+ input.bootstrapParams
1054
+ );
1055
+ }
946
1056
  function getRequestedAppVariantIdFromRequest(req) {
947
1057
  return req.getKeyValueFromQuery("app_variant_id");
948
1058
  }
@@ -961,12 +1071,19 @@ function getRequestedRedirectToPathFromRequest(req) {
961
1071
 
962
1072
  // src/config.ts
963
1073
  var pluginConfig;
1074
+ var superTokensConfig;
964
1075
  function setPluginConfig(config2) {
965
1076
  pluginConfig = config2;
966
1077
  }
967
1078
  function getPluginConfig() {
968
1079
  return pluginConfig;
969
1080
  }
1081
+ function setSuperTokensConfig(config2) {
1082
+ superTokensConfig = config2;
1083
+ }
1084
+ function getSuperTokensConfig() {
1085
+ return superTokensConfig;
1086
+ }
970
1087
  function assertRowndAppVariantIsConfigured(appVariantId) {
971
1088
  if (!appVariantId) {
972
1089
  return;
@@ -1047,7 +1164,10 @@ function buildSignInMethodsConfig(methodsArray) {
1047
1164
  },
1048
1165
  apple: {
1049
1166
  enabled: !!appleMethod,
1050
- client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
1167
+ client_id: getStringMethodProperty(appleMethod, "clientId") ?? "",
1168
+ ...getStringMethodProperty(appleMethod, "webClientType") !== void 0 ? { web_client_type: getStringMethodProperty(appleMethod, "webClientType") } : {},
1169
+ ...getStringMethodProperty(appleMethod, "iosClientType") !== void 0 ? { ios_client_type: getStringMethodProperty(appleMethod, "iosClientType") } : {},
1170
+ ...getStringMethodProperty(appleMethod, "androidClientType") !== void 0 ? { android_client_type: getStringMethodProperty(appleMethod, "androidClientType") } : {}
1051
1171
  },
1052
1172
  anonymous: {
1053
1173
  enabled: !!anonymousMethod && anonymousType !== "instant",
@@ -1321,6 +1441,7 @@ function buildAuthMobileConfig(authMobile) {
1321
1441
 
1322
1442
  // src/rownd-compatibility.ts
1323
1443
  import SuperTokens from "supertokens-node";
1444
+ import UserMetadata2 from "supertokens-node/recipe/usermetadata";
1324
1445
  var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
1325
1446
  "user_id",
1326
1447
  "email",
@@ -1495,6 +1616,140 @@ function buildRowndSessionClaimPayload(input) {
1495
1616
  ...anonymousId ? { anonymous_id: anonymousId } : {}
1496
1617
  };
1497
1618
  }
1619
+ function normalizeRowndOAuthScopes(scopes) {
1620
+ return [...new Set(scopes.filter((scope) => scope.length > 0))];
1621
+ }
1622
+ function getRowndOAuthAudience(input) {
1623
+ const requested = input.requestedResource ?? input.requestedAudience;
1624
+ if (requested?.startsWith("app:")) {
1625
+ return requested;
1626
+ }
1627
+ return void 0;
1628
+ }
1629
+ function applyRowndOAuthResourceParams(input) {
1630
+ const resource = firstString(input.params.resource);
1631
+ const audience = firstString(input.params.audience);
1632
+ const rowndAudience = getRowndOAuthAudience({
1633
+ requestedResource: resource,
1634
+ requestedAudience: audience
1635
+ });
1636
+ if (!rowndAudience) {
1637
+ return;
1638
+ }
1639
+ input.userContext.rowndOAuthAudience = rowndAudience;
1640
+ input.params.audience = audience ?? rowndAudience;
1641
+ delete input.params.resource;
1642
+ }
1643
+ async function buildRowndOAuthPayload(input) {
1644
+ const currentPayload = input.currentPayload ?? {};
1645
+ const standardClaims = input.user ? await buildStandardOAuthClaims(input.user, input.scopes) : {};
1646
+ const rowndClaims = input.user ? await buildRowndOAuthSessionClaims(input.user, currentPayload) : {};
1647
+ const audience = getRowndOAuthAudience({
1648
+ requestedAudience: typeof input.userContext?.rowndOAuthAudience === "string" ? input.userContext.rowndOAuthAudience : void 0
1649
+ });
1650
+ return {
1651
+ ...currentPayload,
1652
+ ...standardClaims,
1653
+ ...rowndClaims,
1654
+ ...audience ? { aud: audience } : {}
1655
+ };
1656
+ }
1657
+ async function buildRowndOAuthUserInfo(input) {
1658
+ const standardClaims = await buildStandardOAuthClaims(
1659
+ input.user,
1660
+ input.scopes
1661
+ );
1662
+ const rowndClaims = pickOAuthUserInfoRowndClaims(input.accessTokenPayload);
1663
+ return {
1664
+ ...input.currentPayload,
1665
+ ...standardClaims,
1666
+ ...rowndClaims
1667
+ };
1668
+ }
1669
+ async function buildStandardOAuthClaims(user, scopes) {
1670
+ const claims = {};
1671
+ const metadata = await getRowndMetadata(user.id);
1672
+ const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
1673
+ const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
1674
+ if (scopes.includes("email")) {
1675
+ const email = firstString(rowndData.email) ?? user.emails[0];
1676
+ if (email) {
1677
+ claims.email = email;
1678
+ claims.email_verified = isOAuthClaimVerified(
1679
+ verifiedData.email,
1680
+ email,
1681
+ user.loginMethods.some(
1682
+ (method) => method.hasSameEmailAs(email) && method.verified
1683
+ )
1684
+ );
1685
+ }
1686
+ }
1687
+ if (scopes.includes("phone")) {
1688
+ const phoneNumber = firstString(rowndData.phone_number) ?? user.phoneNumbers[0];
1689
+ if (phoneNumber) {
1690
+ claims.phone_number = phoneNumber;
1691
+ claims.phone_number_verified = isOAuthClaimVerified(
1692
+ verifiedData.phone_number,
1693
+ phoneNumber,
1694
+ user.loginMethods.some(
1695
+ (method) => method.hasSamePhoneNumberAs(phoneNumber) && method.verified
1696
+ )
1697
+ );
1698
+ }
1699
+ }
1700
+ if (scopes.includes("profile")) {
1701
+ const givenName = firstString(rowndData.first_name);
1702
+ const familyName = firstString(rowndData.last_name);
1703
+ const name = [givenName, familyName].filter(Boolean).join(" ");
1704
+ if (name) claims.name = name;
1705
+ if (givenName) claims.given_name = givenName;
1706
+ if (familyName) claims.family_name = familyName;
1707
+ if (typeof metadata.original_rownd_user?.data?.updated_at === "string") {
1708
+ claims.updated_at = metadata.original_rownd_user.data.updated_at;
1709
+ }
1710
+ }
1711
+ return claims;
1712
+ }
1713
+ async function getRowndMetadata(userId) {
1714
+ const metadata = await UserMetadata2.getUserMetadata(userId);
1715
+ return metadata.metadata || {};
1716
+ }
1717
+ function pickOAuthUserInfoRowndClaims(payload) {
1718
+ const claims = {};
1719
+ for (const key of [
1720
+ "app_user_id",
1721
+ "auth_level",
1722
+ "is_verified_user",
1723
+ "is_anonymous",
1724
+ "anonymous_id",
1725
+ "https://auth.rownd.io/app_user_id",
1726
+ "https://auth.rownd.io/auth_level",
1727
+ "https://auth.rownd.io/is_verified_user",
1728
+ "https://auth.rownd.io/is_anonymous"
1729
+ ]) {
1730
+ if (payload[key] !== void 0) {
1731
+ claims[key] = payload[key];
1732
+ }
1733
+ }
1734
+ return claims;
1735
+ }
1736
+ function firstString(value) {
1737
+ if (Array.isArray(value)) {
1738
+ return value.find((entry) => typeof entry === "string");
1739
+ }
1740
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1741
+ }
1742
+ function isOAuthClaimVerified(value, expectedValue, fallback) {
1743
+ return value === true || value === expectedValue || fallback;
1744
+ }
1745
+ async function buildRowndOAuthSessionClaims(user, currentPayload) {
1746
+ return buildRowndSessionClaimPayload({
1747
+ userId: user.id,
1748
+ user,
1749
+ metadata: await getRowndMetadata(user.id),
1750
+ currentPayload
1751
+ });
1752
+ }
1498
1753
  async function shouldLinkRowndAccounts(input) {
1499
1754
  const [newAccountInfo, , session] = input;
1500
1755
  if (!session) {
@@ -1596,12 +1851,12 @@ function hasVerifiedRealLoginMethod(user) {
1596
1851
  });
1597
1852
  }
1598
1853
  function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
1599
- if (originalAuthLevel === INSTANT_AUTH_METHOD_ID) {
1600
- return INSTANT_AUTH_METHOD_ID;
1601
- }
1602
1854
  if (hasVerifiedRealLoginMethod(user)) {
1603
1855
  return "verified";
1604
1856
  }
1857
+ if (originalAuthLevel === INSTANT_AUTH_METHOD_ID) {
1858
+ return INSTANT_AUTH_METHOD_ID;
1859
+ }
1605
1860
  return getGuestAuthLevel(user) || originalAuthLevel || (verifiedData && Object.keys(verifiedData).length > 0 ? "verified" : "unverified");
1606
1861
  }
1607
1862
  function canUpdateUserDataField(field) {
@@ -1642,7 +1897,7 @@ import EmailVerification from "supertokens-node/recipe/emailverification";
1642
1897
  import Passwordless from "supertokens-node/recipe/passwordless";
1643
1898
  import Session from "supertokens-node/recipe/session";
1644
1899
  import { BooleanClaim } from "supertokens-node/recipe/session/claims";
1645
- import UserMetadata2 from "supertokens-node/recipe/usermetadata";
1900
+ import UserMetadata3 from "supertokens-node/recipe/usermetadata";
1646
1901
  async function importUser(stUser, config2) {
1647
1902
  const headers = {
1648
1903
  "Content-Type": "application/json"
@@ -1681,7 +1936,7 @@ async function recordRowndAppVariantForUser(userId, appVariantId) {
1681
1936
  if (appVariants.includes(appVariantId)) {
1682
1937
  return;
1683
1938
  }
1684
- await UserMetadata2.updateUserMetadata(userId, {
1939
+ await UserMetadata3.updateUserMetadata(userId, {
1685
1940
  ...metadata,
1686
1941
  original_rownd_user: {
1687
1942
  ...originalRowndUser,
@@ -1713,8 +1968,78 @@ async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId
1713
1968
  appVariantId
1714
1969
  });
1715
1970
  }
1971
+ async function createMagicLinkWithConfirmationBypass(input) {
1972
+ const hasEmail = typeof input.email === "string" && input.email.length > 0;
1973
+ const hasPhoneNumber = typeof input.phoneNumber === "string" && input.phoneNumber.length > 0;
1974
+ if (hasEmail === hasPhoneNumber) {
1975
+ throw new Error("Exactly one of email or phoneNumber is required");
1976
+ }
1977
+ const stConfig = getSuperTokensConfig();
1978
+ if (!stConfig) {
1979
+ throw new Error("SuperTokens config is not initialized");
1980
+ }
1981
+ const pluginConfig2 = getPluginConfig();
1982
+ if (!pluginConfig2) {
1983
+ throw new Error("Rownd plugin config is not initialized");
1984
+ }
1985
+ const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
1986
+ const appVariantId = input.appVariantId;
1987
+ assertRowndAppVariantIsConfigured(appVariantId);
1988
+ const clientDomain = resolveAllowedClientDomain({
1989
+ clientDomain: input.clientDomain,
1990
+ pluginConfig: pluginConfig2,
1991
+ stConfig,
1992
+ request: input.request,
1993
+ userContext: input.userContext
1994
+ });
1995
+ const redirectToPath = normalizeRedirectToPathForClientDomain(input.redirectToPath, clientDomain);
1996
+ assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath);
1997
+ const userContext = {
1998
+ ...input.userContext ?? {},
1999
+ ...input.displayContext ? { rowndDisplayContext: input.displayContext } : {},
2000
+ rowndRedirectToPath: redirectToPath,
2001
+ ...input.clientDomain ? { rowndClientDomain: input.clientDomain } : {},
2002
+ ...appVariantId ? { rowndAppVariantId: appVariantId } : {}
2003
+ };
2004
+ const codeInfo = hasEmail ? await Passwordless.createCode({
2005
+ email: input.email,
2006
+ tenantId,
2007
+ session: input.session,
2008
+ userContext
2009
+ }) : await Passwordless.createCode({
2010
+ phoneNumber: input.phoneNumber,
2011
+ tenantId,
2012
+ session: input.session,
2013
+ userContext
2014
+ });
2015
+ if (codeInfo.status !== "OK") {
2016
+ throw new Error("Failed to create magic link");
2017
+ }
2018
+ const magicLink = `${getWebsiteDomain({
2019
+ stConfig,
2020
+ request: input.request,
2021
+ userContext
2022
+ })}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
2023
+ codeInfo.preAuthSessionId
2024
+ )}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
2025
+ const rewrittenUrl = new URL(rewriteMagicLink({
2026
+ magicLink,
2027
+ clientDomain,
2028
+ bootstrapParams: getMagicLinkBootstrapParams({
2029
+ appKey: pluginConfig2.rowndAppKey,
2030
+ apiDomain: getAppInfoString(stConfig.appInfo.apiDomain),
2031
+ apiBasePath: getAppInfoString(stConfig.appInfo.apiBasePath),
2032
+ appVariantId,
2033
+ displayContext: input.displayContext,
2034
+ redirectToPath,
2035
+ clientDomainKey: input.clientDomain
2036
+ })
2037
+ }));
2038
+ rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
2039
+ return rewrittenUrl.toString();
2040
+ }
1716
2041
  async function getUserMetadata(userId) {
1717
- const metadata = await UserMetadata2.getUserMetadata(userId);
2042
+ const metadata = await UserMetadata3.getUserMetadata(userId);
1718
2043
  return metadata.metadata || {};
1719
2044
  }
1720
2045
  function getPendingVerifications(metadata) {
@@ -1871,7 +2196,7 @@ async function updateUserData(userId, inputData) {
1871
2196
  ...metadata,
1872
2197
  ...inputData
1873
2198
  };
1874
- await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
2199
+ await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
1875
2200
  return getUserById(userId);
1876
2201
  }
1877
2202
  async function startPendingEmailVerification(input) {
@@ -1891,7 +2216,7 @@ async function startPendingEmailVerification(input) {
1891
2216
  );
1892
2217
  }
1893
2218
  if (pendingEmailVerifications.length > 0) {
1894
- await UserMetadata2.updateUserMetadata(input.userId, {
2219
+ await UserMetadata3.updateUserMetadata(input.userId, {
1895
2220
  ...metadata,
1896
2221
  rownd_pending_verification: pendingVerifications.filter(
1897
2222
  (pendingVerification2) => pendingVerification2.field !== "email"
@@ -1914,7 +2239,7 @@ async function startPendingEmailVerification(input) {
1914
2239
  value: input.email,
1915
2240
  created_at: (/* @__PURE__ */ new Date()).toISOString()
1916
2241
  };
1917
- await UserMetadata2.updateUserMetadata(input.userId, {
2242
+ await UserMetadata3.updateUserMetadata(input.userId, {
1918
2243
  ...metadata,
1919
2244
  rownd_pending_verification: [
1920
2245
  ...pendingVerifications.filter(
@@ -1960,6 +2285,7 @@ async function completePendingEmailVerification(input) {
1960
2285
  return;
1961
2286
  }
1962
2287
  let metadataUserId = userId;
2288
+ let verifiedRecipeUserId = input.recipeUserId;
1963
2289
  const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
1964
2290
  if (passwordlessEmailMethod) {
1965
2291
  const updateResult = await Passwordless.updateUser({
@@ -1972,6 +2298,7 @@ async function completePendingEmailVerification(input) {
1972
2298
  `Failed to update verified email method: ${updateResult.status}`
1973
2299
  );
1974
2300
  }
2301
+ verifiedRecipeUserId = passwordlessEmailMethod.recipeUserId;
1975
2302
  } else if (hasOnlyGuestLoginMethods(user)) {
1976
2303
  const isPasswordlessSignUpAllowed = await AccountLinking.isSignUpAllowed(
1977
2304
  PUBLIC_TENANT_ID,
@@ -1991,6 +2318,7 @@ async function completePendingEmailVerification(input) {
1991
2318
  tenantId: PUBLIC_TENANT_ID,
1992
2319
  userContext: input.userContext
1993
2320
  });
2321
+ verifiedRecipeUserId = passwordlessUser.recipeUserId;
1994
2322
  const primaryUserResult = await AccountLinking.createPrimaryUser(
1995
2323
  passwordlessUser.recipeUserId,
1996
2324
  input.userContext
@@ -2035,7 +2363,11 @@ async function completePendingEmailVerification(input) {
2035
2363
  )
2036
2364
  )
2037
2365
  };
2038
- await UserMetadata2.updateUserMetadata(metadataUserId, updatedMetadata);
2366
+ await UserMetadata3.updateUserMetadata(metadataUserId, updatedMetadata);
2367
+ return {
2368
+ userId: metadataUserId,
2369
+ recipeUserId: verifiedRecipeUserId
2370
+ };
2039
2371
  }
2040
2372
  function isMatchingPendingEmailVerification(verification, email) {
2041
2373
  return verification.field === "email" && verification.value === email;
@@ -2046,7 +2378,7 @@ async function updateUserMetadata(userId, inputMeta) {
2046
2378
  ...metadata,
2047
2379
  ...inputMeta
2048
2380
  };
2049
- await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
2381
+ await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
2050
2382
  return {
2051
2383
  id: userId,
2052
2384
  meta: Object.fromEntries(
@@ -2067,6 +2399,9 @@ import { randomUUID } from "crypto";
2067
2399
  import SuperTokens3 from "supertokens-node";
2068
2400
  import Session2 from "supertokens-node/recipe/session";
2069
2401
  import ThirdParty from "supertokens-node/recipe/thirdparty";
2402
+ function isBodyString(body, key) {
2403
+ return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
2404
+ }
2070
2405
  function handleGetAppConfig(deps) {
2071
2406
  return async (req) => {
2072
2407
  const appVariantId = getRequestedAppVariantIdFromRequest(req);
@@ -2087,6 +2422,43 @@ function handleGetAppConfig(deps) {
2087
2422
  };
2088
2423
  };
2089
2424
  }
2425
+ function handleValidatePasswordlessConfirmationBypass(deps) {
2426
+ return async (req) => {
2427
+ try {
2428
+ const body = await getJsonBody(req);
2429
+ const clientDomain = isBodyString(body, "clientDomain") ? body.clientDomain : void 0;
2430
+ const redirectToPath = isBodyString(body, "redirectToPath") ? body.redirectToPath : void 0;
2431
+ const appVariantId = isBodyString(body, "appVariantId") ? body.appVariantId : void 0;
2432
+ assertRowndAppVariantIsConfigured(appVariantId);
2433
+ const resolvedClientDomain = resolveAllowedClientDomain({
2434
+ clientDomain,
2435
+ pluginConfig: deps.pluginConfig,
2436
+ stConfig: deps.stConfig,
2437
+ request: req
2438
+ });
2439
+ const normalizedRedirectToPath = normalizeRedirectToPathForClientDomain(
2440
+ redirectToPath,
2441
+ resolvedClientDomain
2442
+ );
2443
+ assertAllowedBypassRedirectPath(
2444
+ deps.pluginConfig,
2445
+ normalizedRedirectToPath
2446
+ );
2447
+ return {
2448
+ status: "OK",
2449
+ bypass: true
2450
+ };
2451
+ } catch (error) {
2452
+ logDebugMessage(
2453
+ `Passwordless confirmation bypass validation failed. Error: ${getErrorMessage(error)}`
2454
+ );
2455
+ return {
2456
+ status: "ERROR",
2457
+ bypass: false
2458
+ };
2459
+ }
2460
+ };
2461
+ }
2090
2462
  function handleGuestLogin(deps) {
2091
2463
  return async (req, res, _session, userContext) => {
2092
2464
  const startedAt = Date.now();
@@ -2424,6 +2796,12 @@ function validateWritableFields(fields) {
2424
2796
  }
2425
2797
 
2426
2798
  // src/plugin.ts
2799
+ var verifyRowndUserSessionOptions = {
2800
+ sessionRequired: true,
2801
+ overrideGlobalClaimValidators: (validators) => validators.filter((validator) => {
2802
+ return !("claim" in validator) || validator.claim.key !== EmailVerificationClaim.key;
2803
+ })
2804
+ };
2427
2805
  var init = createPluginInitFunction(
2428
2806
  (pluginConfig2) => {
2429
2807
  const rowndClient2 = createInstance({
@@ -2484,6 +2862,7 @@ var init = createPluginInitFunction(
2484
2862
  }
2485
2863
  },
2486
2864
  routeHandlers(stConfig) {
2865
+ setSuperTokensConfig(stConfig);
2487
2866
  const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
2488
2867
  hubBootstrapParams = {
2489
2868
  apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
@@ -2514,6 +2893,13 @@ var init = createPluginInitFunction(
2514
2893
  method: "post",
2515
2894
  handler: withRequestHandler(handleMigrate(routeHandlerDeps))
2516
2895
  },
2896
+ {
2897
+ path: `${apiBasePath}/plugin/passwordless-cross-device-confirmation/validate`,
2898
+ method: "post",
2899
+ handler: withRequestHandler(
2900
+ handleValidatePasswordlessConfirmationBypass(routeHandlerDeps)
2901
+ )
2902
+ },
2517
2903
  {
2518
2904
  path: `${apiBasePath}/plugin/migrate-session`,
2519
2905
  method: "post",
@@ -2531,13 +2917,13 @@ var init = createPluginInitFunction(
2531
2917
  {
2532
2918
  path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
2533
2919
  method: "get",
2534
- verifySessionOptions: { sessionRequired: true },
2920
+ verifySessionOptions: verifyRowndUserSessionOptions,
2535
2921
  handler: withRequestHandler(handleGetUser())
2536
2922
  },
2537
2923
  {
2538
2924
  path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
2539
2925
  method: "put",
2540
- verifySessionOptions: { sessionRequired: true },
2926
+ verifySessionOptions: verifyRowndUserSessionOptions,
2541
2927
  handler: withRequestHandler(handleUpdateUser())
2542
2928
  },
2543
2929
  {
@@ -2561,19 +2947,87 @@ var init = createPluginInitFunction(
2561
2947
  {
2562
2948
  path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
2563
2949
  method: "get",
2564
- verifySessionOptions: { sessionRequired: true },
2950
+ verifySessionOptions: verifyRowndUserSessionOptions,
2565
2951
  handler: withRequestHandler(handleGetUserField())
2566
2952
  },
2567
2953
  {
2568
2954
  path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
2569
2955
  method: "put",
2570
- verifySessionOptions: { sessionRequired: true },
2956
+ verifySessionOptions: verifyRowndUserSessionOptions,
2571
2957
  handler: withRequestHandler(handleUpdateUserField())
2572
2958
  }
2573
2959
  ]
2574
2960
  };
2575
2961
  },
2576
2962
  overrideMap: {
2963
+ oauth2provider: {
2964
+ recipeInitRequired: false,
2965
+ functions: (originalImplementation) => ({
2966
+ ...originalImplementation,
2967
+ getRequestedScopes: async (input) => {
2968
+ const scopes = await originalImplementation.getRequestedScopes(
2969
+ input
2970
+ );
2971
+ return normalizeRowndOAuthScopes(scopes);
2972
+ },
2973
+ buildAccessTokenPayload: async (input) => {
2974
+ const payload = await originalImplementation.buildAccessTokenPayload(
2975
+ input
2976
+ );
2977
+ return buildRowndOAuthPayload({
2978
+ user: input.user,
2979
+ client: input.client,
2980
+ scopes: input.scopes,
2981
+ currentPayload: payload,
2982
+ userContext: input.userContext
2983
+ });
2984
+ },
2985
+ buildIdTokenPayload: async (input) => {
2986
+ const payload = await originalImplementation.buildIdTokenPayload(
2987
+ input
2988
+ );
2989
+ return buildRowndOAuthPayload({
2990
+ user: input.user,
2991
+ client: input.client,
2992
+ scopes: input.scopes,
2993
+ currentPayload: payload,
2994
+ userContext: input.userContext
2995
+ });
2996
+ },
2997
+ buildUserInfo: async (input) => {
2998
+ const payload = await originalImplementation.buildUserInfo(input);
2999
+ return buildRowndOAuthUserInfo({
3000
+ user: input.user,
3001
+ accessTokenPayload: input.accessTokenPayload,
3002
+ scopes: input.scopes,
3003
+ currentPayload: payload
3004
+ });
3005
+ }
3006
+ }),
3007
+ apis: (originalImplementation) => ({
3008
+ ...originalImplementation,
3009
+ authGET: async (input) => {
3010
+ if (originalImplementation.authGET === void 0) {
3011
+ throw new Error("OAuth2Provider authGET is unavailable");
3012
+ }
3013
+ applyRowndOAuthResourceParams({
3014
+ params: input.params,
3015
+ userContext: input.userContext
3016
+ });
3017
+ return originalImplementation.authGET(input);
3018
+ },
3019
+ tokenPOST: async (input) => {
3020
+ if (originalImplementation.tokenPOST === void 0) {
3021
+ throw new Error("OAuth2Provider tokenPOST is unavailable");
3022
+ }
3023
+ applyRowndOAuthResourceParams({
3024
+ params: input.body,
3025
+ userContext: input.userContext
3026
+ });
3027
+ return originalImplementation.tokenPOST(input);
3028
+ }
3029
+ })
3030
+ },
2577
3031
  passwordless: {
2578
3032
  config: (config2) => {
2579
3033
  const originalEmailDeliveryOverride = config2.emailDelivery?.override;
@@ -2794,11 +3248,25 @@ var init = createPluginInitFunction(
2794
3248
  }
2795
3249
  const response = await originalImplementation.verifyEmailPOST(input);
2796
3250
  if (response.status === "OK") {
2797
- await completePendingEmailVerification({
3251
+ const verificationResult = await completePendingEmailVerification({
2798
3252
  recipeUserId: response.user.recipeUserId,
2799
3253
  email: response.user.email,
2800
3254
  userContext: input.userContext
2801
3255
  });
3256
+ const session = input.session;
3257
+ const shouldReplaceSession = session && verificationResult && (session.getUserId(input.userContext) !== verificationResult.userId || session.getRecipeUserId(input.userContext).getAsString() !== verificationResult.recipeUserId.getAsString());
3258
+ if (shouldReplaceSession) {
3259
+ await session.revokeSession(input.userContext);
3260
+ response.newSession = await Session3.createNewSession(
3261
+ input.options.req,
3262
+ input.options.res,
3263
+ session.getTenantId(input.userContext),
3264
+ verificationResult.recipeUserId,
3265
+ {},
3266
+ {},
3267
+ input.userContext
3268
+ );
3269
+ }
2802
3270
  }
2803
3271
  return response;
2804
3272
  }
@@ -2836,6 +3304,7 @@ var init = createPluginInitFunction(
2836
3304
  rowndAppSecret: config2.rowndAppSecret,
2837
3305
  enableDebugLogs: config2.enableDebugLogs,
2838
3306
  clientDomains: config2.clientDomains,
3307
+ crossDeviceConfirmationBypass: config2.crossDeviceConfirmationBypass,
2839
3308
  telemetry: config2.telemetry,
2840
3309
  schema: config2.schema,
2841
3310
  appConfig: config2.appConfig,
@@ -2863,6 +3332,7 @@ export {
2863
3332
  HUB_LOGIN_PAGE_PATH,
2864
3333
  HUB_VERIFY_EMAIL_PAGE_PATH,
2865
3334
  INSTANT_AUTH_METHOD_ID,
3335
+ PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM,
2866
3336
  PLUGIN_ID,
2867
3337
  PLUGIN_SDK_VERSION,
2868
3338
  PLUGIN_VERSION,
@@ -2870,6 +3340,7 @@ export {
2870
3340
  ROWND_JWT_CLAIMS,
2871
3341
  ROWND_PLUGIN_ERROR_MESSAGES,
2872
3342
  RowndPluginError,
3343
+ createMagicLinkWithConfirmationBypass,
2873
3344
  index_default as default,
2874
3345
  getRowndClient,
2875
3346
  init,