@supertokens-plugins/rownd-nodejs 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bulkMigrate.js +15 -11
- package/dist/generateAppConfig.js +3 -1
- package/dist/index.js +47 -22
- package/dist/index.mjs +47 -22
- package/dist/initConfig.js +10 -0
- package/dist/setupCoreInstance.js +111 -46
- package/package.json +1 -1
package/dist/bulkMigrate.js
CHANGED
|
@@ -40,6 +40,7 @@ var fs2 = __toESM(require("fs/promises"));
|
|
|
40
40
|
var import_zod2 = require("zod");
|
|
41
41
|
|
|
42
42
|
// src/rownd-compatibility.ts
|
|
43
|
+
var import_node_crypto = require("crypto");
|
|
43
44
|
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
44
45
|
var import_usermetadata = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
45
46
|
|
|
@@ -55,6 +56,11 @@ var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
|
55
56
|
"google_id",
|
|
56
57
|
"apple_id"
|
|
57
58
|
]);
|
|
59
|
+
var SUPERTOKENS_FAKE_EMAIL_DOMAIN = "stfakeemail.supertokens.com";
|
|
60
|
+
function buildSuperTokensFakeEmail(thirdPartyUserId, thirdPartyId) {
|
|
61
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(`${thirdPartyId}:${thirdPartyUserId}`).digest("hex").slice(0, 32);
|
|
62
|
+
return `st-${thirdPartyId}-${hash}@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`;
|
|
63
|
+
}
|
|
58
64
|
function isIdentityField(field) {
|
|
59
65
|
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
60
66
|
}
|
|
@@ -66,27 +72,23 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
66
72
|
throw new Error("Rownd user has no user_id");
|
|
67
73
|
}
|
|
68
74
|
if (rowndUserData.google_id) {
|
|
69
|
-
|
|
70
|
-
throw new Error("Rownd Google user is missing email");
|
|
71
|
-
}
|
|
75
|
+
const googleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.google_id, "google");
|
|
72
76
|
loginMethods.push({
|
|
73
77
|
recipeId: "thirdparty",
|
|
74
78
|
thirdPartyId: "google",
|
|
75
79
|
thirdPartyUserId: rowndUserData.google_id,
|
|
76
|
-
email:
|
|
77
|
-
isVerified: !!rowndUserVerifiedData.google_id
|
|
80
|
+
email: googleEmail,
|
|
81
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
78
82
|
});
|
|
79
83
|
}
|
|
80
84
|
if (rowndUserData.apple_id) {
|
|
81
|
-
|
|
82
|
-
throw new Error("Rownd Apple user is missing email");
|
|
83
|
-
}
|
|
85
|
+
const appleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.apple_id, "apple");
|
|
84
86
|
loginMethods.push({
|
|
85
87
|
recipeId: "thirdparty",
|
|
86
88
|
thirdPartyId: "apple",
|
|
87
89
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
88
|
-
email:
|
|
89
|
-
isVerified: !!rowndUserVerifiedData.apple_id
|
|
90
|
+
email: appleEmail,
|
|
91
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
90
92
|
});
|
|
91
93
|
}
|
|
92
94
|
if (rowndUserData.phone_number) {
|
|
@@ -159,7 +161,9 @@ var ConfigSchema = import_zod.z.object({
|
|
|
159
161
|
appKey: import_zod.z.string(),
|
|
160
162
|
appSecret: import_zod.z.string(),
|
|
161
163
|
bearerToken: import_zod.z.string().optional(),
|
|
162
|
-
pageSize: import_zod.z.number().int().positive()
|
|
164
|
+
pageSize: import_zod.z.number().int().positive(),
|
|
165
|
+
oidcClientSecrets: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
166
|
+
provisionOidcClientIdAliases: import_zod.z.boolean().default(true)
|
|
163
167
|
}),
|
|
164
168
|
supertokens: import_zod.z.object({
|
|
165
169
|
connectionURI: import_zod.z.string(),
|
|
@@ -61,7 +61,9 @@ var ConfigSchema = import_zod.z.object({
|
|
|
61
61
|
appKey: import_zod.z.string(),
|
|
62
62
|
appSecret: import_zod.z.string(),
|
|
63
63
|
bearerToken: import_zod.z.string().optional(),
|
|
64
|
-
pageSize: import_zod.z.number().int().positive()
|
|
64
|
+
pageSize: import_zod.z.number().int().positive(),
|
|
65
|
+
oidcClientSecrets: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
66
|
+
provisionOidcClientIdAliases: import_zod.z.boolean().default(true)
|
|
65
67
|
}),
|
|
66
68
|
supertokens: import_zod.z.object({
|
|
67
69
|
connectionURI: import_zod.z.string(),
|
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,7 @@ 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
869
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
870
870
|
try {
|
|
871
871
|
const url = new URL(inputUrl);
|
|
@@ -1066,7 +1066,8 @@ function getMagicLinkBootstrapParams(input) {
|
|
|
1066
1066
|
...input.appVariantId ? { appVariantId: input.appVariantId } : {},
|
|
1067
1067
|
...input.displayContext ? { displayContext: input.displayContext } : {},
|
|
1068
1068
|
...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
|
|
1069
|
-
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
|
|
1069
|
+
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {},
|
|
1070
|
+
...input.oauthLoginChallenge ? { oauthLoginChallenge: input.oauthLoginChallenge } : {}
|
|
1070
1071
|
};
|
|
1071
1072
|
}
|
|
1072
1073
|
function rewriteMagicLink(input) {
|
|
@@ -1092,6 +1093,10 @@ function getRequestedRedirectToPathFromRequest(req) {
|
|
|
1092
1093
|
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1093
1094
|
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1094
1095
|
}
|
|
1096
|
+
function getRequestedOAuthLoginChallengeFromRequest(req) {
|
|
1097
|
+
const loginChallenge = req.getKeyValueFromQuery(ROWND_OAUTH_LOGIN_CHALLENGE_PARAM);
|
|
1098
|
+
return typeof loginChallenge === "string" && loginChallenge.length > 0 ? loginChallenge : void 0;
|
|
1099
|
+
}
|
|
1095
1100
|
|
|
1096
1101
|
// src/config.ts
|
|
1097
1102
|
var pluginConfig;
|
|
@@ -1464,6 +1469,7 @@ function buildAuthMobileConfig(authMobile) {
|
|
|
1464
1469
|
}
|
|
1465
1470
|
|
|
1466
1471
|
// src/rownd-compatibility.ts
|
|
1472
|
+
var import_node_crypto = require("crypto");
|
|
1467
1473
|
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
1468
1474
|
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1469
1475
|
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -1477,6 +1483,14 @@ var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
|
1477
1483
|
"original_rownd_user",
|
|
1478
1484
|
"rownd_pending_verification"
|
|
1479
1485
|
]);
|
|
1486
|
+
var SUPERTOKENS_FAKE_EMAIL_DOMAIN = "stfakeemail.supertokens.com";
|
|
1487
|
+
function isSuperTokensFakeEmail(email) {
|
|
1488
|
+
return typeof email === "string" && email.toLowerCase().endsWith(`@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`);
|
|
1489
|
+
}
|
|
1490
|
+
function buildSuperTokensFakeEmail(thirdPartyUserId, thirdPartyId) {
|
|
1491
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(`${thirdPartyId}:${thirdPartyUserId}`).digest("hex").slice(0, 32);
|
|
1492
|
+
return `st-${thirdPartyId}-${hash}@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`;
|
|
1493
|
+
}
|
|
1480
1494
|
function isIdentityField(field) {
|
|
1481
1495
|
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1482
1496
|
}
|
|
@@ -1491,27 +1505,23 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1491
1505
|
throw new Error("Rownd user has no user_id");
|
|
1492
1506
|
}
|
|
1493
1507
|
if (rowndUserData.google_id) {
|
|
1494
|
-
|
|
1495
|
-
throw new Error("Rownd Google user is missing email");
|
|
1496
|
-
}
|
|
1508
|
+
const googleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.google_id, "google");
|
|
1497
1509
|
loginMethods.push({
|
|
1498
1510
|
recipeId: "thirdparty",
|
|
1499
1511
|
thirdPartyId: "google",
|
|
1500
1512
|
thirdPartyUserId: rowndUserData.google_id,
|
|
1501
|
-
email:
|
|
1502
|
-
isVerified: !!rowndUserVerifiedData.google_id
|
|
1513
|
+
email: googleEmail,
|
|
1514
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
1503
1515
|
});
|
|
1504
1516
|
}
|
|
1505
1517
|
if (rowndUserData.apple_id) {
|
|
1506
|
-
|
|
1507
|
-
throw new Error("Rownd Apple user is missing email");
|
|
1508
|
-
}
|
|
1518
|
+
const appleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.apple_id, "apple");
|
|
1509
1519
|
loginMethods.push({
|
|
1510
1520
|
recipeId: "thirdparty",
|
|
1511
1521
|
thirdPartyId: "apple",
|
|
1512
1522
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
1513
|
-
email:
|
|
1514
|
-
isVerified: !!rowndUserVerifiedData.apple_id
|
|
1523
|
+
email: appleEmail,
|
|
1524
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
1515
1525
|
});
|
|
1516
1526
|
}
|
|
1517
1527
|
if (rowndUserData.phone_number) {
|
|
@@ -1696,7 +1706,7 @@ async function buildStandardOAuthClaims(user, scopes) {
|
|
|
1696
1706
|
const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
|
|
1697
1707
|
const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
|
|
1698
1708
|
if (scopes.includes("email")) {
|
|
1699
|
-
const email = firstString(rowndData.email)
|
|
1709
|
+
const email = firstRealEmail(firstString(rowndData.email), ...user.emails);
|
|
1700
1710
|
if (email) {
|
|
1701
1711
|
claims.email = email;
|
|
1702
1712
|
claims.email_verified = isOAuthClaimVerified(
|
|
@@ -1763,6 +1773,11 @@ function firstString(value) {
|
|
|
1763
1773
|
}
|
|
1764
1774
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1765
1775
|
}
|
|
1776
|
+
function firstRealEmail(...values) {
|
|
1777
|
+
return values.find((entry) => {
|
|
1778
|
+
return typeof entry === "string" && entry.length > 0 && !isSuperTokensFakeEmail(entry);
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1766
1781
|
function isOAuthClaimVerified(value, expectedValue, fallback) {
|
|
1767
1782
|
return value === true || value === expectedValue || fallback;
|
|
1768
1783
|
}
|
|
@@ -2046,6 +2061,7 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2046
2061
|
})}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
|
|
2047
2062
|
codeInfo.preAuthSessionId
|
|
2048
2063
|
)}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
|
|
2064
|
+
const oauthLoginChallenge = userContext.rowndOAuthLoginChallenge;
|
|
2049
2065
|
const rewrittenUrl = new URL(rewriteMagicLink({
|
|
2050
2066
|
magicLink,
|
|
2051
2067
|
clientDomain,
|
|
@@ -2056,7 +2072,8 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2056
2072
|
appVariantId,
|
|
2057
2073
|
displayContext: input.displayContext,
|
|
2058
2074
|
redirectToPath,
|
|
2059
|
-
clientDomainKey: input.clientDomain
|
|
2075
|
+
clientDomainKey: input.clientDomain,
|
|
2076
|
+
oauthLoginChallenge: typeof oauthLoginChallenge === "string" ? oauthLoginChallenge : void 0
|
|
2060
2077
|
})
|
|
2061
2078
|
}));
|
|
2062
2079
|
rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
|
|
@@ -2107,7 +2124,7 @@ async function getUserById(userId) {
|
|
|
2107
2124
|
};
|
|
2108
2125
|
for (const method of stUser.loginMethods) {
|
|
2109
2126
|
if (method.recipeId === "passwordless") {
|
|
2110
|
-
if (method.email) {
|
|
2127
|
+
if (method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2111
2128
|
verifiedData.email = method.email;
|
|
2112
2129
|
if (data.email === void 0) data.email = method.email;
|
|
2113
2130
|
}
|
|
@@ -2119,10 +2136,10 @@ async function getUserById(userId) {
|
|
|
2119
2136
|
} else if (method.recipeId === "thirdparty") {
|
|
2120
2137
|
const thirdPartyId = getThirdPartyId(method);
|
|
2121
2138
|
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
2122
|
-
if (method.verified && method.email) {
|
|
2139
|
+
if (method.verified && method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2123
2140
|
verifiedData.email = method.email;
|
|
2124
2141
|
}
|
|
2125
|
-
if (method.email && data.email === void 0) {
|
|
2142
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2126
2143
|
data.email = method.email;
|
|
2127
2144
|
}
|
|
2128
2145
|
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
@@ -2134,7 +2151,7 @@ async function getUserById(userId) {
|
|
|
2134
2151
|
verifiedData.apple_id = thirdPartyUserId;
|
|
2135
2152
|
}
|
|
2136
2153
|
} else if (method.recipeId === "emailpassword") {
|
|
2137
|
-
if (method.email && data.email === void 0) {
|
|
2154
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2138
2155
|
data.email = method.email;
|
|
2139
2156
|
}
|
|
2140
2157
|
}
|
|
@@ -2839,6 +2856,7 @@ var init = createPluginInitFunction(
|
|
|
2839
2856
|
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2840
2857
|
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2841
2858
|
const clientDomain = input?.userContext?.rowndClientDomain;
|
|
2859
|
+
const oauthLoginChallenge = input?.userContext?.rowndOAuthLoginChallenge;
|
|
2842
2860
|
const clientDomainKey = clientDomain ?? (displayContext === "mobile_app" ? "mobile" : "browser");
|
|
2843
2861
|
const clientBaseUrl = pluginConfig2.clientDomains?.[clientDomainKey];
|
|
2844
2862
|
const bootstrapParams = {
|
|
@@ -2846,7 +2864,8 @@ var init = createPluginInitFunction(
|
|
|
2846
2864
|
...hubBootstrapParams ?? {},
|
|
2847
2865
|
...typeof appVariantId === "string" ? { appVariantId } : {},
|
|
2848
2866
|
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2849
|
-
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2867
|
+
...typeof redirectToPath === "string" ? { redirectToPath } : {},
|
|
2868
|
+
...typeof oauthLoginChallenge === "string" ? { oauthLoginChallenge } : {}
|
|
2850
2869
|
};
|
|
2851
2870
|
const rewrittenLink = input[linkKey] ? clientBaseUrl ? rewriteLinkToBaseUrl(
|
|
2852
2871
|
input[linkKey],
|
|
@@ -3132,6 +3151,12 @@ var init = createPluginInitFunction(
|
|
|
3132
3151
|
if (appVariantId) {
|
|
3133
3152
|
input.userContext.rowndAppVariantId = appVariantId;
|
|
3134
3153
|
}
|
|
3154
|
+
const oauthLoginChallenge = getRequestedOAuthLoginChallengeFromRequest(
|
|
3155
|
+
input.options.req
|
|
3156
|
+
);
|
|
3157
|
+
if (oauthLoginChallenge) {
|
|
3158
|
+
input.userContext.rowndOAuthLoginChallenge = oauthLoginChallenge;
|
|
3159
|
+
}
|
|
3135
3160
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
3136
3161
|
return originalImplementation.createCodePOST(input);
|
|
3137
3162
|
},
|
package/dist/index.mjs
CHANGED
|
@@ -616,12 +616,11 @@ function createLoggerConfig() {
|
|
|
616
616
|
}
|
|
617
617
|
var config = createLoggerConfig();
|
|
618
618
|
var logger = common_default(config);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
formatters.o = function(value) {
|
|
619
|
+
if (logger.formatters) {
|
|
620
|
+
logger.formatters.o = function(value) {
|
|
622
621
|
return util.inspect(value, { colors: this.useColors, compact: true }).split("\n").map((line) => line.trim()).join(" ");
|
|
623
622
|
};
|
|
624
|
-
formatters.O = function(value) {
|
|
623
|
+
logger.formatters.O = function(value) {
|
|
625
624
|
return util.inspect(value, { colors: this.useColors, compact: false });
|
|
626
625
|
};
|
|
627
626
|
}
|
|
@@ -843,6 +842,7 @@ var RowndPluginError = class extends Error {
|
|
|
843
842
|
};
|
|
844
843
|
|
|
845
844
|
// src/utils.ts
|
|
845
|
+
var ROWND_OAUTH_LOGIN_CHALLENGE_PARAM = "rownd_oauth_login_challenge";
|
|
846
846
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
847
847
|
try {
|
|
848
848
|
const url = new URL(inputUrl);
|
|
@@ -1043,7 +1043,8 @@ function getMagicLinkBootstrapParams(input) {
|
|
|
1043
1043
|
...input.appVariantId ? { appVariantId: input.appVariantId } : {},
|
|
1044
1044
|
...input.displayContext ? { displayContext: input.displayContext } : {},
|
|
1045
1045
|
...input.redirectToPath ? { redirectToPath: input.redirectToPath } : {},
|
|
1046
|
-
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {}
|
|
1046
|
+
...input.clientDomainKey ? { clientDomain: input.clientDomainKey } : {},
|
|
1047
|
+
...input.oauthLoginChallenge ? { oauthLoginChallenge: input.oauthLoginChallenge } : {}
|
|
1047
1048
|
};
|
|
1048
1049
|
}
|
|
1049
1050
|
function rewriteMagicLink(input) {
|
|
@@ -1069,6 +1070,10 @@ function getRequestedRedirectToPathFromRequest(req) {
|
|
|
1069
1070
|
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1070
1071
|
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1071
1072
|
}
|
|
1073
|
+
function getRequestedOAuthLoginChallengeFromRequest(req) {
|
|
1074
|
+
const loginChallenge = req.getKeyValueFromQuery(ROWND_OAUTH_LOGIN_CHALLENGE_PARAM);
|
|
1075
|
+
return typeof loginChallenge === "string" && loginChallenge.length > 0 ? loginChallenge : void 0;
|
|
1076
|
+
}
|
|
1072
1077
|
|
|
1073
1078
|
// src/config.ts
|
|
1074
1079
|
var pluginConfig;
|
|
@@ -1441,6 +1446,7 @@ function buildAuthMobileConfig(authMobile) {
|
|
|
1441
1446
|
}
|
|
1442
1447
|
|
|
1443
1448
|
// src/rownd-compatibility.ts
|
|
1449
|
+
import { createHash } from "crypto";
|
|
1444
1450
|
import SuperTokens from "supertokens-node";
|
|
1445
1451
|
import UserMetadata2 from "supertokens-node/recipe/usermetadata";
|
|
1446
1452
|
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -1454,6 +1460,14 @@ var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
|
1454
1460
|
"original_rownd_user",
|
|
1455
1461
|
"rownd_pending_verification"
|
|
1456
1462
|
]);
|
|
1463
|
+
var SUPERTOKENS_FAKE_EMAIL_DOMAIN = "stfakeemail.supertokens.com";
|
|
1464
|
+
function isSuperTokensFakeEmail(email) {
|
|
1465
|
+
return typeof email === "string" && email.toLowerCase().endsWith(`@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`);
|
|
1466
|
+
}
|
|
1467
|
+
function buildSuperTokensFakeEmail(thirdPartyUserId, thirdPartyId) {
|
|
1468
|
+
const hash = createHash("sha256").update(`${thirdPartyId}:${thirdPartyUserId}`).digest("hex").slice(0, 32);
|
|
1469
|
+
return `st-${thirdPartyId}-${hash}@${SUPERTOKENS_FAKE_EMAIL_DOMAIN}`;
|
|
1470
|
+
}
|
|
1457
1471
|
function isIdentityField(field) {
|
|
1458
1472
|
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1459
1473
|
}
|
|
@@ -1468,27 +1482,23 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1468
1482
|
throw new Error("Rownd user has no user_id");
|
|
1469
1483
|
}
|
|
1470
1484
|
if (rowndUserData.google_id) {
|
|
1471
|
-
|
|
1472
|
-
throw new Error("Rownd Google user is missing email");
|
|
1473
|
-
}
|
|
1485
|
+
const googleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.google_id, "google");
|
|
1474
1486
|
loginMethods.push({
|
|
1475
1487
|
recipeId: "thirdparty",
|
|
1476
1488
|
thirdPartyId: "google",
|
|
1477
1489
|
thirdPartyUserId: rowndUserData.google_id,
|
|
1478
|
-
email:
|
|
1479
|
-
isVerified: !!rowndUserVerifiedData.google_id
|
|
1490
|
+
email: googleEmail,
|
|
1491
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.google_id
|
|
1480
1492
|
});
|
|
1481
1493
|
}
|
|
1482
1494
|
if (rowndUserData.apple_id) {
|
|
1483
|
-
|
|
1484
|
-
throw new Error("Rownd Apple user is missing email");
|
|
1485
|
-
}
|
|
1495
|
+
const appleEmail = rowndUserData.email ?? buildSuperTokensFakeEmail(rowndUserData.apple_id, "apple");
|
|
1486
1496
|
loginMethods.push({
|
|
1487
1497
|
recipeId: "thirdparty",
|
|
1488
1498
|
thirdPartyId: "apple",
|
|
1489
1499
|
thirdPartyUserId: rowndUserData.apple_id,
|
|
1490
|
-
email:
|
|
1491
|
-
isVerified: !!rowndUserVerifiedData.apple_id
|
|
1500
|
+
email: appleEmail,
|
|
1501
|
+
isVerified: !!rowndUserData.email && !!rowndUserVerifiedData.apple_id
|
|
1492
1502
|
});
|
|
1493
1503
|
}
|
|
1494
1504
|
if (rowndUserData.phone_number) {
|
|
@@ -1673,7 +1683,7 @@ async function buildStandardOAuthClaims(user, scopes) {
|
|
|
1673
1683
|
const rowndData = isJsonRecord(metadata.original_rownd_user?.data) ? metadata.original_rownd_user.data : {};
|
|
1674
1684
|
const verifiedData = isJsonRecord(metadata.original_rownd_user?.verified_data) ? metadata.original_rownd_user.verified_data : {};
|
|
1675
1685
|
if (scopes.includes("email")) {
|
|
1676
|
-
const email = firstString(rowndData.email)
|
|
1686
|
+
const email = firstRealEmail(firstString(rowndData.email), ...user.emails);
|
|
1677
1687
|
if (email) {
|
|
1678
1688
|
claims.email = email;
|
|
1679
1689
|
claims.email_verified = isOAuthClaimVerified(
|
|
@@ -1740,6 +1750,11 @@ function firstString(value) {
|
|
|
1740
1750
|
}
|
|
1741
1751
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1742
1752
|
}
|
|
1753
|
+
function firstRealEmail(...values) {
|
|
1754
|
+
return values.find((entry) => {
|
|
1755
|
+
return typeof entry === "string" && entry.length > 0 && !isSuperTokensFakeEmail(entry);
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1743
1758
|
function isOAuthClaimVerified(value, expectedValue, fallback) {
|
|
1744
1759
|
return value === true || value === expectedValue || fallback;
|
|
1745
1760
|
}
|
|
@@ -2023,6 +2038,7 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2023
2038
|
})}${getAppInfoString(stConfig.appInfo.websiteBasePath)}/verify?preAuthSessionId=${encodeURIComponent(
|
|
2024
2039
|
codeInfo.preAuthSessionId
|
|
2025
2040
|
)}&tenantId=${encodeURIComponent(tenantId)}#${encodeURIComponent(codeInfo.linkCode)}`;
|
|
2041
|
+
const oauthLoginChallenge = userContext.rowndOAuthLoginChallenge;
|
|
2026
2042
|
const rewrittenUrl = new URL(rewriteMagicLink({
|
|
2027
2043
|
magicLink,
|
|
2028
2044
|
clientDomain,
|
|
@@ -2033,7 +2049,8 @@ async function createMagicLinkWithConfirmationBypass(input) {
|
|
|
2033
2049
|
appVariantId,
|
|
2034
2050
|
displayContext: input.displayContext,
|
|
2035
2051
|
redirectToPath,
|
|
2036
|
-
clientDomainKey: input.clientDomain
|
|
2052
|
+
clientDomainKey: input.clientDomain,
|
|
2053
|
+
oauthLoginChallenge: typeof oauthLoginChallenge === "string" ? oauthLoginChallenge : void 0
|
|
2037
2054
|
})
|
|
2038
2055
|
}));
|
|
2039
2056
|
rewrittenUrl.searchParams.set(PASSWORDLESS_BYPASS_DEVICE_CONFIRMATION_PARAM, "true");
|
|
@@ -2084,7 +2101,7 @@ async function getUserById(userId) {
|
|
|
2084
2101
|
};
|
|
2085
2102
|
for (const method of stUser.loginMethods) {
|
|
2086
2103
|
if (method.recipeId === "passwordless") {
|
|
2087
|
-
if (method.email) {
|
|
2104
|
+
if (method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2088
2105
|
verifiedData.email = method.email;
|
|
2089
2106
|
if (data.email === void 0) data.email = method.email;
|
|
2090
2107
|
}
|
|
@@ -2096,10 +2113,10 @@ async function getUserById(userId) {
|
|
|
2096
2113
|
} else if (method.recipeId === "thirdparty") {
|
|
2097
2114
|
const thirdPartyId = getThirdPartyId(method);
|
|
2098
2115
|
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
2099
|
-
if (method.verified && method.email) {
|
|
2116
|
+
if (method.verified && method.email && !isSuperTokensFakeEmail(method.email)) {
|
|
2100
2117
|
verifiedData.email = method.email;
|
|
2101
2118
|
}
|
|
2102
|
-
if (method.email && data.email === void 0) {
|
|
2119
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2103
2120
|
data.email = method.email;
|
|
2104
2121
|
}
|
|
2105
2122
|
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
@@ -2111,7 +2128,7 @@ async function getUserById(userId) {
|
|
|
2111
2128
|
verifiedData.apple_id = thirdPartyUserId;
|
|
2112
2129
|
}
|
|
2113
2130
|
} else if (method.recipeId === "emailpassword") {
|
|
2114
|
-
if (method.email && data.email === void 0) {
|
|
2131
|
+
if (method.email && !isSuperTokensFakeEmail(method.email) && data.email === void 0) {
|
|
2115
2132
|
data.email = method.email;
|
|
2116
2133
|
}
|
|
2117
2134
|
}
|
|
@@ -2816,6 +2833,7 @@ var init = createPluginInitFunction(
|
|
|
2816
2833
|
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2817
2834
|
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2818
2835
|
const clientDomain = input?.userContext?.rowndClientDomain;
|
|
2836
|
+
const oauthLoginChallenge = input?.userContext?.rowndOAuthLoginChallenge;
|
|
2819
2837
|
const clientDomainKey = clientDomain ?? (displayContext === "mobile_app" ? "mobile" : "browser");
|
|
2820
2838
|
const clientBaseUrl = pluginConfig2.clientDomains?.[clientDomainKey];
|
|
2821
2839
|
const bootstrapParams = {
|
|
@@ -2823,7 +2841,8 @@ var init = createPluginInitFunction(
|
|
|
2823
2841
|
...hubBootstrapParams ?? {},
|
|
2824
2842
|
...typeof appVariantId === "string" ? { appVariantId } : {},
|
|
2825
2843
|
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2826
|
-
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2844
|
+
...typeof redirectToPath === "string" ? { redirectToPath } : {},
|
|
2845
|
+
...typeof oauthLoginChallenge === "string" ? { oauthLoginChallenge } : {}
|
|
2827
2846
|
};
|
|
2828
2847
|
const rewrittenLink = input[linkKey] ? clientBaseUrl ? rewriteLinkToBaseUrl(
|
|
2829
2848
|
input[linkKey],
|
|
@@ -3109,6 +3128,12 @@ var init = createPluginInitFunction(
|
|
|
3109
3128
|
if (appVariantId) {
|
|
3110
3129
|
input.userContext.rowndAppVariantId = appVariantId;
|
|
3111
3130
|
}
|
|
3131
|
+
const oauthLoginChallenge = getRequestedOAuthLoginChallengeFromRequest(
|
|
3132
|
+
input.options.req
|
|
3133
|
+
);
|
|
3134
|
+
if (oauthLoginChallenge) {
|
|
3135
|
+
input.userContext.rowndOAuthLoginChallenge = oauthLoginChallenge;
|
|
3136
|
+
}
|
|
3112
3137
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
3113
3138
|
return originalImplementation.createCodePOST(input);
|
|
3114
3139
|
},
|
package/dist/initConfig.js
CHANGED
|
@@ -49,6 +49,16 @@ rownd:
|
|
|
49
49
|
# bearerToken: <ROWND_API_BEARER_TOKEN>
|
|
50
50
|
# Number of Rownd users to request per page.
|
|
51
51
|
pageSize: 100
|
|
52
|
+
# Rownd stores existing OIDC credential secrets hashed. Provide plaintext
|
|
53
|
+
# values here when migrating OIDC clients. Keys can be credential client IDs
|
|
54
|
+
# (key_...) and OIDC client configuration IDs (oc_...).
|
|
55
|
+
oidcClientSecrets: {}
|
|
56
|
+
# oidcClientSecrets:
|
|
57
|
+
# key_123: ras_plaintext_secret
|
|
58
|
+
# oc_123: ras_plaintext_secret_for_oidc_client_id_alias
|
|
59
|
+
# Rownd accepts the OIDC client configuration ID (oc_...) as a client_id in
|
|
60
|
+
# addition to each linked credential client_id (key_...).
|
|
61
|
+
provisionOidcClientIdAliases: true
|
|
52
62
|
|
|
53
63
|
supertokens:
|
|
54
64
|
connectionURI: <CONNECTION_URI>
|
|
@@ -62,7 +62,9 @@ var ConfigSchema = import_zod.z.object({
|
|
|
62
62
|
appKey: import_zod.z.string(),
|
|
63
63
|
appSecret: import_zod.z.string(),
|
|
64
64
|
bearerToken: import_zod.z.string().optional(),
|
|
65
|
-
pageSize: import_zod.z.number().int().positive()
|
|
65
|
+
pageSize: import_zod.z.number().int().positive(),
|
|
66
|
+
oidcClientSecrets: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
67
|
+
provisionOidcClientIdAliases: import_zod.z.boolean().default(true)
|
|
66
68
|
}),
|
|
67
69
|
supertokens: import_zod.z.object({
|
|
68
70
|
connectionURI: import_zod.z.string(),
|
|
@@ -187,55 +189,118 @@ async function provisionSuperTokensInfrastructure(config, oidcClients) {
|
|
|
187
189
|
if (config.supertokens.apiKey) {
|
|
188
190
|
headers["api-key"] = config.supertokens.apiKey;
|
|
189
191
|
}
|
|
192
|
+
const provisionedClientIds = /* @__PURE__ */ new Set();
|
|
190
193
|
for (const oidcClient of oidcClients) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
const credentials = oidcClient.credentials ?? [];
|
|
195
|
+
for (const credential of credentials) {
|
|
196
|
+
await createSuperTokensOAuthClient({
|
|
197
|
+
config,
|
|
198
|
+
headers,
|
|
199
|
+
oidcClient,
|
|
200
|
+
clientId: credential.client_id,
|
|
201
|
+
clientSecret: resolveOidcClientSecret(
|
|
202
|
+
config,
|
|
203
|
+
credential.client_id,
|
|
204
|
+
credential.secret
|
|
205
|
+
),
|
|
206
|
+
appVariantId: credential.app_variant_id,
|
|
207
|
+
provisionedClientIds
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (config.rownd.provisionOidcClientIdAliases && credentials.length > 0) {
|
|
211
|
+
const firstCredential = credentials[0];
|
|
212
|
+
if (!firstCredential) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const firstCredentialSecret = resolveOidcClientSecret(
|
|
216
|
+
config,
|
|
217
|
+
firstCredential.client_id,
|
|
218
|
+
firstCredential.secret
|
|
194
219
|
);
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
).toString(),
|
|
200
|
-
{
|
|
201
|
-
method: "POST",
|
|
202
|
-
headers,
|
|
203
|
-
body: JSON.stringify({
|
|
204
|
-
clientName: oidcClient.name || credential.client_id || "Rownd Client",
|
|
205
|
-
clientId: credential.client_id,
|
|
206
|
-
clientSecret: credential.secret,
|
|
207
|
-
redirectUris: oidcClient.config.redirect_uris || [],
|
|
208
|
-
postLogoutRedirectUris: oidcClient.config.post_logout_uris || [],
|
|
209
|
-
scope: oidcClient.config.allowed_scopes?.join(" "),
|
|
210
|
-
responseTypes: ["code", "token", "id_token"],
|
|
211
|
-
grantTypes: [
|
|
212
|
-
"authorization_code",
|
|
213
|
-
"refresh_token",
|
|
214
|
-
"client_credentials",
|
|
215
|
-
"implicit"
|
|
216
|
-
],
|
|
217
|
-
tokenEndpointAuthMethod: oidcClient.config.token_endpoint_auth_method ?? "client_secret_basic",
|
|
218
|
-
audience: [`app:${config.rownd.appId}`],
|
|
219
|
-
metadata: {
|
|
220
|
-
rowndOidcClientId: oidcClient.id,
|
|
221
|
-
rowndAppVariantId: credential.app_variant_id || void 0,
|
|
222
|
-
rowndAllowedScopes: oidcClient.config.allowed_scopes || [],
|
|
223
|
-
rowndApplicationType: oidcClient.config.application_type,
|
|
224
|
-
rowndIsPkceRequired: oidcClient.config.is_pkce_required,
|
|
225
|
-
rowndIsPkceSupported: oidcClient.config.is_pkce_supported,
|
|
226
|
-
rowndDeviceFlowEnabled: oidcClient.config.device_flow_enabled
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
}
|
|
220
|
+
const aliasSecret = resolveOidcClientSecret(
|
|
221
|
+
config,
|
|
222
|
+
oidcClient.id,
|
|
223
|
+
firstCredentialSecret
|
|
230
224
|
);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
225
|
+
await createSuperTokensOAuthClient({
|
|
226
|
+
config,
|
|
227
|
+
headers,
|
|
228
|
+
oidcClient,
|
|
229
|
+
clientId: oidcClient.id,
|
|
230
|
+
clientSecret: aliasSecret,
|
|
231
|
+
isOidcClientIdAlias: true,
|
|
232
|
+
provisionedClientIds
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function resolveOidcClientSecret(config, clientId, apiSecret) {
|
|
238
|
+
const secret = config.rownd.oidcClientSecrets?.[clientId] ?? apiSecret;
|
|
239
|
+
if (!secret) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Missing plaintext secret for Rownd OIDC client ${clientId}. Add rownd.oidcClientSecrets.${clientId} to the config.`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (secret.startsWith("$argon2")) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Rownd returned a hashed secret for OIDC client ${clientId}. Add the plaintext secret under rownd.oidcClientSecrets.${clientId}.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return secret;
|
|
250
|
+
}
|
|
251
|
+
async function createSuperTokensOAuthClient(input) {
|
|
252
|
+
if (input.provisionedClientIds.has(input.clientId)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
input.provisionedClientIds.add(input.clientId);
|
|
256
|
+
console.log(`Provisioning SuperTokens OAuth2 Client: ${input.clientId}`);
|
|
257
|
+
const grantTypes = [
|
|
258
|
+
"authorization_code",
|
|
259
|
+
"refresh_token",
|
|
260
|
+
"client_credentials",
|
|
261
|
+
"implicit"
|
|
262
|
+
];
|
|
263
|
+
if (input.oidcClient.config.device_flow_enabled) {
|
|
264
|
+
grantTypes.push("urn:ietf:params:oauth:grant-type:device_code");
|
|
265
|
+
}
|
|
266
|
+
const oauthRes = await fetch(
|
|
267
|
+
new URL(
|
|
268
|
+
"/recipe/oauth/clients",
|
|
269
|
+
input.config.supertokens.connectionURI
|
|
270
|
+
).toString(),
|
|
271
|
+
{
|
|
272
|
+
method: "POST",
|
|
273
|
+
headers: input.headers,
|
|
274
|
+
body: JSON.stringify({
|
|
275
|
+
clientName: input.oidcClient.name || input.clientId || "Rownd Client",
|
|
276
|
+
clientId: input.clientId,
|
|
277
|
+
clientSecret: input.clientSecret,
|
|
278
|
+
redirectUris: input.oidcClient.config.redirect_uris || [],
|
|
279
|
+
postLogoutRedirectUris: input.oidcClient.config.post_logout_uris || [],
|
|
280
|
+
scope: input.oidcClient.config.allowed_scopes?.join(" "),
|
|
281
|
+
responseTypes: ["code", "token", "id_token"],
|
|
282
|
+
grantTypes,
|
|
283
|
+
tokenEndpointAuthMethod: input.oidcClient.config.token_endpoint_auth_method ?? "client_secret_basic",
|
|
284
|
+
audience: [`app:${input.config.rownd.appId}`],
|
|
285
|
+
metadata: {
|
|
286
|
+
rowndOidcClientId: input.oidcClient.id,
|
|
287
|
+
rowndAppVariantId: input.appVariantId || void 0,
|
|
288
|
+
rowndAllowedScopes: input.oidcClient.config.allowed_scopes || [],
|
|
289
|
+
rowndApplicationType: input.oidcClient.config.application_type,
|
|
290
|
+
rowndIsPkceRequired: input.oidcClient.config.is_pkce_required,
|
|
291
|
+
rowndIsPkceSupported: input.oidcClient.config.is_pkce_supported,
|
|
292
|
+
rowndDeviceFlowEnabled: input.oidcClient.config.device_flow_enabled,
|
|
293
|
+
rowndIsOidcClientIdAlias: input.isOidcClientIdAlias || void 0
|
|
237
294
|
}
|
|
238
|
-
}
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
if (!oauthRes.ok) {
|
|
299
|
+
const errorText = await oauthRes.text();
|
|
300
|
+
if (!errorText.includes("already exists") && !errorText.includes("Duplicate")) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Failed to create OAuth2 Client ${input.clientId}: ${oauthRes.status} ${errorText}`
|
|
303
|
+
);
|
|
239
304
|
}
|
|
240
305
|
}
|
|
241
306
|
}
|