@supertokens-plugins/rownd-nodejs 0.3.0-beta.6 → 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/README.md +58 -2
- package/dist/bulkMigrate.js +1 -0
- package/dist/generateAppConfig.js +4 -1
- package/dist/index.d.mts +38 -3
- package/dist/index.d.ts +38 -3
- package/dist/index.js +449 -9
- package/dist/index.mjs +447 -9
- package/dist/setupCoreInstance.js +51 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -273,6 +273,7 @@ __export(index_exports, {
|
|
|
273
273
|
HUB_LOGIN_PAGE_PATH: () => HUB_LOGIN_PAGE_PATH,
|
|
274
274
|
HUB_VERIFY_EMAIL_PAGE_PATH: () => HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
275
275
|
INSTANT_AUTH_METHOD_ID: () => INSTANT_AUTH_METHOD_ID,
|
|
276
|
+
PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM: () => PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM,
|
|
276
277
|
PLUGIN_ID: () => PLUGIN_ID,
|
|
277
278
|
PLUGIN_SDK_VERSION: () => PLUGIN_SDK_VERSION,
|
|
278
279
|
PLUGIN_VERSION: () => PLUGIN_VERSION,
|
|
@@ -280,6 +281,7 @@ __export(index_exports, {
|
|
|
280
281
|
ROWND_JWT_CLAIMS: () => ROWND_JWT_CLAIMS,
|
|
281
282
|
ROWND_PLUGIN_ERROR_MESSAGES: () => ROWND_PLUGIN_ERROR_MESSAGES,
|
|
282
283
|
RowndPluginError: () => RowndPluginError,
|
|
284
|
+
createMagicLinkWithConfirmationBypass: () => createMagicLinkWithConfirmationBypass,
|
|
283
285
|
default: () => index_default,
|
|
284
286
|
getRowndClient: () => getRowndClient,
|
|
285
287
|
init: () => init,
|
|
@@ -724,6 +726,7 @@ var DEFAULT_ROWND_SCHEMA = {
|
|
|
724
726
|
};
|
|
725
727
|
var HUB_LOGIN_PAGE_PATH = "/account/login";
|
|
726
728
|
var HUB_VERIFY_EMAIL_PAGE_PATH = "/account/verify-email";
|
|
729
|
+
var PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM = "bypassDeviceConfirmation";
|
|
727
730
|
|
|
728
731
|
// src/logger.ts
|
|
729
732
|
var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
@@ -966,6 +969,109 @@ function getStringList(value) {
|
|
|
966
969
|
function getErrorMessage(error) {
|
|
967
970
|
return error instanceof Error ? error.message : "Unknown error";
|
|
968
971
|
}
|
|
972
|
+
function getAppInfoString(value) {
|
|
973
|
+
return value.getAsStringDangerous();
|
|
974
|
+
}
|
|
975
|
+
function getWebsiteDomain(input) {
|
|
976
|
+
return getAppInfoString(input.stConfig.appInfo.getOrigin({
|
|
977
|
+
request: input.request,
|
|
978
|
+
userContext: input.userContext ?? {}
|
|
979
|
+
}));
|
|
980
|
+
}
|
|
981
|
+
function normalizeClientDomain(value) {
|
|
982
|
+
if (value.endsWith("://")) {
|
|
983
|
+
return value;
|
|
984
|
+
}
|
|
985
|
+
const parsed = new URL(value);
|
|
986
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
987
|
+
return parsed.origin;
|
|
988
|
+
}
|
|
989
|
+
return `${parsed.protocol}//${parsed.host}`;
|
|
990
|
+
}
|
|
991
|
+
function resolveClientDomain(input) {
|
|
992
|
+
if (!input.clientDomain) {
|
|
993
|
+
return input.websiteDomain;
|
|
994
|
+
}
|
|
995
|
+
const resolved = input.pluginConfig.clientDomains?.[input.clientDomain];
|
|
996
|
+
if (!resolved) {
|
|
997
|
+
throw new Error(`Unknown clientDomain key: ${input.clientDomain}`);
|
|
998
|
+
}
|
|
999
|
+
return resolved;
|
|
1000
|
+
}
|
|
1001
|
+
function resolveAllowedClientDomain(input) {
|
|
1002
|
+
const websiteDomain = getWebsiteDomain(input);
|
|
1003
|
+
const resolvedClientDomain = resolveClientDomain({
|
|
1004
|
+
clientDomain: input.clientDomain,
|
|
1005
|
+
pluginConfig: input.pluginConfig,
|
|
1006
|
+
websiteDomain
|
|
1007
|
+
});
|
|
1008
|
+
const normalizedClientDomain = normalizeClientDomain(resolvedClientDomain);
|
|
1009
|
+
const allowed = [
|
|
1010
|
+
websiteDomain,
|
|
1011
|
+
...Object.values(input.pluginConfig.clientDomains ?? {})
|
|
1012
|
+
].map(normalizeClientDomain);
|
|
1013
|
+
if (!allowed.includes(normalizedClientDomain)) {
|
|
1014
|
+
throw new Error(`clientDomain is not allowed: ${resolvedClientDomain}`);
|
|
1015
|
+
}
|
|
1016
|
+
return normalizedClientDomain;
|
|
1017
|
+
}
|
|
1018
|
+
function normalizeRedirectToPathForClientDomain(redirectToPath, clientDomain) {
|
|
1019
|
+
if (!redirectToPath) {
|
|
1020
|
+
return void 0;
|
|
1021
|
+
}
|
|
1022
|
+
if (redirectToPath === "NATIVE_APP") {
|
|
1023
|
+
return redirectToPath;
|
|
1024
|
+
}
|
|
1025
|
+
if (redirectToPath.startsWith("//")) {
|
|
1026
|
+
throw new Error("redirectToPath cannot be schemaless");
|
|
1027
|
+
}
|
|
1028
|
+
const normalizedClientDomain = normalizeClientDomain(clientDomain);
|
|
1029
|
+
const redirectUrl = new URL(
|
|
1030
|
+
redirectToPath,
|
|
1031
|
+
normalizedClientDomain.endsWith("://") ? "http://localhost" : normalizedClientDomain
|
|
1032
|
+
);
|
|
1033
|
+
const hasExplicitScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(redirectToPath);
|
|
1034
|
+
if (hasExplicitScheme) {
|
|
1035
|
+
if (redirectUrl.protocol !== "http:" && redirectUrl.protocol !== "https:") {
|
|
1036
|
+
throw new Error("redirectToPath must be http(s) or relative");
|
|
1037
|
+
}
|
|
1038
|
+
if (redirectUrl.origin !== normalizedClientDomain) {
|
|
1039
|
+
throw new Error("redirectToPath must match clientDomain");
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return `${redirectUrl.pathname}${redirectUrl.search}${redirectUrl.hash}`;
|
|
1043
|
+
}
|
|
1044
|
+
function assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath) {
|
|
1045
|
+
if (!redirectToPath) {
|
|
1046
|
+
throw new Error("redirectToPath is required for confirmation bypass magic links");
|
|
1047
|
+
}
|
|
1048
|
+
const allowedRedirectPaths = pluginConfig2.crossDeviceConfirmationBypass?.allowedRedirectPaths ?? [];
|
|
1049
|
+
if (allowedRedirectPaths.length === 0) {
|
|
1050
|
+
throw new Error("crossDeviceConfirmationBypass.allowedRedirectPaths must be configured");
|
|
1051
|
+
}
|
|
1052
|
+
if (!allowedRedirectPaths.includes(redirectToPath)) {
|
|
1053
|
+
throw new Error(`redirectToPath is not allowed for confirmation bypass: ${redirectToPath}`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function getMagicLinkBootstrapParams(input) {
|
|
1057
|
+
return {
|
|
1058
|
+
appKey: input.appKey,
|
|
1059
|
+
apiDomain: input.apiDomain,
|
|
1060
|
+
apiBasePath: input.apiBasePath,
|
|
1061
|
+
...input.appVariantId ? { appVariantId: input.appVariantId } : {},
|
|
1062
|
+
...input.displayContext ? { displayContext: input.displayContext } : {},
|
|
1063
|
+
...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
|
|
1064
|
+
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
function rewriteMagicLink(input) {
|
|
1068
|
+
return rewriteLinkToBaseUrl(
|
|
1069
|
+
input.magicLink,
|
|
1070
|
+
HUB_LOGIN_PAGE_PATH,
|
|
1071
|
+
input.clientDomain,
|
|
1072
|
+
input.bootstrapParams
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
969
1075
|
function getRequestedAppVariantIdFromRequest(req) {
|
|
970
1076
|
return req.getKeyValueFromQuery("app_variant_id");
|
|
971
1077
|
}
|
|
@@ -984,12 +1090,19 @@ function getRequestedRedirectToPathFromRequest(req) {
|
|
|
984
1090
|
|
|
985
1091
|
// src/config.ts
|
|
986
1092
|
var pluginConfig;
|
|
1093
|
+
var superTokensConfig;
|
|
987
1094
|
function setPluginConfig(config2) {
|
|
988
1095
|
pluginConfig = config2;
|
|
989
1096
|
}
|
|
990
1097
|
function getPluginConfig() {
|
|
991
1098
|
return pluginConfig;
|
|
992
1099
|
}
|
|
1100
|
+
function setSuperTokensConfig(config2) {
|
|
1101
|
+
superTokensConfig = config2;
|
|
1102
|
+
}
|
|
1103
|
+
function getSuperTokensConfig() {
|
|
1104
|
+
return superTokensConfig;
|
|
1105
|
+
}
|
|
993
1106
|
function assertRowndAppVariantIsConfigured(appVariantId) {
|
|
994
1107
|
if (!appVariantId) {
|
|
995
1108
|
return;
|
|
@@ -1070,7 +1183,10 @@ function buildSignInMethodsConfig(methodsArray) {
|
|
|
1070
1183
|
},
|
|
1071
1184
|
apple: {
|
|
1072
1185
|
enabled: !!appleMethod,
|
|
1073
|
-
client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
|
|
1186
|
+
client_id: getStringMethodProperty(appleMethod, "clientId") ?? "",
|
|
1187
|
+
...getStringMethodProperty(appleMethod, "webClientType") !== void 0 ? { web_client_type: getStringMethodProperty(appleMethod, "webClientType") } : {},
|
|
1188
|
+
...getStringMethodProperty(appleMethod, "iosClientType") !== void 0 ? { ios_client_type: getStringMethodProperty(appleMethod, "iosClientType") } : {},
|
|
1189
|
+
...getStringMethodProperty(appleMethod, "androidClientType") !== void 0 ? { android_client_type: getStringMethodProperty(appleMethod, "androidClientType") } : {}
|
|
1074
1190
|
},
|
|
1075
1191
|
anonymous: {
|
|
1076
1192
|
enabled: !!anonymousMethod && anonymousType !== "instant",
|
|
@@ -1344,6 +1460,7 @@ function buildAuthMobileConfig(authMobile) {
|
|
|
1344
1460
|
|
|
1345
1461
|
// src/rownd-compatibility.ts
|
|
1346
1462
|
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
1463
|
+
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1347
1464
|
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1348
1465
|
"user_id",
|
|
1349
1466
|
"email",
|
|
@@ -1518,6 +1635,140 @@ function buildRowndSessionClaimPayload(input) {
|
|
|
1518
1635
|
...anonymousId ? { anonymous_id: anonymousId } : {}
|
|
1519
1636
|
};
|
|
1520
1637
|
}
|
|
1638
|
+
function normalizeRowndOAuthScopes(scopes) {
|
|
1639
|
+
return [...new Set(scopes.filter((scope) => scope.length > 0))];
|
|
1640
|
+
}
|
|
1641
|
+
function getRowndOAuthAudience(input) {
|
|
1642
|
+
const requested = input.requestedResource ?? input.requestedAudience;
|
|
1643
|
+
if (requested?.startsWith("app:")) {
|
|
1644
|
+
return requested;
|
|
1645
|
+
}
|
|
1646
|
+
return void 0;
|
|
1647
|
+
}
|
|
1648
|
+
function applyRowndOAuthResourceParams(input) {
|
|
1649
|
+
const resource = firstString(input.params.resource);
|
|
1650
|
+
const audience = firstString(input.params.audience);
|
|
1651
|
+
const rowndAudience = getRowndOAuthAudience({
|
|
1652
|
+
requestedResource: resource,
|
|
1653
|
+
requestedAudience: audience
|
|
1654
|
+
});
|
|
1655
|
+
if (!rowndAudience) {
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
input.userContext.rowndOAuthAudience = rowndAudience;
|
|
1659
|
+
input.params.audience = audience ?? rowndAudience;
|
|
1660
|
+
delete input.params.resource;
|
|
1661
|
+
}
|
|
1662
|
+
async function buildRowndOAuthPayload(input) {
|
|
1663
|
+
const currentPayload = input.currentPayload ?? {};
|
|
1664
|
+
const standardClaims = input.user ? await buildStandardOAuthClaims(input.user, input.scopes) : {};
|
|
1665
|
+
const rowndClaims = input.user ? await buildRowndOAuthSessionClaims(input.user, currentPayload) : {};
|
|
1666
|
+
const audience = getRowndOAuthAudience({
|
|
1667
|
+
requestedAudience: typeof input.userContext?.rowndOAuthAudience === "string" ? input.userContext.rowndOAuthAudience : void 0
|
|
1668
|
+
});
|
|
1669
|
+
return {
|
|
1670
|
+
...currentPayload,
|
|
1671
|
+
...standardClaims,
|
|
1672
|
+
...rowndClaims,
|
|
1673
|
+
...audience ? { aud: audience } : {}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
async function buildRowndOAuthUserInfo(input) {
|
|
1677
|
+
const standardClaims = await buildStandardOAuthClaims(
|
|
1678
|
+
input.user,
|
|
1679
|
+
input.scopes
|
|
1680
|
+
);
|
|
1681
|
+
const rowndClaims = pickOAuthUserInfoRowndClaims(input.accessTokenPayload);
|
|
1682
|
+
return {
|
|
1683
|
+
...input.currentPayload,
|
|
1684
|
+
...standardClaims,
|
|
1685
|
+
...rowndClaims
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
async function buildStandardOAuthClaims(user, scopes) {
|
|
1689
|
+
const claims = {};
|
|
1690
|
+
const metadata = await getRowndMetadata(user.id);
|
|
1691
|
+
const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
|
|
1692
|
+
const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
|
|
1693
|
+
if (scopes.includes("email")) {
|
|
1694
|
+
const email = firstString(rowndData.email) ?? user.emails[0];
|
|
1695
|
+
if (email) {
|
|
1696
|
+
claims.email = email;
|
|
1697
|
+
claims.email_verified = isOAuthClaimVerified(
|
|
1698
|
+
verifiedData.email,
|
|
1699
|
+
email,
|
|
1700
|
+
user.loginMethods.some(
|
|
1701
|
+
(method) => method.hasSameEmailAs(email) && method.verified
|
|
1702
|
+
)
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
if (scopes.includes("phone")) {
|
|
1707
|
+
const phoneNumber = firstString(rowndData.phone_number) ?? user.phoneNumbers[0];
|
|
1708
|
+
if (phoneNumber) {
|
|
1709
|
+
claims.phone_number = phoneNumber;
|
|
1710
|
+
claims.phone_number_verified = isOAuthClaimVerified(
|
|
1711
|
+
verifiedData.phone_number,
|
|
1712
|
+
phoneNumber,
|
|
1713
|
+
user.loginMethods.some(
|
|
1714
|
+
(method) => method.hasSamePhoneNumberAs(phoneNumber) && method.verified
|
|
1715
|
+
)
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
if (scopes.includes("profile")) {
|
|
1720
|
+
const givenName = firstString(rowndData.first_name);
|
|
1721
|
+
const familyName = firstString(rowndData.last_name);
|
|
1722
|
+
const name = [givenName, familyName].filter(Boolean).join(" ");
|
|
1723
|
+
if (name) claims.name = name;
|
|
1724
|
+
if (givenName) claims.given_name = givenName;
|
|
1725
|
+
if (familyName) claims.family_name = familyName;
|
|
1726
|
+
if (typeof metadata.original_rownd_user?.data?.updated_at === "string") {
|
|
1727
|
+
claims.updated_at = metadata.original_rownd_user.data.updated_at;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return claims;
|
|
1731
|
+
}
|
|
1732
|
+
async function getRowndMetadata(userId) {
|
|
1733
|
+
const metadata = await import_usermetadata2.default.getUserMetadata(userId);
|
|
1734
|
+
return metadata.metadata || {};
|
|
1735
|
+
}
|
|
1736
|
+
function pickOAuthUserInfoRowndClaims(payload) {
|
|
1737
|
+
const claims = {};
|
|
1738
|
+
for (const key of [
|
|
1739
|
+
"app_user_id",
|
|
1740
|
+
"auth_level",
|
|
1741
|
+
"is_verified_user",
|
|
1742
|
+
"is_anonymous",
|
|
1743
|
+
"anonymous_id",
|
|
1744
|
+
"https://auth.rownd.io/app_user_id",
|
|
1745
|
+
"https://auth.rownd.io/auth_level",
|
|
1746
|
+
"https://auth.rownd.io/is_verified_user",
|
|
1747
|
+
"https://auth.rownd.io/is_anonymous"
|
|
1748
|
+
]) {
|
|
1749
|
+
if (payload[key] !== void 0) {
|
|
1750
|
+
claims[key] = payload[key];
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return claims;
|
|
1754
|
+
}
|
|
1755
|
+
function firstString(value) {
|
|
1756
|
+
if (Array.isArray(value)) {
|
|
1757
|
+
return value.find((entry) => typeof entry === "string");
|
|
1758
|
+
}
|
|
1759
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1760
|
+
}
|
|
1761
|
+
function isOAuthClaimVerified(value, expectedValue, fallback) {
|
|
1762
|
+
return value === true || value === expectedValue || fallback;
|
|
1763
|
+
}
|
|
1764
|
+
async function buildRowndOAuthSessionClaims(user, currentPayload) {
|
|
1765
|
+
return buildRowndSessionClaimPayload({
|
|
1766
|
+
userId: user.id,
|
|
1767
|
+
user,
|
|
1768
|
+
metadata: await getRowndMetadata(user.id),
|
|
1769
|
+
currentPayload
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1521
1772
|
async function shouldLinkRowndAccounts(input) {
|
|
1522
1773
|
const [newAccountInfo, , session] = input;
|
|
1523
1774
|
if (!session) {
|
|
@@ -1665,7 +1916,7 @@ var import_emailverification = __toESM(require("supertokens-node/recipe/emailver
|
|
|
1665
1916
|
var import_passwordless = __toESM(require("supertokens-node/recipe/passwordless"));
|
|
1666
1917
|
var import_session = __toESM(require("supertokens-node/recipe/session"));
|
|
1667
1918
|
var import_claims = require("supertokens-node/recipe/session/claims");
|
|
1668
|
-
var
|
|
1919
|
+
var import_usermetadata3 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1669
1920
|
async function importUser(stUser, config2) {
|
|
1670
1921
|
const headers = {
|
|
1671
1922
|
"Content-Type": "application/json"
|
|
@@ -1704,7 +1955,7 @@ async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
|
1704
1955
|
if (appVariants.includes(appVariantId)) {
|
|
1705
1956
|
return;
|
|
1706
1957
|
}
|
|
1707
|
-
await
|
|
1958
|
+
await import_usermetadata3.default.updateUserMetadata(userId, {
|
|
1708
1959
|
...metadata,
|
|
1709
1960
|
original_rownd_user: {
|
|
1710
1961
|
...originalRowndUser,
|
|
@@ -1736,8 +1987,78 @@ async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId
|
|
|
1736
1987
|
appVariantId
|
|
1737
1988
|
});
|
|
1738
1989
|
}
|
|
1990
|
+
async function createMagicLinkWithConfirmationBypass(input) {
|
|
1991
|
+
const hasEmail = typeof input.email === "string" && input.email.length > 0;
|
|
1992
|
+
const hasPhoneNumber = typeof input.phoneNumber === "string" && input.phoneNumber.length > 0;
|
|
1993
|
+
if (hasEmail === hasPhoneNumber) {
|
|
1994
|
+
throw new Error("Exactly one of email or phoneNumber is required");
|
|
1995
|
+
}
|
|
1996
|
+
const stConfig = getSuperTokensConfig();
|
|
1997
|
+
if (!stConfig) {
|
|
1998
|
+
throw new Error("SuperTokens config is not initialized");
|
|
1999
|
+
}
|
|
2000
|
+
const pluginConfig2 = getPluginConfig();
|
|
2001
|
+
if (!pluginConfig2) {
|
|
2002
|
+
throw new Error("Rownd plugin config is not initialized");
|
|
2003
|
+
}
|
|
2004
|
+
const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
|
|
2005
|
+
const appVariantId = input.appVariantId;
|
|
2006
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2007
|
+
const clientDomain = resolveAllowedClientDomain({
|
|
2008
|
+
clientDomain: input.clientDomain,
|
|
2009
|
+
pluginConfig: pluginConfig2,
|
|
2010
|
+
stConfig,
|
|
2011
|
+
request: input.request,
|
|
2012
|
+
userContext: input.userContext
|
|
2013
|
+
});
|
|
2014
|
+
const redirectToPath = normalizeRedirectToPathForClientDomain(input.redirectToPath, clientDomain);
|
|
2015
|
+
assertAllowedBypassRedirectPath(pluginConfig2, redirectToPath);
|
|
2016
|
+
const userContext = {
|
|
2017
|
+
...input.userContext ?? {},
|
|
2018
|
+
...input.displayContext ? { rowndDisplayContext: input.displayContext } : {},
|
|
2019
|
+
rowndRedirectToPath: redirectToPath,
|
|
2020
|
+
...input.clientDomain ? { rowndClientDomain: input.clientDomain } : {},
|
|
2021
|
+
...appVariantId ? { rowndAppVariantId: appVariantId } : {}
|
|
2022
|
+
};
|
|
2023
|
+
const codeInfo = hasEmail ? await import_passwordless.default.createCode({
|
|
2024
|
+
email: input.email,
|
|
2025
|
+
tenantId,
|
|
2026
|
+
session: input.session,
|
|
2027
|
+
userContext
|
|
2028
|
+
}) : await import_passwordless.default.createCode({
|
|
2029
|
+
phoneNumber: input.phoneNumber,
|
|
2030
|
+
tenantId,
|
|
2031
|
+
session: input.session,
|
|
2032
|
+
userContext
|
|
2033
|
+
});
|
|
2034
|
+
if (codeInfo.status !== "OK") {
|
|
2035
|
+
throw new Error("Failed to create magic link");
|
|
2036
|
+
}
|
|
2037
|
+
const magicLink = `${getWebsiteDomain({
|
|
2038
|
+
stConfig,
|
|
2039
|
+
request: input.request,
|
|
2040
|
+
userContext
|
|
2041
|
+
})}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
|
|
2042
|
+
codeInfo.preAuthSessionId
|
|
2043
|
+
)}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
|
|
2044
|
+
const rewrittenUrl = new URL(rewriteMagicLink({
|
|
2045
|
+
magicLink,
|
|
2046
|
+
clientDomain,
|
|
2047
|
+
bootstrapParams: getMagicLinkBootstrapParams({
|
|
2048
|
+
appKey: pluginConfig2.rowndAppKey,
|
|
2049
|
+
apiDomain: getAppInfoString(stConfig.appInfo.apiDomain),
|
|
2050
|
+
apiBasePath: getAppInfoString(stConfig.appInfo.apiBasePath),
|
|
2051
|
+
appVariantId,
|
|
2052
|
+
displayContext: input.displayContext,
|
|
2053
|
+
redirectToPath,
|
|
2054
|
+
clientDomainKey: input.clientDomain
|
|
2055
|
+
})
|
|
2056
|
+
}));
|
|
2057
|
+
rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
|
|
2058
|
+
return rewrittenUrl.toString();
|
|
2059
|
+
}
|
|
1739
2060
|
async function getUserMetadata(userId) {
|
|
1740
|
-
const metadata = await
|
|
2061
|
+
const metadata = await import_usermetadata3.default.getUserMetadata(userId);
|
|
1741
2062
|
return metadata.metadata || {};
|
|
1742
2063
|
}
|
|
1743
2064
|
function getPendingVerifications(metadata) {
|
|
@@ -1894,7 +2215,7 @@ async function updateUserData(userId, inputData) {
|
|
|
1894
2215
|
...metadata,
|
|
1895
2216
|
...inputData
|
|
1896
2217
|
};
|
|
1897
|
-
await
|
|
2218
|
+
await import_usermetadata3.default.updateUserMetadata(userId, updatedMetadata);
|
|
1898
2219
|
return getUserById(userId);
|
|
1899
2220
|
}
|
|
1900
2221
|
async function startPendingEmailVerification(input) {
|
|
@@ -1914,7 +2235,7 @@ async function startPendingEmailVerification(input) {
|
|
|
1914
2235
|
);
|
|
1915
2236
|
}
|
|
1916
2237
|
if (pendingEmailVerifications.length > 0) {
|
|
1917
|
-
await
|
|
2238
|
+
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
1918
2239
|
...metadata,
|
|
1919
2240
|
rownd_pending_verification: pendingVerifications.filter(
|
|
1920
2241
|
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
@@ -1937,7 +2258,7 @@ async function startPendingEmailVerification(input) {
|
|
|
1937
2258
|
value: input.email,
|
|
1938
2259
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1939
2260
|
};
|
|
1940
|
-
await
|
|
2261
|
+
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
1941
2262
|
...metadata,
|
|
1942
2263
|
rownd_pending_verification: [
|
|
1943
2264
|
...pendingVerifications.filter(
|
|
@@ -2058,7 +2379,7 @@ async function completePendingEmailVerification(input) {
|
|
|
2058
2379
|
)
|
|
2059
2380
|
)
|
|
2060
2381
|
};
|
|
2061
|
-
await
|
|
2382
|
+
await import_usermetadata3.default.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
2062
2383
|
}
|
|
2063
2384
|
function isMatchingPendingEmailVerification(verification, email) {
|
|
2064
2385
|
return verification.field === "email" && verification.value === email;
|
|
@@ -2069,7 +2390,7 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
2069
2390
|
...metadata,
|
|
2070
2391
|
...inputMeta
|
|
2071
2392
|
};
|
|
2072
|
-
await
|
|
2393
|
+
await import_usermetadata3.default.updateUserMetadata(userId, updatedMetadata);
|
|
2073
2394
|
return {
|
|
2074
2395
|
id: userId,
|
|
2075
2396
|
meta: Object.fromEntries(
|
|
@@ -2090,6 +2411,9 @@ var import_crypto = require("crypto");
|
|
|
2090
2411
|
var import_supertokens_node3 = __toESM(require("supertokens-node"));
|
|
2091
2412
|
var import_session2 = __toESM(require("supertokens-node/recipe/session"));
|
|
2092
2413
|
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
2414
|
+
function isBodyString(body, key) {
|
|
2415
|
+
return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
|
|
2416
|
+
}
|
|
2093
2417
|
function handleGetAppConfig(deps) {
|
|
2094
2418
|
return async (req) => {
|
|
2095
2419
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
@@ -2110,6 +2434,43 @@ function handleGetAppConfig(deps) {
|
|
|
2110
2434
|
};
|
|
2111
2435
|
};
|
|
2112
2436
|
}
|
|
2437
|
+
function handleValidatePasswordlessConfirmationBypass(deps) {
|
|
2438
|
+
return async (req) => {
|
|
2439
|
+
try {
|
|
2440
|
+
const body = await getJsonBody(req);
|
|
2441
|
+
const clientDomain = isBodyString(body, "clientDomain") ? body.clientDomain : void 0;
|
|
2442
|
+
const redirectToPath = isBodyString(body, "redirectToPath") ? body.redirectToPath : void 0;
|
|
2443
|
+
const appVariantId = isBodyString(body, "appVariantId") ? body.appVariantId : void 0;
|
|
2444
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2445
|
+
const resolvedClientDomain = resolveAllowedClientDomain({
|
|
2446
|
+
clientDomain,
|
|
2447
|
+
pluginConfig: deps.pluginConfig,
|
|
2448
|
+
stConfig: deps.stConfig,
|
|
2449
|
+
request: req
|
|
2450
|
+
});
|
|
2451
|
+
const normalizedRedirectToPath = normalizeRedirectToPathForClientDomain(
|
|
2452
|
+
redirectToPath,
|
|
2453
|
+
resolvedClientDomain
|
|
2454
|
+
);
|
|
2455
|
+
assertAllowedBypassRedirectPath(
|
|
2456
|
+
deps.pluginConfig,
|
|
2457
|
+
normalizedRedirectToPath
|
|
2458
|
+
);
|
|
2459
|
+
return {
|
|
2460
|
+
status: "OK",
|
|
2461
|
+
bypass: true
|
|
2462
|
+
};
|
|
2463
|
+
} catch (error) {
|
|
2464
|
+
logDebugMessage(
|
|
2465
|
+
`Passwordless confirmation bypass validation failed. Error: ${getErrorMessage(error)}`
|
|
2466
|
+
);
|
|
2467
|
+
return {
|
|
2468
|
+
status: "ERROR",
|
|
2469
|
+
bypass: false
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2113
2474
|
function handleGuestLogin(deps) {
|
|
2114
2475
|
return async (req, res, _session, userContext) => {
|
|
2115
2476
|
const startedAt = Date.now();
|
|
@@ -2507,6 +2868,7 @@ var init = createPluginInitFunction(
|
|
|
2507
2868
|
}
|
|
2508
2869
|
},
|
|
2509
2870
|
routeHandlers(stConfig) {
|
|
2871
|
+
setSuperTokensConfig(stConfig);
|
|
2510
2872
|
const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
|
|
2511
2873
|
hubBootstrapParams = {
|
|
2512
2874
|
apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
|
|
@@ -2537,6 +2899,13 @@ var init = createPluginInitFunction(
|
|
|
2537
2899
|
method: "post",
|
|
2538
2900
|
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2539
2901
|
},
|
|
2902
|
+
{
|
|
2903
|
+
path: `${apiBasePath}/plugin/passwordless-cross-device-confirmation/validate`,
|
|
2904
|
+
method: "post",
|
|
2905
|
+
handler: withRequestHandler(
|
|
2906
|
+
handleValidatePasswordlessConfirmationBypass(routeHandlerDeps)
|
|
2907
|
+
)
|
|
2908
|
+
},
|
|
2540
2909
|
{
|
|
2541
2910
|
path: `${apiBasePath}/plugin/migrate-session`,
|
|
2542
2911
|
method: "post",
|
|
@@ -2597,6 +2966,74 @@ var init = createPluginInitFunction(
|
|
|
2597
2966
|
};
|
|
2598
2967
|
},
|
|
2599
2968
|
overrideMap: {
|
|
2969
|
+
oauth2provider: {
|
|
2970
|
+
recipeInitRequired: false,
|
|
2971
|
+
functions: (originalImplementation) => ({
|
|
2972
|
+
...originalImplementation,
|
|
2973
|
+
getRequestedScopes: async (input) => {
|
|
2974
|
+
const scopes = await originalImplementation.getRequestedScopes(
|
|
2975
|
+
input
|
|
2976
|
+
);
|
|
2977
|
+
return normalizeRowndOAuthScopes(scopes);
|
|
2978
|
+
},
|
|
2979
|
+
buildAccessTokenPayload: async (input) => {
|
|
2980
|
+
const payload = await originalImplementation.buildAccessTokenPayload(
|
|
2981
|
+
input
|
|
2982
|
+
);
|
|
2983
|
+
return buildRowndOAuthPayload({
|
|
2984
|
+
user: input.user,
|
|
2985
|
+
client: input.client,
|
|
2986
|
+
scopes: input.scopes,
|
|
2987
|
+
currentPayload: payload,
|
|
2988
|
+
userContext: input.userContext
|
|
2989
|
+
});
|
|
2990
|
+
},
|
|
2991
|
+
buildIdTokenPayload: async (input) => {
|
|
2992
|
+
const payload = await originalImplementation.buildIdTokenPayload(
|
|
2993
|
+
input
|
|
2994
|
+
);
|
|
2995
|
+
return buildRowndOAuthPayload({
|
|
2996
|
+
user: input.user,
|
|
2997
|
+
client: input.client,
|
|
2998
|
+
scopes: input.scopes,
|
|
2999
|
+
currentPayload: payload,
|
|
3000
|
+
userContext: input.userContext
|
|
3001
|
+
});
|
|
3002
|
+
},
|
|
3003
|
+
buildUserInfo: async (input) => {
|
|
3004
|
+
const payload = await originalImplementation.buildUserInfo(input);
|
|
3005
|
+
return buildRowndOAuthUserInfo({
|
|
3006
|
+
user: input.user,
|
|
3007
|
+
accessTokenPayload: input.accessTokenPayload,
|
|
3008
|
+
scopes: input.scopes,
|
|
3009
|
+
currentPayload: payload
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
}),
|
|
3013
|
+
apis: (originalImplementation) => ({
|
|
3014
|
+
...originalImplementation,
|
|
3015
|
+
authGET: async (input) => {
|
|
3016
|
+
if (originalImplementation.authGET === void 0) {
|
|
3017
|
+
throw new Error("OAuth2Provider authGET is unavailable");
|
|
3018
|
+
}
|
|
3019
|
+
applyRowndOAuthResourceParams({
|
|
3020
|
+
params: input.params,
|
|
3021
|
+
userContext: input.userContext
|
|
3022
|
+
});
|
|
3023
|
+
return originalImplementation.authGET(input);
|
|
3024
|
+
},
|
|
3025
|
+
tokenPOST: async (input) => {
|
|
3026
|
+
if (originalImplementation.tokenPOST === void 0) {
|
|
3027
|
+
throw new Error("OAuth2Provider tokenPOST is unavailable");
|
|
3028
|
+
}
|
|
3029
|
+
applyRowndOAuthResourceParams({
|
|
3030
|
+
params: input.body,
|
|
3031
|
+
userContext: input.userContext
|
|
3032
|
+
});
|
|
3033
|
+
return originalImplementation.tokenPOST(input);
|
|
3034
|
+
}
|
|
3035
|
+
})
|
|
3036
|
+
},
|
|
2600
3037
|
passwordless: {
|
|
2601
3038
|
config: (config2) => {
|
|
2602
3039
|
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
@@ -2859,6 +3296,7 @@ var init = createPluginInitFunction(
|
|
|
2859
3296
|
rowndAppSecret: config2.rowndAppSecret,
|
|
2860
3297
|
enableDebugLogs: config2.enableDebugLogs,
|
|
2861
3298
|
clientDomains: config2.clientDomains,
|
|
3299
|
+
crossDeviceConfirmationBypass: config2.crossDeviceConfirmationBypass,
|
|
2862
3300
|
telemetry: config2.telemetry,
|
|
2863
3301
|
schema: config2.schema,
|
|
2864
3302
|
appConfig: config2.appConfig,
|
|
@@ -2887,6 +3325,7 @@ var index_default = { init };
|
|
|
2887
3325
|
HUB_LOGIN_PAGE_PATH,
|
|
2888
3326
|
HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
2889
3327
|
INSTANT_AUTH_METHOD_ID,
|
|
3328
|
+
PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM,
|
|
2890
3329
|
PLUGIN_ID,
|
|
2891
3330
|
PLUGIN_SDK_VERSION,
|
|
2892
3331
|
PLUGIN_VERSION,
|
|
@@ -2894,6 +3333,7 @@ var index_default = { init };
|
|
|
2894
3333
|
ROWND_JWT_CLAIMS,
|
|
2895
3334
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
2896
3335
|
RowndPluginError,
|
|
3336
|
+
createMagicLinkWithConfirmationBypass,
|
|
2897
3337
|
getRowndClient,
|
|
2898
3338
|
init,
|
|
2899
3339
|
setRowndClient
|