@supertokens-plugins/rownd-nodejs 0.5.0 → 0.6.0
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 +15 -3
- package/dist/bulkMigrate.js +37 -17
- package/dist/generateAppConfig.js +5 -2
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +151 -73
- package/dist/index.mjs +151 -73
- package/dist/initConfig.js +12 -0
- package/dist/setupCoreInstance.js +113 -47
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -639,12 +639,11 @@ function createLoggerConfig() {
|
|
|
639
639
|
}
|
|
640
640
|
var config = createLoggerConfig();
|
|
641
641
|
var logger = common_default(config);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
formatters.o = function(value) {
|
|
642
|
+
if (logger.formatters) {
|
|
643
|
+
logger.formatters.o = function(value) {
|
|
645
644
|
return import_util.default.inspect(value, { colors: this.useColors, compact: true }).split("\n").map((line) => line.trim()).join(" ");
|
|
646
645
|
};
|
|
647
|
-
formatters.O = function(value) {
|
|
646
|
+
logger.formatters.O = function(value) {
|
|
648
647
|
return import_util.default.inspect(value, { colors: this.useColors, compact: false });
|
|
649
648
|
};
|
|
650
649
|
}
|
|
@@ -866,6 +865,10 @@ var RowndPluginError = class extends Error {
|
|
|
866
865
|
};
|
|
867
866
|
|
|
868
867
|
// src/utils.ts
|
|
868
|
+
var ROWND_OAUTH_LOGIN_CHALLENGE_PARAM = "rownd_oauth_login_challenge";
|
|
869
|
+
function resolveTenantId(req) {
|
|
870
|
+
return req.getKeyValueFromQuery("tenantId") || PUBLIC_TENANT_ID;
|
|
871
|
+
}
|
|
869
872
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
870
873
|
try {
|
|
871
874
|
const url = new URL(inputUrl);
|
|
@@ -1066,7 +1069,8 @@ function getMagicLinkBootstrapParams(input) {
|
|
|
1066
1069
|
...input.appVariantId ? { appVariantId: input.appVariantId } : {},
|
|
1067
1070
|
...input.displayContext ? { displayContext: input.displayContext } : {},
|
|
1068
1071
|
...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
|
|
1069
|
-
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
|
|
1072
|
+
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {},
|
|
1073
|
+
...input.oauthLoginChallenge ? { oauthLoginChallenge: input.oauthLoginChallenge } : {}
|
|
1070
1074
|
};
|
|
1071
1075
|
}
|
|
1072
1076
|
function rewriteMagicLink(input) {
|
|
@@ -1092,6 +1096,10 @@ function getRequestedRedirectToPathFromRequest(req) {
|
|
|
1092
1096
|
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1093
1097
|
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1094
1098
|
}
|
|
1099
|
+
function getRequestedOAuthLoginChallengeFromRequest(req) {
|
|
1100
|
+
const loginChallenge = req.getKeyValueFromQuery(ROWND_OAUTH_LOGIN_CHALLENGE_PARAM);
|
|
1101
|
+
return typeof loginChallenge === "string" && loginChallenge.length > 0 ? loginChallenge : void 0;
|
|
1102
|
+
}
|
|
1095
1103
|
|
|
1096
1104
|
// src/config.ts
|
|
1097
1105
|
var pluginConfig;
|
|
@@ -1464,6 +1472,7 @@ function buildAuthMobileConfig(authMobile) {
|
|
|
1464
1472
|
}
|
|
1465
1473
|
|
|
1466
1474
|
// src/rownd-compatibility.ts
|
|
1475
|
+
var import_node_crypto = require("crypto");
|
|
1467
1476
|
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
1468
1477
|
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1469
1478
|
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -1477,13 +1486,21 @@ var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
|
1477
1486
|
"original_rownd_user",
|
|
1478
1487
|
"rownd_pending_verification"
|
|
1479
1488
|
]);
|
|
1489
|
+
var SUPERTOKENS_FAKE_EMAIL_DOMAIN = "stfakeemail.supertokens.com";
|
|
1490
|
+
function isSuperTokensFakeEmail(email) {
|
|
1491
|
+
return typeof email === "string" && email.toLowerCase().endsWith(`@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`);
|
|
1492
|
+
}
|
|
1493
|
+
function buildSuperTokensFakeEmail(thirdPartyUserId, thirdPartyId) {
|
|
1494
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(`${thirdPartyId}:${thirdPartyUserId}`).digest("hex").slice(0, 32);
|
|
1495
|
+
return `st-${thirdPartyId}-${hash}@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`;
|
|
1496
|
+
}
|
|
1480
1497
|
function isIdentityField(field) {
|
|
1481
1498
|
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1482
1499
|
}
|
|
1483
1500
|
function isInternalMetadataField(field) {
|
|
1484
1501
|
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1485
1502
|
}
|
|
1486
|
-
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1503
|
+
function mapRowndUserToSuperTokens(rowndUser, tenantId) {
|
|
1487
1504
|
const loginMethods = [];
|
|
1488
1505
|
const rowndUserData = rowndUser.data || {};
|
|
1489
1506
|
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
@@ -1491,41 +1508,41 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1491
1508
|
throw new Error("Rownd user has no user_id");
|
|
1492
1509
|
}
|
|
1493
1510
|
if (rowndUserData.google_id) {
|
|
1494
|
-
|
|
1495
|
-
throw new Error("Rownd Google user is missing email");
|
|
1496
|
-
}
|
|
1511
|
+
const googleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.google_id, "google");
|
|
1497
1512
|
loginMethods.push({
|
|
1498
1513
|
recipeId: "thirdparty",
|
|
1499
1514
|
thirdPartyId: "google",
|
|
1500
1515
|
thirdPartyUserId: rowndUserData.google_id,
|
|
1501
|
-
email:
|
|
1502
|
-
isVerified: !!rowndUserVerifiedData.google_id
|
|
1516
|
+
email: googleEmail,
|
|
1517
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id,
|
|
1518
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1503
1519
|
});
|
|
1504
1520
|
}
|
|
1505
1521
|
if (rowndUserData.apple_id) {
|
|
1506
|
-
|
|
1507
|
-
throw new Error("Rownd Apple user is missing email");
|
|
1508
|
-
}
|
|
1522
|
+
const appleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.apple_id, "apple");
|
|
1509
1523
|
loginMethods.push({
|
|
1510
1524
|
recipeId: "thirdparty",
|
|
1511
1525
|
thirdPartyId: "apple",
|
|
1512
1526
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
1513
|
-
email:
|
|
1514
|
-
isVerified: !!rowndUserVerifiedData.apple_id
|
|
1527
|
+
email: appleEmail,
|
|
1528
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id,
|
|
1529
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1515
1530
|
});
|
|
1516
1531
|
}
|
|
1517
1532
|
if (rowndUserData.phone_number) {
|
|
1518
1533
|
loginMethods.push({
|
|
1519
1534
|
recipeId: "passwordless",
|
|
1520
1535
|
phoneNumber: rowndUserData.phone_number,
|
|
1521
|
-
isVerified: !!rowndUserVerifiedData.phone_number
|
|
1536
|
+
isVerified: !!rowndUserVerifiedData.phone_number,
|
|
1537
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1522
1538
|
});
|
|
1523
1539
|
}
|
|
1524
1540
|
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
1525
1541
|
loginMethods.push({
|
|
1526
1542
|
recipeId: "passwordless",
|
|
1527
1543
|
email: rowndUserData.email,
|
|
1528
|
-
isVerified: !!rowndUserVerifiedData.email
|
|
1544
|
+
isVerified: !!rowndUserVerifiedData.email,
|
|
1545
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1529
1546
|
});
|
|
1530
1547
|
}
|
|
1531
1548
|
let authLevel = rowndUser.auth_level;
|
|
@@ -1537,7 +1554,8 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1537
1554
|
thirdPartyId,
|
|
1538
1555
|
thirdPartyUserId: rowndUserData.user_id,
|
|
1539
1556
|
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1540
|
-
isVerified: false
|
|
1557
|
+
isVerified: false,
|
|
1558
|
+
...tenantId ? { tenantIds: [tenantId] } : {}
|
|
1541
1559
|
});
|
|
1542
1560
|
}
|
|
1543
1561
|
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
@@ -1696,7 +1714,7 @@ async function buildStandardOAuthClaims(user, scopes) {
|
|
|
1696
1714
|
const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
|
|
1697
1715
|
const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
|
|
1698
1716
|
if (scopes.includes("email")) {
|
|
1699
|
-
const email = firstString(rowndData.email)
|
|
1717
|
+
const email = firstRealEmail(firstString(rowndData.email), ...user.emails);
|
|
1700
1718
|
if (email) {
|
|
1701
1719
|
claims.email = email;
|
|
1702
1720
|
claims.email_verified = isOAuthClaimVerified(
|
|
@@ -1763,6 +1781,11 @@ function firstString(value) {
|
|
|
1763
1781
|
}
|
|
1764
1782
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1765
1783
|
}
|
|
1784
|
+
function firstRealEmail(...values) {
|
|
1785
|
+
return values.find((entry) => {
|
|
1786
|
+
return typeof entry === "string" && entry.length > 0 && !isSuperTokensFakeEmail(entry);
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1766
1789
|
function isOAuthClaimVerified(value, expectedValue, fallback) {
|
|
1767
1790
|
return value === true || value === expectedValue || fallback;
|
|
1768
1791
|
}
|
|
@@ -2046,6 +2069,7 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2046
2069
|
})}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
|
|
2047
2070
|
codeInfo.preAuthSessionId
|
|
2048
2071
|
)}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
|
|
2072
|
+
const oauthLoginChallenge = userContext.rowndOAuthLoginChallenge;
|
|
2049
2073
|
const rewrittenUrl = new URL(rewriteMagicLink({
|
|
2050
2074
|
magicLink,
|
|
2051
2075
|
clientDomain,
|
|
@@ -2056,7 +2080,8 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2056
2080
|
appVariantId,
|
|
2057
2081
|
displayContext: input.displayContext,
|
|
2058
2082
|
redirectToPath,
|
|
2059
|
-
clientDomainKey: input.clientDomain
|
|
2083
|
+
clientDomainKey: input.clientDomain,
|
|
2084
|
+
oauthLoginChallenge: typeof oauthLoginChallenge === "string" ? oauthLoginChallenge : void 0
|
|
2060
2085
|
})
|
|
2061
2086
|
}));
|
|
2062
2087
|
rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
|
|
@@ -2076,7 +2101,7 @@ function getPendingVerifications(metadata) {
|
|
|
2076
2101
|
function isPendingVerification(value) {
|
|
2077
2102
|
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
2078
2103
|
}
|
|
2079
|
-
async function getUserById(userId) {
|
|
2104
|
+
async function getUserById(userId, tenantId = PUBLIC_TENANT_ID) {
|
|
2080
2105
|
const metadata = await getUserMetadata(userId);
|
|
2081
2106
|
const stUser = await import_supertokens_node2.default.getUser(userId);
|
|
2082
2107
|
if (!stUser) {
|
|
@@ -2105,9 +2130,12 @@ async function getUserById(userId) {
|
|
|
2105
2130
|
const verifiedData = {
|
|
2106
2131
|
...originalRowndUser?.verified_data || {}
|
|
2107
2132
|
};
|
|
2108
|
-
|
|
2133
|
+
const tenantLoginMethods = stUser.loginMethods.filter(
|
|
2134
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2135
|
+
);
|
|
2136
|
+
for (const method of tenantLoginMethods) {
|
|
2109
2137
|
if (method.recipeId === "passwordless") {
|
|
2110
|
-
if (method.email) {
|
|
2138
|
+
if (method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2111
2139
|
verifiedData.email = method.email;
|
|
2112
2140
|
if (data.email === void 0) data.email = method.email;
|
|
2113
2141
|
}
|
|
@@ -2119,10 +2147,10 @@ async function getUserById(userId) {
|
|
|
2119
2147
|
} else if (method.recipeId === "thirdparty") {
|
|
2120
2148
|
const thirdPartyId = getThirdPartyId(method);
|
|
2121
2149
|
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
2122
|
-
if (method.verified && method.email) {
|
|
2150
|
+
if (method.verified && method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2123
2151
|
verifiedData.email = method.email;
|
|
2124
2152
|
}
|
|
2125
|
-
if (method.email && data.email === void 0) {
|
|
2153
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2126
2154
|
data.email = method.email;
|
|
2127
2155
|
}
|
|
2128
2156
|
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
@@ -2134,7 +2162,7 @@ async function getUserById(userId) {
|
|
|
2134
2162
|
verifiedData.apple_id = thirdPartyUserId;
|
|
2135
2163
|
}
|
|
2136
2164
|
} else if (method.recipeId === "emailpassword") {
|
|
2137
|
-
if (method.email && data.email === void 0) {
|
|
2165
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2138
2166
|
data.email = method.email;
|
|
2139
2167
|
}
|
|
2140
2168
|
}
|
|
@@ -2145,12 +2173,16 @@ async function getUserById(userId) {
|
|
|
2145
2173
|
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
2146
2174
|
verifiedData.phone_number = data.phone_number;
|
|
2147
2175
|
}
|
|
2148
|
-
const
|
|
2176
|
+
const tenantUser = {
|
|
2177
|
+
...stUser,
|
|
2178
|
+
loginMethods: tenantLoginMethods
|
|
2179
|
+
};
|
|
2180
|
+
const anonymousId = getAnonymousId(stUser.id, tenantUser, metadata);
|
|
2149
2181
|
if (anonymousId && data.anonymous_id === void 0) {
|
|
2150
2182
|
data.anonymous_id = anonymousId;
|
|
2151
2183
|
}
|
|
2152
2184
|
const authLevel = getEffectiveAuthLevel(
|
|
2153
|
-
|
|
2185
|
+
tenantUser,
|
|
2154
2186
|
originalRowndUser?.auth_level,
|
|
2155
2187
|
verifiedData
|
|
2156
2188
|
);
|
|
@@ -2159,15 +2191,15 @@ async function getUserById(userId) {
|
|
|
2159
2191
|
data[key] = "";
|
|
2160
2192
|
}
|
|
2161
2193
|
}
|
|
2162
|
-
const sortedByJoined = [...
|
|
2194
|
+
const sortedByJoined = [...tenantLoginMethods].sort(
|
|
2163
2195
|
(a, b) => a.timeJoined - b.timeJoined
|
|
2164
2196
|
);
|
|
2165
|
-
const latestSessionInfo = await getLatestSessionInfo(stUser.id);
|
|
2197
|
+
const latestSessionInfo = await getLatestSessionInfo(stUser.id, tenantId);
|
|
2166
2198
|
const firstMethod = sortedByJoined[0];
|
|
2167
2199
|
const latestSessionRecipeUserId = latestSessionInfo?.recipeUserId.getAsString();
|
|
2168
2200
|
const lastMethod = latestSessionRecipeUserId ? stUser.loginMethods.find(
|
|
2169
2201
|
(method) => method.recipeUserId.getAsString() === latestSessionRecipeUserId
|
|
2170
|
-
) : [...
|
|
2202
|
+
) : [...tenantLoginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
|
|
2171
2203
|
const lastSignInAt = latestSessionInfo?.timeCreated ?? stUser.timeJoined;
|
|
2172
2204
|
const metadataMeta = Object.fromEntries(
|
|
2173
2205
|
Object.entries(metadata).filter(
|
|
@@ -2195,11 +2227,11 @@ async function getUserById(userId) {
|
|
|
2195
2227
|
attributes: originalRowndUser?.attributes || {}
|
|
2196
2228
|
};
|
|
2197
2229
|
}
|
|
2198
|
-
async function getLatestSessionInfo(userId) {
|
|
2230
|
+
async function getLatestSessionInfo(userId, tenantId) {
|
|
2199
2231
|
const sessionHandles = await import_session.default.getAllSessionHandlesForUser(
|
|
2200
2232
|
userId,
|
|
2201
2233
|
true,
|
|
2202
|
-
|
|
2234
|
+
tenantId
|
|
2203
2235
|
);
|
|
2204
2236
|
const sessionInfos = await Promise.all(
|
|
2205
2237
|
sessionHandles.map(
|
|
@@ -2214,21 +2246,21 @@ async function getLatestSessionInfo(userId) {
|
|
|
2214
2246
|
}
|
|
2215
2247
|
return latestSessionInfo;
|
|
2216
2248
|
}
|
|
2217
|
-
async function updateUserData(userId, inputData) {
|
|
2249
|
+
async function updateUserData(userId, inputData, tenantId = PUBLIC_TENANT_ID) {
|
|
2218
2250
|
const metadata = await getUserMetadata(userId);
|
|
2219
2251
|
const updatedMetadata = {
|
|
2220
2252
|
...metadata,
|
|
2221
2253
|
...inputData
|
|
2222
2254
|
};
|
|
2223
2255
|
await import_usermetadata3.default.updateUserMetadata(userId, updatedMetadata);
|
|
2224
|
-
return getUserById(userId);
|
|
2256
|
+
return getUserById(userId, tenantId);
|
|
2225
2257
|
}
|
|
2226
2258
|
async function startPendingEmailVerification(input) {
|
|
2227
2259
|
const metadata = await getUserMetadata(input.userId);
|
|
2228
|
-
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
2260
|
+
const currentEmail = (await getUserById(input.userId, input.tenantId)).data.email;
|
|
2229
2261
|
const pendingVerifications = getPendingVerifications(metadata);
|
|
2230
2262
|
const pendingEmailVerifications = pendingVerifications.filter(
|
|
2231
|
-
(pendingVerification2) => pendingVerification2.field === "email"
|
|
2263
|
+
(pendingVerification2) => pendingVerification2.field === "email" && (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) === input.tenantId
|
|
2232
2264
|
);
|
|
2233
2265
|
if (currentEmail === input.email) {
|
|
2234
2266
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
@@ -2243,11 +2275,11 @@ async function startPendingEmailVerification(input) {
|
|
|
2243
2275
|
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
2244
2276
|
...metadata,
|
|
2245
2277
|
rownd_pending_verification: pendingVerifications.filter(
|
|
2246
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2278
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2247
2279
|
)
|
|
2248
2280
|
});
|
|
2249
2281
|
}
|
|
2250
|
-
return getUserById(input.userId);
|
|
2282
|
+
return getUserById(input.userId, input.tenantId);
|
|
2251
2283
|
}
|
|
2252
2284
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
2253
2285
|
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
@@ -2261,13 +2293,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2261
2293
|
id: input.pendingVerificationId,
|
|
2262
2294
|
field: "email",
|
|
2263
2295
|
value: input.email,
|
|
2264
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2296
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2297
|
+
tenantId: input.tenantId
|
|
2265
2298
|
};
|
|
2266
2299
|
await import_usermetadata3.default.updateUserMetadata(input.userId, {
|
|
2267
2300
|
...metadata,
|
|
2268
2301
|
rownd_pending_verification: [
|
|
2269
2302
|
...pendingVerifications.filter(
|
|
2270
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2303
|
+
(pendingVerification2) => pendingVerification2.field !== "email" || (pendingVerification2.tenantId ?? PUBLIC_TENANT_ID) !== input.tenantId
|
|
2271
2304
|
),
|
|
2272
2305
|
pendingVerification
|
|
2273
2306
|
]
|
|
@@ -2286,12 +2319,14 @@ async function startPendingEmailVerification(input) {
|
|
|
2286
2319
|
await completePendingEmailVerification({
|
|
2287
2320
|
recipeUserId: input.recipeUserId,
|
|
2288
2321
|
email: input.email,
|
|
2322
|
+
tenantId: input.tenantId,
|
|
2289
2323
|
userContext: input.userContext
|
|
2290
2324
|
});
|
|
2291
2325
|
}
|
|
2292
|
-
return getUserById(input.userId);
|
|
2326
|
+
return getUserById(input.userId, input.tenantId);
|
|
2293
2327
|
}
|
|
2294
2328
|
async function completePendingEmailVerification(input) {
|
|
2329
|
+
const tenantId = input.tenantId ?? PUBLIC_TENANT_ID;
|
|
2295
2330
|
const user = await import_supertokens_node2.default.getUser(
|
|
2296
2331
|
input.recipeUserId.getAsString(),
|
|
2297
2332
|
input.userContext
|
|
@@ -2302,7 +2337,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2302
2337
|
const pendingVerification = pendingVerifications.find(
|
|
2303
2338
|
(pendingVerification2) => isMatchingPendingEmailVerification(
|
|
2304
2339
|
pendingVerification2,
|
|
2305
|
-
input.email
|
|
2340
|
+
input.email,
|
|
2341
|
+
tenantId
|
|
2306
2342
|
)
|
|
2307
2343
|
);
|
|
2308
2344
|
if (!pendingVerification) {
|
|
@@ -2310,7 +2346,12 @@ async function completePendingEmailVerification(input) {
|
|
|
2310
2346
|
}
|
|
2311
2347
|
let metadataUserId = userId;
|
|
2312
2348
|
let verifiedRecipeUserId = input.recipeUserId;
|
|
2313
|
-
const
|
|
2349
|
+
const tenantLoginMethods = user?.loginMethods.filter(
|
|
2350
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2351
|
+
) ?? [];
|
|
2352
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(
|
|
2353
|
+
tenantLoginMethods
|
|
2354
|
+
);
|
|
2314
2355
|
if (passwordlessEmailMethod) {
|
|
2315
2356
|
const updateResult = await import_passwordless.default.updateUser({
|
|
2316
2357
|
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
@@ -2323,9 +2364,9 @@ async function completePendingEmailVerification(input) {
|
|
|
2323
2364
|
);
|
|
2324
2365
|
}
|
|
2325
2366
|
verifiedRecipeUserId = passwordlessEmailMethod.recipeUserId;
|
|
2326
|
-
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
2367
|
+
} else if (hasOnlyGuestLoginMethods(user ? { ...user, loginMethods: tenantLoginMethods } : user)) {
|
|
2327
2368
|
const isPasswordlessSignUpAllowed = await import_accountlinking.default.isSignUpAllowed(
|
|
2328
|
-
|
|
2369
|
+
tenantId,
|
|
2329
2370
|
{
|
|
2330
2371
|
recipeId: "passwordless",
|
|
2331
2372
|
email: input.email
|
|
@@ -2339,7 +2380,7 @@ async function completePendingEmailVerification(input) {
|
|
|
2339
2380
|
}
|
|
2340
2381
|
const passwordlessUser = await import_passwordless.default.signInUp({
|
|
2341
2382
|
email: input.email,
|
|
2342
|
-
tenantId
|
|
2383
|
+
tenantId,
|
|
2343
2384
|
userContext: input.userContext
|
|
2344
2385
|
});
|
|
2345
2386
|
verifiedRecipeUserId = passwordlessUser.recipeUserId;
|
|
@@ -2383,7 +2424,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2383
2424
|
rownd_pending_verification: targetPendingVerifications.filter(
|
|
2384
2425
|
(verification) => !isMatchingPendingEmailVerification(
|
|
2385
2426
|
verification,
|
|
2386
|
-
input.email
|
|
2427
|
+
input.email,
|
|
2428
|
+
tenantId
|
|
2387
2429
|
)
|
|
2388
2430
|
)
|
|
2389
2431
|
};
|
|
@@ -2393,8 +2435,8 @@ async function completePendingEmailVerification(input) {
|
|
|
2393
2435
|
recipeUserId: verifiedRecipeUserId
|
|
2394
2436
|
};
|
|
2395
2437
|
}
|
|
2396
|
-
function isMatchingPendingEmailVerification(verification, email) {
|
|
2397
|
-
return verification.field === "email" && verification.value === email;
|
|
2438
|
+
function isMatchingPendingEmailVerification(verification, email, tenantId) {
|
|
2439
|
+
return verification.field === "email" && verification.value === email && (verification.tenantId ?? PUBLIC_TENANT_ID) === tenantId;
|
|
2398
2440
|
}
|
|
2399
2441
|
async function updateUserMetadata(userId, inputMeta) {
|
|
2400
2442
|
const metadata = await getUserMetadata(userId);
|
|
@@ -2412,8 +2454,8 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
2412
2454
|
)
|
|
2413
2455
|
};
|
|
2414
2456
|
}
|
|
2415
|
-
function getPasswordlessEmailLoginMethod(
|
|
2416
|
-
return
|
|
2457
|
+
function getPasswordlessEmailLoginMethod(loginMethods) {
|
|
2458
|
+
return loginMethods.find((method) => {
|
|
2417
2459
|
return method.recipeId === "passwordless" && !!method.email;
|
|
2418
2460
|
});
|
|
2419
2461
|
}
|
|
@@ -2423,6 +2465,7 @@ var import_crypto = require("crypto");
|
|
|
2423
2465
|
var import_supertokens_node3 = __toESM(require("supertokens-node"));
|
|
2424
2466
|
var import_session2 = __toESM(require("supertokens-node/recipe/session"));
|
|
2425
2467
|
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
2468
|
+
var import_multitenancy = __toESM(require("supertokens-node/recipe/multitenancy"));
|
|
2426
2469
|
function isBodyString(body, key) {
|
|
2427
2470
|
return isRecord(body) && typeof body[key] === "string" && body[key].length > 0;
|
|
2428
2471
|
}
|
|
@@ -2487,7 +2530,9 @@ function handleGuestLogin(deps) {
|
|
|
2487
2530
|
return async (req, res, _session, userContext) => {
|
|
2488
2531
|
const startedAt = Date.now();
|
|
2489
2532
|
const guestId = `guest_${(0, import_crypto.randomUUID)()}`;
|
|
2533
|
+
let tenantId;
|
|
2490
2534
|
try {
|
|
2535
|
+
tenantId = resolveTenantId(req);
|
|
2491
2536
|
const body = parseGuestBody(await getJsonBody(req));
|
|
2492
2537
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2493
2538
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2495,7 +2540,7 @@ function handleGuestLogin(deps) {
|
|
|
2495
2540
|
const thirdPartyUserId = thirdPartyId === INSTANT_AUTH_METHOD_ID ? `anon_${(0, import_crypto.randomUUID)()}` : guestId;
|
|
2496
2541
|
const authLevel = thirdPartyId === INSTANT_AUTH_METHOD_ID ? INSTANT_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
2497
2542
|
const response = await import_thirdparty.default.manuallyCreateOrUpdateUser(
|
|
2498
|
-
|
|
2543
|
+
tenantId,
|
|
2499
2544
|
thirdPartyId,
|
|
2500
2545
|
thirdPartyUserId,
|
|
2501
2546
|
`${thirdPartyUserId}@anonymous.local`,
|
|
@@ -2511,7 +2556,7 @@ function handleGuestLogin(deps) {
|
|
|
2511
2556
|
await import_session2.default.createNewSession(
|
|
2512
2557
|
req,
|
|
2513
2558
|
res,
|
|
2514
|
-
|
|
2559
|
+
tenantId,
|
|
2515
2560
|
response.recipeUserId,
|
|
2516
2561
|
{
|
|
2517
2562
|
...buildRowndAudience({}, appVariantId),
|
|
@@ -2526,7 +2571,7 @@ function handleGuestLogin(deps) {
|
|
|
2526
2571
|
deps.telemetryClient.recordSuccess({
|
|
2527
2572
|
outcome: "success",
|
|
2528
2573
|
durationMs: Date.now() - startedAt,
|
|
2529
|
-
tenantId
|
|
2574
|
+
tenantId,
|
|
2530
2575
|
superTokensUserId: response.user.id
|
|
2531
2576
|
});
|
|
2532
2577
|
return {
|
|
@@ -2538,7 +2583,7 @@ function handleGuestLogin(deps) {
|
|
|
2538
2583
|
deps.telemetryClient.recordError({
|
|
2539
2584
|
error,
|
|
2540
2585
|
startedAt,
|
|
2541
|
-
tenantId
|
|
2586
|
+
tenantId
|
|
2542
2587
|
});
|
|
2543
2588
|
return {
|
|
2544
2589
|
status: "ERROR",
|
|
@@ -2550,7 +2595,7 @@ function handleGuestLogin(deps) {
|
|
|
2550
2595
|
function handleMigrate(deps) {
|
|
2551
2596
|
return async (req, res, _session, userContext) => {
|
|
2552
2597
|
const startedAt = Date.now();
|
|
2553
|
-
let tenantId
|
|
2598
|
+
let tenantId;
|
|
2554
2599
|
let rowndUserId;
|
|
2555
2600
|
let superTokensUserId;
|
|
2556
2601
|
let user;
|
|
@@ -2559,6 +2604,7 @@ function handleMigrate(deps) {
|
|
|
2559
2604
|
if (!deps.stConfig.supertokens) {
|
|
2560
2605
|
throw new Error("Supertokens config not found");
|
|
2561
2606
|
}
|
|
2607
|
+
tenantId = resolveTenantId(req);
|
|
2562
2608
|
const parsed = await parseRequest(req);
|
|
2563
2609
|
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2564
2610
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
@@ -2566,13 +2612,16 @@ function handleMigrate(deps) {
|
|
|
2566
2612
|
const rowndUser = await fetchOptionalRowndUserInfo(rowndUserId);
|
|
2567
2613
|
if (!rowndUser) {
|
|
2568
2614
|
logDebugMessage(
|
|
2569
|
-
`Skipping migration because user does not exist in Rownd. tenantId: ${
|
|
2615
|
+
`Skipping migration because user does not exist in Rownd. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2570
2616
|
);
|
|
2571
2617
|
return { status: "OK" };
|
|
2572
2618
|
}
|
|
2573
2619
|
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2574
2620
|
if (!user) {
|
|
2575
|
-
const stUserImport = mapRowndUserToSuperTokens(
|
|
2621
|
+
const stUserImport = mapRowndUserToSuperTokens(
|
|
2622
|
+
rowndUser,
|
|
2623
|
+
tenantId === PUBLIC_TENANT_ID ? void 0 : tenantId
|
|
2624
|
+
);
|
|
2576
2625
|
try {
|
|
2577
2626
|
await importUser(stUserImport, deps.stConfig.supertokens);
|
|
2578
2627
|
clearSuperTokensCoreCallCache(userContext);
|
|
@@ -2590,29 +2639,43 @@ function handleMigrate(deps) {
|
|
|
2590
2639
|
superTokensUserId = user.id;
|
|
2591
2640
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2592
2641
|
logDebugMessage(
|
|
2593
|
-
`User already migrated (race condition). tenantId: ${
|
|
2642
|
+
`User already migrated (race condition). tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2594
2643
|
);
|
|
2595
2644
|
}
|
|
2596
2645
|
logDebugMessage(
|
|
2597
|
-
`User migrated successfully. tenantId: ${
|
|
2646
|
+
`User migrated successfully. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2598
2647
|
);
|
|
2599
2648
|
} else {
|
|
2600
2649
|
superTokensUserId = user.id;
|
|
2601
2650
|
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2602
2651
|
logDebugMessage(
|
|
2603
|
-
`User already migrated. tenantId: ${
|
|
2652
|
+
`User already migrated. tenantId: ${tenantId}, rowndUserId: ${rowndUserId}`
|
|
2604
2653
|
);
|
|
2605
2654
|
}
|
|
2606
2655
|
if (superTokensUserId) {
|
|
2607
2656
|
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
2608
2657
|
}
|
|
2658
|
+
const tenantLoginMethod = user?.loginMethods.find(
|
|
2659
|
+
(method) => method.tenantIds.includes(tenantId)
|
|
2660
|
+
);
|
|
2661
|
+
recipeUserId = tenantLoginMethod?.recipeUserId ?? recipeUserId;
|
|
2609
2662
|
if (!recipeUserId) {
|
|
2610
2663
|
throw new Error("User not found or has no login methods");
|
|
2611
2664
|
}
|
|
2665
|
+
if (!tenantLoginMethod) {
|
|
2666
|
+
const associationResult = await import_multitenancy.default.associateUserToTenant(
|
|
2667
|
+
tenantId,
|
|
2668
|
+
recipeUserId,
|
|
2669
|
+
userContext
|
|
2670
|
+
);
|
|
2671
|
+
if (associationResult.status !== "OK") {
|
|
2672
|
+
throw new Error(`Failed to associate migrated user with tenant: ${associationResult.status}`);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2612
2675
|
await import_session2.default.createNewSession(
|
|
2613
2676
|
req,
|
|
2614
2677
|
res,
|
|
2615
|
-
|
|
2678
|
+
tenantId,
|
|
2616
2679
|
recipeUserId,
|
|
2617
2680
|
{
|
|
2618
2681
|
...buildRowndAudience({}, appVariantId)
|
|
@@ -2621,7 +2684,7 @@ function handleMigrate(deps) {
|
|
|
2621
2684
|
userContext
|
|
2622
2685
|
);
|
|
2623
2686
|
logDebugMessage(
|
|
2624
|
-
`Session migrated successfully. tenantId: ${
|
|
2687
|
+
`Session migrated successfully. tenantId: ${tenantId}, userId: ${superTokensUserId}`
|
|
2625
2688
|
);
|
|
2626
2689
|
deps.telemetryClient.recordSuccess({
|
|
2627
2690
|
outcome: "success",
|
|
@@ -2655,7 +2718,7 @@ function clearSuperTokensCoreCallCache(userContext) {
|
|
|
2655
2718
|
}
|
|
2656
2719
|
function handleGetUser() {
|
|
2657
2720
|
return async (_req, _res, session) => {
|
|
2658
|
-
const user = await getUserById(session.getUserId());
|
|
2721
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2659
2722
|
return {
|
|
2660
2723
|
status: "OK",
|
|
2661
2724
|
...user
|
|
@@ -2681,7 +2744,11 @@ function handleUpdateUser() {
|
|
|
2681
2744
|
return permissionError;
|
|
2682
2745
|
}
|
|
2683
2746
|
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
2684
|
-
await updateUserData(
|
|
2747
|
+
await updateUserData(
|
|
2748
|
+
session.getUserId(),
|
|
2749
|
+
dataWithoutEmail,
|
|
2750
|
+
session.getTenantId()
|
|
2751
|
+
);
|
|
2685
2752
|
}
|
|
2686
2753
|
if (hasEmailUpdate) {
|
|
2687
2754
|
const pendingVerificationResult = await startPendingEmailVerification({
|
|
@@ -2697,7 +2764,7 @@ function handleUpdateUser() {
|
|
|
2697
2764
|
...pendingVerificationResult
|
|
2698
2765
|
};
|
|
2699
2766
|
}
|
|
2700
|
-
const user = await getUserById(session.getUserId());
|
|
2767
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2701
2768
|
return {
|
|
2702
2769
|
status: "OK",
|
|
2703
2770
|
...user
|
|
@@ -2764,7 +2831,7 @@ function handleGetUserField() {
|
|
|
2764
2831
|
if (!field) {
|
|
2765
2832
|
return missingFieldResponse();
|
|
2766
2833
|
}
|
|
2767
|
-
const user = await getUserById(session.getUserId());
|
|
2834
|
+
const user = await getUserById(session.getUserId(), session.getTenantId());
|
|
2768
2835
|
return {
|
|
2769
2836
|
status: "OK",
|
|
2770
2837
|
value: user.data[field]
|
|
@@ -2798,9 +2865,11 @@ function handleUpdateUserField() {
|
|
|
2798
2865
|
if (permissionError) {
|
|
2799
2866
|
return permissionError;
|
|
2800
2867
|
}
|
|
2801
|
-
const updateUserDataResult = await updateUserData(
|
|
2802
|
-
|
|
2803
|
-
|
|
2868
|
+
const updateUserDataResult = await updateUserData(
|
|
2869
|
+
session.getUserId(),
|
|
2870
|
+
{ [field]: payload.value },
|
|
2871
|
+
session.getTenantId()
|
|
2872
|
+
);
|
|
2804
2873
|
return {
|
|
2805
2874
|
status: "OK",
|
|
2806
2875
|
...updateUserDataResult
|
|
@@ -2839,6 +2908,7 @@ var init = createPluginInitFunction(
|
|
|
2839
2908
|
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2840
2909
|
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2841
2910
|
const clientDomain = input?.userContext?.rowndClientDomain;
|
|
2911
|
+
const oauthLoginChallenge = input?.userContext?.rowndOAuthLoginChallenge;
|
|
2842
2912
|
const clientDomainKey = clientDomain ?? (displayContext === "mobile_app" ? "mobile" : "browser");
|
|
2843
2913
|
const clientBaseUrl = pluginConfig2.clientDomains?.[clientDomainKey];
|
|
2844
2914
|
const bootstrapParams = {
|
|
@@ -2846,7 +2916,8 @@ var init = createPluginInitFunction(
|
|
|
2846
2916
|
...hubBootstrapParams ?? {},
|
|
2847
2917
|
...typeof appVariantId === "string" ? { appVariantId } : {},
|
|
2848
2918
|
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2849
|
-
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2919
|
+
...typeof redirectToPath === "string" ? { redirectToPath } : {},
|
|
2920
|
+
...typeof oauthLoginChallenge === "string" ? { oauthLoginChallenge } : {}
|
|
2850
2921
|
};
|
|
2851
2922
|
const rewrittenLink = input[linkKey] ? clientBaseUrl ? rewriteLinkToBaseUrl(
|
|
2852
2923
|
input[linkKey],
|
|
@@ -3132,6 +3203,12 @@ var init = createPluginInitFunction(
|
|
|
3132
3203
|
if (appVariantId) {
|
|
3133
3204
|
input.userContext.rowndAppVariantId = appVariantId;
|
|
3134
3205
|
}
|
|
3206
|
+
const oauthLoginChallenge = getRequestedOAuthLoginChallengeFromRequest(
|
|
3207
|
+
input.options.req
|
|
3208
|
+
);
|
|
3209
|
+
if (oauthLoginChallenge) {
|
|
3210
|
+
input.userContext.rowndOAuthLoginChallenge = oauthLoginChallenge;
|
|
3211
|
+
}
|
|
3135
3212
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
3136
3213
|
return originalImplementation.createCodePOST(input);
|
|
3137
3214
|
},
|
|
@@ -3275,6 +3352,7 @@ var init = createPluginInitFunction(
|
|
|
3275
3352
|
const verificationResult = await completePendingEmailVerification({
|
|
3276
3353
|
recipeUserId: response.user.recipeUserId,
|
|
3277
3354
|
email: response.user.email,
|
|
3355
|
+
tenantId: input.tenantId,
|
|
3278
3356
|
userContext: input.userContext
|
|
3279
3357
|
});
|
|
3280
3358
|
const session = input.session;
|