@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.mjs
CHANGED
|
@@ -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",
|
|
@@ -1495,6 +1610,140 @@ function buildRowndSessionClaimPayload(input) {
|
|
|
1495
1610
|
...anonymousId ? { anonymous_id: anonymousId } : {}
|
|
1496
1611
|
};
|
|
1497
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
|
+
}
|
|
1498
1747
|
async function shouldLinkRowndAccounts(input) {
|
|
1499
1748
|
const [newAccountInfo, , session] = input;
|
|
1500
1749
|
if (!session) {
|
|
@@ -1642,7 +1891,7 @@ import EmailVerification from "supertokens-node/recipe/emailverification";
|
|
|
1642
1891
|
import Passwordless from "supertokens-node/recipe/passwordless";
|
|
1643
1892
|
import Session from "supertokens-node/recipe/session";
|
|
1644
1893
|
import { BooleanClaim } from "supertokens-node/recipe/session/claims";
|
|
1645
|
-
import
|
|
1894
|
+
import UserMetadata3 from "supertokens-node/recipe/usermetadata";
|
|
1646
1895
|
async function importUser(stUser, config2) {
|
|
1647
1896
|
const headers = {
|
|
1648
1897
|
"Content-Type": "application/json"
|
|
@@ -1681,7 +1930,7 @@ async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
|
1681
1930
|
if (appVariants.includes(appVariantId)) {
|
|
1682
1931
|
return;
|
|
1683
1932
|
}
|
|
1684
|
-
await
|
|
1933
|
+
await UserMetadata3.updateUserMetadata(userId, {
|
|
1685
1934
|
...metadata,
|
|
1686
1935
|
original_rownd_user: {
|
|
1687
1936
|
...originalRowndUser,
|
|
@@ -1713,8 +1962,78 @@ async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId
|
|
|
1713
1962
|
appVariantId
|
|
1714
1963
|
});
|
|
1715
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
|
+
}
|
|
1716
2035
|
async function getUserMetadata(userId) {
|
|
1717
|
-
const metadata = await
|
|
2036
|
+
const metadata = await UserMetadata3.getUserMetadata(userId);
|
|
1718
2037
|
return metadata.metadata || {};
|
|
1719
2038
|
}
|
|
1720
2039
|
function getPendingVerifications(metadata) {
|
|
@@ -1871,7 +2190,7 @@ async function updateUserData(userId, inputData) {
|
|
|
1871
2190
|
...metadata,
|
|
1872
2191
|
...inputData
|
|
1873
2192
|
};
|
|
1874
|
-
await
|
|
2193
|
+
await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
|
|
1875
2194
|
return getUserById(userId);
|
|
1876
2195
|
}
|
|
1877
2196
|
async function startPendingEmailVerification(input) {
|
|
@@ -1891,7 +2210,7 @@ async function startPendingEmailVerification(input) {
|
|
|
1891
2210
|
);
|
|
1892
2211
|
}
|
|
1893
2212
|
if (pendingEmailVerifications.length > 0) {
|
|
1894
|
-
await
|
|
2213
|
+
await UserMetadata3.updateUserMetadata(input.userId, {
|
|
1895
2214
|
...metadata,
|
|
1896
2215
|
rownd_pending_verification: pendingVerifications.filter(
|
|
1897
2216
|
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
@@ -1914,7 +2233,7 @@ async function startPendingEmailVerification(input) {
|
|
|
1914
2233
|
value: input.email,
|
|
1915
2234
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1916
2235
|
};
|
|
1917
|
-
await
|
|
2236
|
+
await UserMetadata3.updateUserMetadata(input.userId, {
|
|
1918
2237
|
...metadata,
|
|
1919
2238
|
rownd_pending_verification: [
|
|
1920
2239
|
...pendingVerifications.filter(
|
|
@@ -2035,7 +2354,7 @@ async function completePendingEmailVerification(input) {
|
|
|
2035
2354
|
)
|
|
2036
2355
|
)
|
|
2037
2356
|
};
|
|
2038
|
-
await
|
|
2357
|
+
await UserMetadata3.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
2039
2358
|
}
|
|
2040
2359
|
function isMatchingPendingEmailVerification(verification, email) {
|
|
2041
2360
|
return verification.field === "email" && verification.value === email;
|
|
@@ -2046,7 +2365,7 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
2046
2365
|
...metadata,
|
|
2047
2366
|
...inputMeta
|
|
2048
2367
|
};
|
|
2049
|
-
await
|
|
2368
|
+
await UserMetadata3.updateUserMetadata(userId, updatedMetadata);
|
|
2050
2369
|
return {
|
|
2051
2370
|
id: userId,
|
|
2052
2371
|
meta: Object.fromEntries(
|
|
@@ -2067,6 +2386,9 @@ import { randomUUID } from "crypto";
|
|
|
2067
2386
|
import SuperTokens3 from "supertokens-node";
|
|
2068
2387
|
import Session2 from "supertokens-node/recipe/session";
|
|
2069
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
|
+
}
|
|
2070
2392
|
function handleGetAppConfig(deps) {
|
|
2071
2393
|
return async (req) => {
|
|
2072
2394
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
@@ -2087,6 +2409,43 @@ function handleGetAppConfig(deps) {
|
|
|
2087
2409
|
};
|
|
2088
2410
|
};
|
|
2089
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
|
+
}
|
|
2090
2449
|
function handleGuestLogin(deps) {
|
|
2091
2450
|
return async (req, res, _session, userContext) => {
|
|
2092
2451
|
const startedAt = Date.now();
|
|
@@ -2484,6 +2843,7 @@ var init = createPluginInitFunction(
|
|
|
2484
2843
|
}
|
|
2485
2844
|
},
|
|
2486
2845
|
routeHandlers(stConfig) {
|
|
2846
|
+
setSuperTokensConfig(stConfig);
|
|
2487
2847
|
const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
|
|
2488
2848
|
hubBootstrapParams = {
|
|
2489
2849
|
apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
|
|
@@ -2514,6 +2874,13 @@ var init = createPluginInitFunction(
|
|
|
2514
2874
|
method: "post",
|
|
2515
2875
|
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2516
2876
|
},
|
|
2877
|
+
{
|
|
2878
|
+
path: `${apiBasePath}/plugin/passwordless-cross-device-confirmation/validate`,
|
|
2879
|
+
method: "post",
|
|
2880
|
+
handler: withRequestHandler(
|
|
2881
|
+
handleValidatePasswordlessConfirmationBypass(routeHandlerDeps)
|
|
2882
|
+
)
|
|
2883
|
+
},
|
|
2517
2884
|
{
|
|
2518
2885
|
path: `${apiBasePath}/plugin/migrate-session`,
|
|
2519
2886
|
method: "post",
|
|
@@ -2574,6 +2941,74 @@ var init = createPluginInitFunction(
|
|
|
2574
2941
|
};
|
|
2575
2942
|
},
|
|
2576
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
|
+
},
|
|
2577
3012
|
passwordless: {
|
|
2578
3013
|
config: (config2) => {
|
|
2579
3014
|
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
@@ -2836,6 +3271,7 @@ var init = createPluginInitFunction(
|
|
|
2836
3271
|
rowndAppSecret: config2.rowndAppSecret,
|
|
2837
3272
|
enableDebugLogs: config2.enableDebugLogs,
|
|
2838
3273
|
clientDomains: config2.clientDomains,
|
|
3274
|
+
crossDeviceConfirmationBypass: config2.crossDeviceConfirmationBypass,
|
|
2839
3275
|
telemetry: config2.telemetry,
|
|
2840
3276
|
schema: config2.schema,
|
|
2841
3277
|
appConfig: config2.appConfig,
|
|
@@ -2863,6 +3299,7 @@ export {
|
|
|
2863
3299
|
HUB_LOGIN_PAGE_PATH,
|
|
2864
3300
|
HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
2865
3301
|
INSTANT_AUTH_METHOD_ID,
|
|
3302
|
+
PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM,
|
|
2866
3303
|
PLUGIN_ID,
|
|
2867
3304
|
PLUGIN_SDK_VERSION,
|
|
2868
3305
|
PLUGIN_VERSION,
|
|
@@ -2870,6 +3307,7 @@ export {
|
|
|
2870
3307
|
ROWND_JWT_CLAIMS,
|
|
2871
3308
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
2872
3309
|
RowndPluginError,
|
|
3310
|
+
createMagicLinkWithConfirmationBypass,
|
|
2873
3311
|
index_default as default,
|
|
2874
3312
|
getRowndClient,
|
|
2875
3313
|
init,
|