@supertokens-plugins/rownd-nodejs 0.3.0-beta.5 → 0.3.0-beta.7

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
@@ -667,7 +667,7 @@ var PLUGIN_SDK_VERSION = ["23.0.0", "23.0.1", ">=23.0.1"];
667
667
  var HANDLE_BASE_PATH = "/plugin/rownd";
668
668
  var PUBLIC_TENANT_ID = "public";
669
669
  var GUEST_AUTH_METHOD_ID = "guest";
670
- var ANONYMOUS_AUTH_METHOD_ID = "anonymous";
670
+ var INSTANT_AUTH_METHOD_ID = "instant";
671
671
  var ROWND_JWT_CLAIMS = {
672
672
  AppUserId: "https://auth.rownd.io/app_user_id",
673
673
  IsVerifiedUser: "https://auth.rownd.io/is_verified_user",
@@ -701,6 +701,7 @@ var DEFAULT_ROWND_SCHEMA = {
701
701
  };
702
702
  var HUB_LOGIN_PAGE_PATH = "/account/login";
703
703
  var HUB_VERIFY_EMAIL_PAGE_PATH = "/account/verify-email";
704
+ var PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM = "bypassDeviceConfirmation";
704
705
 
705
706
  // src/logger.ts
706
707
  var { logDebugMessage, enableDebugLogs } = buildLogger(
@@ -943,6 +944,109 @@ function getStringList(value) {
943
944
  function getErrorMessage(error) {
944
945
  return error instanceof Error ? error.message : "Unknown error";
945
946
  }
947
+ function getAppInfoString(value) {
948
+ return value.getAsStringDangerous();
949
+ }
950
+ function getWebsiteDomain(input) {
951
+ return getAppInfoString(input.stConfig.appInfo.getOrigin({
952
+ request: input.request,
953
+ userContext: input.userContext ?? {}
954
+ }));
955
+ }
956
+ function normalizeClientDomain(value) {
957
+ if (value.endsWith("://")) {
958
+ return value;
959
+ }
960
+ const parsed = new URL(value);
961
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
962
+ return parsed.origin;
963
+ }
964
+ return `${parsed.protocol}//${parsed.host}`;
965
+ }
966
+ function resolveClientDomain(input) {
967
+ if (!input.clientDomain) {
968
+ return input.websiteDomain;
969
+ }
970
+ const resolved = input.pluginConfig.clientDomains?.[input.clientDomain];
971
+ if (!resolved) {
972
+ throw new Error(`Unknown clientDomain key: ${input.clientDomain}`);
973
+ }
974
+ return resolved;
975
+ }
976
+ function resolveAllowedClientDomain(input) {
977
+ const websiteDomain = getWebsiteDomain(input);
978
+ const resolvedClientDomain = resolveClientDomain({
979
+ clientDomain: input.clientDomain,
980
+ pluginConfig: input.pluginConfig,
981
+ websiteDomain
982
+ });
983
+ const normalizedClientDomain = normalizeClientDomain(resolvedClientDomain);
984
+ const allowed = [
985
+ websiteDomain,
986
+ ...Object.values(input.pluginConfig.clientDomains ?? {})
987
+ ].map(normalizeClientDomain);
988
+ if (!allowed.includes(normalizedClientDomain)) {
989
+ throw new Error(`clientDomain is not allowed: ${resolvedClientDomain}`);
990
+ }
991
+ return normalizedClientDomain;
992
+ }
993
+ function normalizeRedirectToPathForClientDomain(redirectToPath, clientDomain) {
994
+ if (!redirectToPath) {
995
+ return void 0;
996
+ }
997
+ if (redirectToPath === "NATIVE_APP") {
998
+ return redirectToPath;
999
+ }
1000
+ if (redirectToPath.startsWith("//")) {
1001
+ throw new Error("redirectToPath cannot be schemaless");
1002
+ }
1003
+ const normalizedClientDomain = normalizeClientDomain(clientDomain);
1004
+ const redirectUrl = new URL(
1005
+ redirectToPath,
1006
+ normalizedClientDomain.endsWith("://") ? "http://localhost" : normalizedClientDomain
1007
+ );
1008
+ const hasExplicitScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(redirectToPath);
1009
+ if (hasExplicitScheme) {
1010
+ if (redirectUrl.protocol !== "http:" && redirectUrl.protocol !== "https:") {
1011
+ throw new Error("redirectToPath must be http(s) or relative");
1012
+ }
1013
+ if (redirectUrl.origin !== normalizedClientDomain) {
1014
+ throw new Error("redirectToPath must match clientDomain");
1015
+ }
1016
+ }
1017
+ return `${redirectUrl.pathname}${redirectUrl.search}${redirectUrl.hash}`;
1018
+ }
1019
+ function assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath) {
1020
+ if (!redirectToPath) {
1021
+ throw new Error("redirectToPath is required for confirmation bypass magic links");
1022
+ }
1023
+ const allowedRedirectPaths = pluginConfig2.crossDeviceConfirmationBypass?.allowedRedirectPaths ?? [];
1024
+ if (allowedRedirectPaths.length === 0) {
1025
+ throw new Error("crossDeviceConfirmationBypass.allowedRedirectPaths must be configured");
1026
+ }
1027
+ if (!allowedRedirectPaths.includes(redirectToPath)) {
1028
+ throw new Error(`redirectToPath is not allowed for confirmation bypass: ${redirectToPath}`);
1029
+ }
1030
+ }
1031
+ function getMagicLinkBootstrapParams(input) {
1032
+ return {
1033
+ appKey: input.appKey,
1034
+ apiDomain: input.apiDomain,
1035
+ apiBasePath: input.apiBasePath,
1036
+ ...input.appVariantId ? { appVariantId: input.appVariantId } : {},
1037
+ ...input.displayContext ? { displayContext: input.displayContext } : {},
1038
+ ...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
1039
+ ...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
1040
+ };
1041
+ }
1042
+ function rewriteMagicLink(input) {
1043
+ return rewriteLinkToBaseUrl(
1044
+ input.magicLink,
1045
+ HUB_LOGIN_PAGE_PATH,
1046
+ input.clientDomain,
1047
+ input.bootstrapParams
1048
+ );
1049
+ }
946
1050
  function getRequestedAppVariantIdFromRequest(req) {
947
1051
  return req.getKeyValueFromQuery("app_variant_id");
948
1052
  }
@@ -961,12 +1065,19 @@ function getRequestedRedirectToPathFromRequest(req) {
961
1065
 
962
1066
  // src/config.ts
963
1067
  var pluginConfig;
1068
+ var superTokensConfig;
964
1069
  function setPluginConfig(config2) {
965
1070
  pluginConfig = config2;
966
1071
  }
967
1072
  function getPluginConfig() {
968
1073
  return pluginConfig;
969
1074
  }
1075
+ function setSuperTokensConfig(config2) {
1076
+ superTokensConfig = config2;
1077
+ }
1078
+ function getSuperTokensConfig() {
1079
+ return superTokensConfig;
1080
+ }
970
1081
  function assertRowndAppVariantIsConfigured(appVariantId) {
971
1082
  if (!appVariantId) {
972
1083
  return;
@@ -1047,7 +1158,10 @@ function buildSignInMethodsConfig(methodsArray) {
1047
1158
  },
1048
1159
  apple: {
1049
1160
  enabled: !!appleMethod,
1050
- client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
1161
+ client_id: getStringMethodProperty(appleMethod, "clientId") ?? "",
1162
+ ...getStringMethodProperty(appleMethod, "webClientType") !== void 0 ? { web_client_type: getStringMethodProperty(appleMethod, "webClientType") } : {},
1163
+ ...getStringMethodProperty(appleMethod, "iosClientType") !== void 0 ? { ios_client_type: getStringMethodProperty(appleMethod, "iosClientType") } : {},
1164
+ ...getStringMethodProperty(appleMethod, "androidClientType") !== void 0 ? { android_client_type: getStringMethodProperty(appleMethod, "androidClientType") } : {}
1051
1165
  },
1052
1166
  anonymous: {
1053
1167
  enabled: !!anonymousMethod && anonymousType !== "instant",
@@ -1321,6 +1435,7 @@ function buildAuthMobileConfig(authMobile) {
1321
1435
 
1322
1436
  // src/rownd-compatibility.ts
1323
1437
  import SuperTokens from "supertokens-node";
1438
+ import UserMetadata2 from "supertokens-node/recipe/usermetadata";
1324
1439
  var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
1325
1440
  "user_id",
1326
1441
  "email",
@@ -1385,7 +1500,7 @@ function mapRowndUserToSuperTokens(rowndUser) {
1385
1500
  }
1386
1501
  let authLevel = rowndUser.auth_level;
1387
1502
  if (loginMethods.length === 0) {
1388
- const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : ANONYMOUS_AUTH_METHOD_ID;
1503
+ const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : INSTANT_AUTH_METHOD_ID;
1389
1504
  if (!authLevel) authLevel = thirdPartyId;
1390
1505
  loginMethods.push({
1391
1506
  recipeId: "thirdparty",
@@ -1457,11 +1572,10 @@ function getAnonymousId(userId, user, metadata) {
1457
1572
  if (typeof originalAnonymousId === "string") {
1458
1573
  return originalAnonymousId;
1459
1574
  }
1460
- const anonymousMethod = user?.loginMethods.find((loginMethod) => {
1461
- return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
1575
+ const guestMethod = user?.loginMethods.find((loginMethod) => {
1576
+ return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === GUEST_AUTH_METHOD_ID;
1462
1577
  });
1463
- const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
1464
- return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
1578
+ return guestMethod ? `anon_${user?.id || userId}` : void 0;
1465
1579
  }
1466
1580
  function buildRowndSessionClaimPayload(input) {
1467
1581
  const currentPayload = input.currentPayload ?? {};
@@ -1478,7 +1592,7 @@ function buildRowndSessionClaimPayload(input) {
1478
1592
  currentPayload,
1479
1593
  input.metadata
1480
1594
  );
1481
- const isAnonymous = currentPayload.is_anonymous === true || authLevel === GUEST_AUTH_METHOD_ID || authLevel === ANONYMOUS_AUTH_METHOD_ID;
1595
+ const isAnonymous = currentPayload.is_anonymous === true || [GUEST_AUTH_METHOD_ID, INSTANT_AUTH_METHOD_ID].includes(authLevel);
1482
1596
  const anonymousId = getAnonymousId(input.userId, input.user, input.metadata);
1483
1597
  const isVerifiedUser = authLevel !== "unverified";
1484
1598
  const audience = buildRowndAudience(currentPayload, input.appVariantId);
@@ -1492,10 +1606,144 @@ function buildRowndSessionClaimPayload(input) {
1492
1606
  [ROWND_JWT_CLAIMS.AppUserId]: appUserId,
1493
1607
  [ROWND_JWT_CLAIMS.AuthLevel]: authLevel,
1494
1608
  [ROWND_JWT_CLAIMS.IsVerifiedUser]: isVerifiedUser,
1495
- [ROWND_JWT_CLAIMS.IsAnonymous]: isAnonymous,
1609
+ ...isAnonymous ? { [ROWND_JWT_CLAIMS.IsAnonymous]: true } : {},
1496
1610
  ...anonymousId ? { anonymous_id: anonymousId } : {}
1497
1611
  };
1498
1612
  }
1613
+ function normalizeRowndOAuthScopes(scopes) {
1614
+ return [...new Set(scopes.filter((scope) => scope.length > 0))];
1615
+ }
1616
+ function getRowndOAuthAudience(input) {
1617
+ const requested = input.requestedResource ?? input.requestedAudience;
1618
+ if (requested?.startsWith("app:")) {
1619
+ return requested;
1620
+ }
1621
+ return void 0;
1622
+ }
1623
+ function applyRowndOAuthResourceParams(input) {
1624
+ const resource = firstString(input.params.resource);
1625
+ const audience = firstString(input.params.audience);
1626
+ const rowndAudience = getRowndOAuthAudience({
1627
+ requestedResource: resource,
1628
+ requestedAudience: audience
1629
+ });
1630
+ if (!rowndAudience) {
1631
+ return;
1632
+ }
1633
+ input.userContext.rowndOAuthAudience = rowndAudience;
1634
+ input.params.audience = audience ?? rowndAudience;
1635
+ delete input.params.resource;
1636
+ }
1637
+ async function buildRowndOAuthPayload(input) {
1638
+ const currentPayload = input.currentPayload ?? {};
1639
+ const standardClaims = input.user ? await buildStandardOAuthClaims(input.user, input.scopes) : {};
1640
+ const rowndClaims = input.user ? await buildRowndOAuthSessionClaims(input.user, currentPayload) : {};
1641
+ const audience = getRowndOAuthAudience({
1642
+ requestedAudience: typeof input.userContext?.rowndOAuthAudience === "string" ? input.userContext.rowndOAuthAudience : void 0
1643
+ });
1644
+ return {
1645
+ ...currentPayload,
1646
+ ...standardClaims,
1647
+ ...rowndClaims,
1648
+ ...audience ? { aud: audience } : {}
1649
+ };
1650
+ }
1651
+ async function buildRowndOAuthUserInfo(input) {
1652
+ const standardClaims = await buildStandardOAuthClaims(
1653
+ input.user,
1654
+ input.scopes
1655
+ );
1656
+ const rowndClaims = pickOAuthUserInfoRowndClaims(input.accessTokenPayload);
1657
+ return {
1658
+ ...input.currentPayload,
1659
+ ...standardClaims,
1660
+ ...rowndClaims
1661
+ };
1662
+ }
1663
+ async function buildStandardOAuthClaims(user, scopes) {
1664
+ const claims = {};
1665
+ const metadata = await getRowndMetadata(user.id);
1666
+ const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
1667
+ const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
1668
+ if (scopes.includes("email")) {
1669
+ const email = firstString(rowndData.email) ?? user.emails[0];
1670
+ if (email) {
1671
+ claims.email = email;
1672
+ claims.email_verified = isOAuthClaimVerified(
1673
+ verifiedData.email,
1674
+ email,
1675
+ user.loginMethods.some(
1676
+ (method) => method.hasSameEmailAs(email) && method.verified
1677
+ )
1678
+ );
1679
+ }
1680
+ }
1681
+ if (scopes.includes("phone")) {
1682
+ const phoneNumber = firstString(rowndData.phone_number) ?? user.phoneNumbers[0];
1683
+ if (phoneNumber) {
1684
+ claims.phone_number = phoneNumber;
1685
+ claims.phone_number_verified = isOAuthClaimVerified(
1686
+ verifiedData.phone_number,
1687
+ phoneNumber,
1688
+ user.loginMethods.some(
1689
+ (method) => method.hasSamePhoneNumberAs(phoneNumber) && method.verified
1690
+ )
1691
+ );
1692
+ }
1693
+ }
1694
+ if (scopes.includes("profile")) {
1695
+ const givenName = firstString(rowndData.first_name);
1696
+ const familyName = firstString(rowndData.last_name);
1697
+ const name = [givenName, familyName].filter(Boolean).join(" ");
1698
+ if (name) claims.name = name;
1699
+ if (givenName) claims.given_name = givenName;
1700
+ if (familyName) claims.family_name = familyName;
1701
+ if (typeof metadata.original_rownd_user?.data?.updated_at === "string") {
1702
+ claims.updated_at = metadata.original_rownd_user.data.updated_at;
1703
+ }
1704
+ }
1705
+ return claims;
1706
+ }
1707
+ async function getRowndMetadata(userId) {
1708
+ const metadata = await UserMetadata2.getUserMetadata(userId);
1709
+ return metadata.metadata || {};
1710
+ }
1711
+ function pickOAuthUserInfoRowndClaims(payload) {
1712
+ const claims = {};
1713
+ for (const key of [
1714
+ "app_user_id",
1715
+ "auth_level",
1716
+ "is_verified_user",
1717
+ "is_anonymous",
1718
+ "anonymous_id",
1719
+ "https://auth.rownd.io/app_user_id",
1720
+ "https://auth.rownd.io/auth_level",
1721
+ "https://auth.rownd.io/is_verified_user",
1722
+ "https://auth.rownd.io/is_anonymous"
1723
+ ]) {
1724
+ if (payload[key] !== void 0) {
1725
+ claims[key] = payload[key];
1726
+ }
1727
+ }
1728
+ return claims;
1729
+ }
1730
+ function firstString(value) {
1731
+ if (Array.isArray(value)) {
1732
+ return value.find((entry) => typeof entry === "string");
1733
+ }
1734
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1735
+ }
1736
+ function isOAuthClaimVerified(value, expectedValue, fallback) {
1737
+ return value === true || value === expectedValue || fallback;
1738
+ }
1739
+ async function buildRowndOAuthSessionClaims(user, currentPayload) {
1740
+ return buildRowndSessionClaimPayload({
1741
+ userId: user.id,
1742
+ user,
1743
+ metadata: await getRowndMetadata(user.id),
1744
+ currentPayload
1745
+ });
1746
+ }
1499
1747
  async function shouldLinkRowndAccounts(input) {
1500
1748
  const [newAccountInfo, , session] = input;
1501
1749
  if (!session) {
@@ -1538,23 +1786,26 @@ function getThirdPartyUserId(method) {
1538
1786
  return method.thirdParty?.userId;
1539
1787
  }
1540
1788
  function getGuestAuthLevel(user) {
1541
- const guestMethod = user?.loginMethods.find(isGuestLoginMethod);
1542
- return guestMethod ? GUEST_AUTH_METHOD_ID : void 0;
1543
- }
1544
- function hasAnonymousLoginMethod(user) {
1545
- return !!user?.loginMethods.some((loginMethod) => {
1546
- return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
1789
+ const guestMethod = user?.loginMethods.find((method) => {
1790
+ return method.recipeId === "thirdparty" && getThirdPartyId(method) === GUEST_AUTH_METHOD_ID;
1547
1791
  });
1792
+ if (guestMethod) {
1793
+ return GUEST_AUTH_METHOD_ID;
1794
+ }
1795
+ const instantMethod = user?.loginMethods.find((method) => {
1796
+ return method.recipeId === "thirdparty" && getThirdPartyId(method) === INSTANT_AUTH_METHOD_ID;
1797
+ });
1798
+ return instantMethod ? INSTANT_AUTH_METHOD_ID : void 0;
1548
1799
  }
1549
1800
  function hasOnlyGuestLoginMethods(user) {
1550
1801
  return !!user?.loginMethods.length && user.loginMethods.every(isGuestLoginMethod);
1551
1802
  }
1552
1803
  function isGuestLoginMethod(method) {
1553
1804
  const thirdPartyId = getThirdPartyId(method);
1554
- return method.recipeId === "thirdparty" && (thirdPartyId === GUEST_AUTH_METHOD_ID || thirdPartyId === ANONYMOUS_AUTH_METHOD_ID);
1805
+ return method.recipeId === "thirdparty" && (thirdPartyId === GUEST_AUTH_METHOD_ID || thirdPartyId === INSTANT_AUTH_METHOD_ID);
1555
1806
  }
1556
1807
  function isGuestAccountInfo(input) {
1557
- return input?.recipeId === "thirdparty" && (input.thirdParty?.id === GUEST_AUTH_METHOD_ID || input.thirdParty?.id === ANONYMOUS_AUTH_METHOD_ID);
1808
+ return input?.recipeId === "thirdparty" && (input.thirdParty?.id === GUEST_AUTH_METHOD_ID || input.thirdParty?.id === INSTANT_AUTH_METHOD_ID);
1558
1809
  }
1559
1810
  function doesAccountInfoMatchAuthMethod(user, accountInfo) {
1560
1811
  if (!user) {
@@ -1594,8 +1845,8 @@ function hasVerifiedRealLoginMethod(user) {
1594
1845
  });
1595
1846
  }
1596
1847
  function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
1597
- if (originalAuthLevel === ANONYMOUS_AUTH_METHOD_ID) {
1598
- return ANONYMOUS_AUTH_METHOD_ID;
1848
+ if (originalAuthLevel === INSTANT_AUTH_METHOD_ID) {
1849
+ return INSTANT_AUTH_METHOD_ID;
1599
1850
  }
1600
1851
  if (hasVerifiedRealLoginMethod(user)) {
1601
1852
  return "verified";
@@ -1640,7 +1891,7 @@ import EmailVerification from "supertokens-node/recipe/emailverification";
1640
1891
  import Passwordless from "supertokens-node/recipe/passwordless";
1641
1892
  import Session from "supertokens-node/recipe/session";
1642
1893
  import { BooleanClaim } from "supertokens-node/recipe/session/claims";
1643
- import UserMetadata2 from "supertokens-node/recipe/usermetadata";
1894
+ import UserMetadata3 from "supertokens-node/recipe/usermetadata";
1644
1895
  async function importUser(stUser, config2) {
1645
1896
  const headers = {
1646
1897
  "Content-Type": "application/json"
@@ -1679,7 +1930,7 @@ async function recordRowndAppVariantForUser(userId, appVariantId) {
1679
1930
  if (appVariants.includes(appVariantId)) {
1680
1931
  return;
1681
1932
  }
1682
- await UserMetadata2.updateUserMetadata(userId, {
1933
+ await UserMetadata3.updateUserMetadata(userId, {
1683
1934
  ...metadata,
1684
1935
  original_rownd_user: {
1685
1936
  ...originalRowndUser,
@@ -1697,7 +1948,7 @@ var RowndIsAnonymousClaim = new BooleanClaim({
1697
1948
  fetchValue: async (userId) => {
1698
1949
  const user = await SuperTokens2.getUser(userId);
1699
1950
  const effectiveAuthLevel = getEffectiveAuthLevel(user);
1700
- return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
1951
+ return [GUEST_AUTH_METHOD_ID, INSTANT_AUTH_METHOD_ID].includes(effectiveAuthLevel);
1701
1952
  }
1702
1953
  });
1703
1954
  async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
@@ -1711,8 +1962,78 @@ async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId
1711
1962
  appVariantId
1712
1963
  });
1713
1964
  }
1965
+ async function createMagicLinkWithConfirmationBypass(input) {
1966
+ const hasEmail = typeof input.email === "string" && input.email.length > 0;
1967
+ const hasPhoneNumber = typeof input.phoneNumber === "string" && input.phoneNumber.length > 0;
1968
+ if (hasEmail === hasPhoneNumber) {
1969
+ throw new Error("Exactly one of email or phoneNumber is required");
1970
+ }
1971
+ const stConfig = getSuperTokensConfig();
1972
+ if (!stConfig) {
1973
+ throw new Error("SuperTokens config is not initialized");
1974
+ }
1975
+ const pluginConfig2 = getPluginConfig();
1976
+ if (!pluginConfig2) {
1977
+ throw new Error("Rownd plugin config is not initialized");
1978
+ }
1979
+ const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
1980
+ const appVariantId = input.appVariantId;
1981
+ assertRowndAppVariantIsConfigured(appVariantId);
1982
+ const clientDomain = resolveAllowedClientDomain({
1983
+ clientDomain: input.clientDomain,
1984
+ pluginConfig: pluginConfig2,
1985
+ stConfig,
1986
+ request: input.request,
1987
+ userContext: input.userContext
1988
+ });
1989
+ const redirectToPath = normalizeRedirectToPathForClientDomain(input.redirectToPath, clientDomain);
1990
+ assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath);
1991
+ const userContext = {
1992
+ ...input.userContext ?? {},
1993
+ ...input.displayContext ? { rowndDisplayContext: input.displayContext } : {},
1994
+ rowndRedirectToPath: redirectToPath,
1995
+ ...input.clientDomain ? { rowndClientDomain: input.clientDomain } : {},
1996
+ ...appVariantId ? { rowndAppVariantId: appVariantId } : {}
1997
+ };
1998
+ const codeInfo = hasEmail ? await Passwordless.createCode({
1999
+ email: input.email,
2000
+ tenantId,
2001
+ session: input.session,
2002
+ userContext
2003
+ }) : await Passwordless.createCode({
2004
+ phoneNumber: input.phoneNumber,
2005
+ tenantId,
2006
+ session: input.session,
2007
+ userContext
2008
+ });
2009
+ if (codeInfo.status !== "OK") {
2010
+ throw new Error("Failed to create magic link");
2011
+ }
2012
+ const magicLink = `${getWebsiteDomain({
2013
+ stConfig,
2014
+ request: input.request,
2015
+ userContext
2016
+ })}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
2017
+ codeInfo.preAuthSessionId
2018
+ )}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
2019
+ const rewrittenUrl = new URL(rewriteMagicLink({
2020
+ magicLink,
2021
+ clientDomain,
2022
+ bootstrapParams: getMagicLinkBootstrapParams({
2023
+ appKey: pluginConfig2.rowndAppKey,
2024
+ apiDomain: getAppInfoString(stConfig.appInfo.apiDomain),
2025
+ apiBasePath: getAppInfoString(stConfig.appInfo.apiBasePath),
2026
+ appVariantId,
2027
+ displayContext: input.displayContext,
2028
+ redirectToPath,
2029
+ clientDomainKey: input.clientDomain
2030
+ })
2031
+ }));
2032
+ rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
2033
+ return rewrittenUrl.toString();
2034
+ }
1714
2035
  async function getUserMetadata(userId) {
1715
- const metadata = await UserMetadata2.getUserMetadata(userId);
2036
+ const metadata = await UserMetadata3.getUserMetadata(userId);
1716
2037
  return metadata.metadata || {};
1717
2038
  }
1718
2039
  function getPendingVerifications(metadata) {
@@ -1869,7 +2190,7 @@ async function updateUserData(userId, inputData) {
1869
2190
  ...metadata,
1870
2191
  ...inputData
1871
2192
  };
1872
- await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
2193
+ await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
1873
2194
  return getUserById(userId);
1874
2195
  }
1875
2196
  async function startPendingEmailVerification(input) {
@@ -1889,7 +2210,7 @@ async function startPendingEmailVerification(input) {
1889
2210
  );
1890
2211
  }
1891
2212
  if (pendingEmailVerifications.length > 0) {
1892
- await UserMetadata2.updateUserMetadata(input.userId, {
2213
+ await UserMetadata3.updateUserMetadata(input.userId, {
1893
2214
  ...metadata,
1894
2215
  rownd_pending_verification: pendingVerifications.filter(
1895
2216
  (pendingVerification2) => pendingVerification2.field !== "email"
@@ -1912,7 +2233,7 @@ async function startPendingEmailVerification(input) {
1912
2233
  value: input.email,
1913
2234
  created_at: (/* @__PURE__ */ new Date()).toISOString()
1914
2235
  };
1915
- await UserMetadata2.updateUserMetadata(input.userId, {
2236
+ await UserMetadata3.updateUserMetadata(input.userId, {
1916
2237
  ...metadata,
1917
2238
  rownd_pending_verification: [
1918
2239
  ...pendingVerifications.filter(
@@ -2033,7 +2354,7 @@ async function completePendingEmailVerification(input) {
2033
2354
  )
2034
2355
  )
2035
2356
  };
2036
- await UserMetadata2.updateUserMetadata(metadataUserId, updatedMetadata);
2357
+ await UserMetadata3.updateUserMetadata(metadataUserId, updatedMetadata);
2037
2358
  }
2038
2359
  function isMatchingPendingEmailVerification(verification, email) {
2039
2360
  return verification.field === "email" && verification.value === email;
@@ -2044,7 +2365,7 @@ async function updateUserMetadata(userId, inputMeta) {
2044
2365
  ...metadata,
2045
2366
  ...inputMeta
2046
2367
  };
2047
- await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
2368
+ await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
2048
2369
  return {
2049
2370
  id: userId,
2050
2371
  meta: Object.fromEntries(
@@ -2065,6 +2386,9 @@ import { randomUUID } from "crypto";
2065
2386
  import SuperTokens3 from "supertokens-node";
2066
2387
  import Session2 from "supertokens-node/recipe/session";
2067
2388
  import ThirdParty from "supertokens-node/recipe/thirdparty";
2389
+ function isBodyString(body, key) {
2390
+ return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
2391
+ }
2068
2392
  function handleGetAppConfig(deps) {
2069
2393
  return async (req) => {
2070
2394
  const appVariantId = getRequestedAppVariantIdFromRequest(req);
@@ -2085,6 +2409,43 @@ function handleGetAppConfig(deps) {
2085
2409
  };
2086
2410
  };
2087
2411
  }
2412
+ function handleValidatePasswordlessConfirmationBypass(deps) {
2413
+ return async (req) => {
2414
+ try {
2415
+ const body = await getJsonBody(req);
2416
+ const clientDomain = isBodyString(body, "clientDomain") ? body.clientDomain : void 0;
2417
+ const redirectToPath = isBodyString(body, "redirectToPath") ? body.redirectToPath : void 0;
2418
+ const appVariantId = isBodyString(body, "appVariantId") ? body.appVariantId : void 0;
2419
+ assertRowndAppVariantIsConfigured(appVariantId);
2420
+ const resolvedClientDomain = resolveAllowedClientDomain({
2421
+ clientDomain,
2422
+ pluginConfig: deps.pluginConfig,
2423
+ stConfig: deps.stConfig,
2424
+ request: req
2425
+ });
2426
+ const normalizedRedirectToPath = normalizeRedirectToPathForClientDomain(
2427
+ redirectToPath,
2428
+ resolvedClientDomain
2429
+ );
2430
+ assertAllowedBypassRedirectPath(
2431
+ deps.pluginConfig,
2432
+ normalizedRedirectToPath
2433
+ );
2434
+ return {
2435
+ status: "OK",
2436
+ bypass: true
2437
+ };
2438
+ } catch (error) {
2439
+ logDebugMessage(
2440
+ `Passwordless confirmation bypass validation failed. Error: ${getErrorMessage(error)}`
2441
+ );
2442
+ return {
2443
+ status: "ERROR",
2444
+ bypass: false
2445
+ };
2446
+ }
2447
+ };
2448
+ }
2088
2449
  function handleGuestLogin(deps) {
2089
2450
  return async (req, res, _session, userContext) => {
2090
2451
  const startedAt = Date.now();
@@ -2093,9 +2454,9 @@ function handleGuestLogin(deps) {
2093
2454
  const body = parseGuestBody(await getJsonBody(req));
2094
2455
  const appVariantId = getRequestedAppVariantIdFromRequest(req);
2095
2456
  assertRowndAppVariantIsConfigured(appVariantId);
2096
- const thirdPartyId = body.authLevel === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
2097
- const thirdPartyUserId = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? `anon_${randomUUID()}` : guestId;
2098
- const authLevel = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
2457
+ const thirdPartyId = body.authLevel === INSTANT_AUTH_METHOD_ID ? INSTANT_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
2458
+ const thirdPartyUserId = thirdPartyId === INSTANT_AUTH_METHOD_ID ? `anon_${randomUUID()}` : guestId;
2459
+ const authLevel = thirdPartyId === INSTANT_AUTH_METHOD_ID ? INSTANT_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
2099
2460
  const response = await ThirdParty.manuallyCreateOrUpdateUser(
2100
2461
  PUBLIC_TENANT_ID,
2101
2462
  thirdPartyId,
@@ -2118,7 +2479,7 @@ function handleGuestLogin(deps) {
2118
2479
  {
2119
2480
  ...buildRowndAudience({}, appVariantId),
2120
2481
  auth_level: authLevel,
2121
- is_anonymous: true,
2482
+ ...[GUEST_AUTH_METHOD_ID, INSTANT_AUTH_METHOD_ID].includes(authLevel) ? { is_anonymous: true } : {},
2122
2483
  app_user_id: response.user.id
2123
2484
  },
2124
2485
  {},
@@ -2482,6 +2843,7 @@ var init = createPluginInitFunction(
2482
2843
  }
2483
2844
  },
2484
2845
  routeHandlers(stConfig) {
2846
+ setSuperTokensConfig(stConfig);
2485
2847
  const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
2486
2848
  hubBootstrapParams = {
2487
2849
  apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
@@ -2512,6 +2874,13 @@ var init = createPluginInitFunction(
2512
2874
  method: "post",
2513
2875
  handler: withRequestHandler(handleMigrate(routeHandlerDeps))
2514
2876
  },
2877
+ {
2878
+ path: `${apiBasePath}/plugin/passwordless-cross-device-confirmation/validate`,
2879
+ method: "post",
2880
+ handler: withRequestHandler(
2881
+ handleValidatePasswordlessConfirmationBypass(routeHandlerDeps)
2882
+ )
2883
+ },
2515
2884
  {
2516
2885
  path: `${apiBasePath}/plugin/migrate-session`,
2517
2886
  method: "post",
@@ -2572,6 +2941,74 @@ var init = createPluginInitFunction(
2572
2941
  };
2573
2942
  },
2574
2943
  overrideMap: {
2944
+ oauth2provider: {
2945
+ recipeInitRequired: false,
2946
+ functions: (originalImplementation) => ({
2947
+ ...originalImplementation,
2948
+ getRequestedScopes: async (input) => {
2949
+ const scopes = await originalImplementation.getRequestedScopes(
2950
+ input
2951
+ );
2952
+ return normalizeRowndOAuthScopes(scopes);
2953
+ },
2954
+ buildAccessTokenPayload: async (input) => {
2955
+ const payload = await originalImplementation.buildAccessTokenPayload(
2956
+ input
2957
+ );
2958
+ return buildRowndOAuthPayload({
2959
+ user: input.user,
2960
+ client: input.client,
2961
+ scopes: input.scopes,
2962
+ currentPayload: payload,
2963
+ userContext: input.userContext
2964
+ });
2965
+ },
2966
+ buildIdTokenPayload: async (input) => {
2967
+ const payload = await originalImplementation.buildIdTokenPayload(
2968
+ input
2969
+ );
2970
+ return buildRowndOAuthPayload({
2971
+ user: input.user,
2972
+ client: input.client,
2973
+ scopes: input.scopes,
2974
+ currentPayload: payload,
2975
+ userContext: input.userContext
2976
+ });
2977
+ },
2978
+ buildUserInfo: async (input) => {
2979
+ const payload = await originalImplementation.buildUserInfo(input);
2980
+ return buildRowndOAuthUserInfo({
2981
+ user: input.user,
2982
+ accessTokenPayload: input.accessTokenPayload,
2983
+ scopes: input.scopes,
2984
+ currentPayload: payload
2985
+ });
2986
+ }
2987
+ }),
2988
+ apis: (originalImplementation) => ({
2989
+ ...originalImplementation,
2990
+ authGET: async (input) => {
2991
+ if (originalImplementation.authGET === void 0) {
2992
+ throw new Error("OAuth2Provider authGET is unavailable");
2993
+ }
2994
+ applyRowndOAuthResourceParams({
2995
+ params: input.params,
2996
+ userContext: input.userContext
2997
+ });
2998
+ return originalImplementation.authGET(input);
2999
+ },
3000
+ tokenPOST: async (input) => {
3001
+ if (originalImplementation.tokenPOST === void 0) {
3002
+ throw new Error("OAuth2Provider tokenPOST is unavailable");
3003
+ }
3004
+ applyRowndOAuthResourceParams({
3005
+ params: input.body,
3006
+ userContext: input.userContext
3007
+ });
3008
+ return originalImplementation.tokenPOST(input);
3009
+ }
3010
+ })
3011
+ },
2575
3012
  passwordless: {
2576
3013
  config: (config2) => {
2577
3014
  const originalEmailDeliveryOverride = config2.emailDelivery?.override;
@@ -2834,6 +3271,7 @@ var init = createPluginInitFunction(
2834
3271
  rowndAppSecret: config2.rowndAppSecret,
2835
3272
  enableDebugLogs: config2.enableDebugLogs,
2836
3273
  clientDomains: config2.clientDomains,
3274
+ crossDeviceConfirmationBypass: config2.crossDeviceConfirmationBypass,
2837
3275
  telemetry: config2.telemetry,
2838
3276
  schema: config2.schema,
2839
3277
  appConfig: config2.appConfig,
@@ -2855,12 +3293,13 @@ function validateClientDomainUrl(key, value) {
2855
3293
  // src/index.ts
2856
3294
  var index_default = { init };
2857
3295
  export {
2858
- ANONYMOUS_AUTH_METHOD_ID,
2859
3296
  DEFAULT_ROWND_SCHEMA,
2860
3297
  GUEST_AUTH_METHOD_ID,
2861
3298
  HANDLE_BASE_PATH,
2862
3299
  HUB_LOGIN_PAGE_PATH,
2863
3300
  HUB_VERIFY_EMAIL_PAGE_PATH,
3301
+ INSTANT_AUTH_METHOD_ID,
3302
+ PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM,
2864
3303
  PLUGIN_ID,
2865
3304
  PLUGIN_SDK_VERSION,
2866
3305
  PLUGIN_VERSION,
@@ -2868,6 +3307,7 @@ export {
2868
3307
  ROWND_JWT_CLAIMS,
2869
3308
  ROWND_PLUGIN_ERROR_MESSAGES,
2870
3309
  RowndPluginError,
3310
+ createMagicLinkWithConfirmationBypass,
2871
3311
  index_default as default,
2872
3312
  getRowndClient,
2873
3313
  init,