@supertokens-plugins/rownd-nodejs 0.3.0-beta.1 → 0.3.0-beta.3
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 +7 -4
- package/dist/bulkMigrate.js +44 -584
- package/dist/index.d.mts +12 -20
- package/dist/index.d.ts +12 -20
- package/dist/index.js +1444 -1455
- package/dist/index.mjs +1441 -1453
- package/package.json +1 -3
package/dist/index.js
CHANGED
|
@@ -271,16 +271,17 @@ __export(index_exports, {
|
|
|
271
271
|
DEFAULT_ROWND_SCHEMA: () => DEFAULT_ROWND_SCHEMA,
|
|
272
272
|
GUEST_AUTH_METHOD_ID: () => GUEST_AUTH_METHOD_ID,
|
|
273
273
|
HANDLE_BASE_PATH: () => HANDLE_BASE_PATH,
|
|
274
|
+
HUB_LOGIN_PAGE_PATH: () => HUB_LOGIN_PAGE_PATH,
|
|
275
|
+
HUB_VERIFY_EMAIL_PAGE_PATH: () => HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
274
276
|
PLUGIN_ID: () => PLUGIN_ID,
|
|
275
277
|
PLUGIN_SDK_VERSION: () => PLUGIN_SDK_VERSION,
|
|
278
|
+
PLUGIN_VERSION: () => PLUGIN_VERSION,
|
|
276
279
|
PUBLIC_TENANT_ID: () => PUBLIC_TENANT_ID,
|
|
277
280
|
ROWND_JWT_CLAIMS: () => ROWND_JWT_CLAIMS,
|
|
278
281
|
ROWND_PLUGIN_ERROR_MESSAGES: () => ROWND_PLUGIN_ERROR_MESSAGES,
|
|
279
282
|
RowndPluginError: () => RowndPluginError,
|
|
280
283
|
default: () => index_default,
|
|
281
284
|
getRowndClient: () => getRowndClient,
|
|
282
|
-
getRowndEmailVerificationContent: () => getRowndEmailVerificationContent,
|
|
283
|
-
getRowndPasswordlessEmailContent: () => getRowndPasswordlessEmailContent,
|
|
284
285
|
init: () => init,
|
|
285
286
|
setRowndClient: () => setRowndClient
|
|
286
287
|
});
|
|
@@ -680,10 +681,11 @@ var buildLogger = (pluginId, version, { fileLocation = true } = {}) => {
|
|
|
680
681
|
|
|
681
682
|
// src/plugin.ts
|
|
682
683
|
var import_node = require("@rownd/node");
|
|
683
|
-
var
|
|
684
|
+
var import_supertokens_node4 = __toESM(require("supertokens-node"));
|
|
684
685
|
|
|
685
686
|
// src/constants.ts
|
|
686
687
|
var PLUGIN_ID = "supertokens-plugin-rownd";
|
|
688
|
+
var PLUGIN_VERSION = "0.3.0";
|
|
687
689
|
var PLUGIN_SDK_VERSION = ["23.0.0", "23.0.1", ">=23.0.1"];
|
|
688
690
|
var HANDLE_BASE_PATH = "/plugin/rownd";
|
|
689
691
|
var PUBLIC_TENANT_ID = "public";
|
|
@@ -720,11 +722,13 @@ var DEFAULT_ROWND_SCHEMA = {
|
|
|
720
722
|
user_visible: true
|
|
721
723
|
}
|
|
722
724
|
};
|
|
725
|
+
var HUB_LOGIN_PAGE_PATH = "/account/login";
|
|
726
|
+
var HUB_VERIFY_EMAIL_PAGE_PATH = "/account/verify-email";
|
|
723
727
|
|
|
724
728
|
// src/logger.ts
|
|
725
729
|
var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
726
730
|
PLUGIN_ID,
|
|
727
|
-
|
|
731
|
+
PLUGIN_VERSION
|
|
728
732
|
);
|
|
729
733
|
|
|
730
734
|
// src/telemetry/axiomTelemetryClient.ts
|
|
@@ -841,17 +845,6 @@ function safeRecordEvent(client, event, outcome) {
|
|
|
841
845
|
}
|
|
842
846
|
}
|
|
843
847
|
|
|
844
|
-
// src/pluginImplementation.ts
|
|
845
|
-
var import_crypto = require("crypto");
|
|
846
|
-
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
847
|
-
var import_accountlinking = __toESM(require("supertokens-node/recipe/accountlinking"));
|
|
848
|
-
var import_emailverification = __toESM(require("supertokens-node/recipe/emailverification"));
|
|
849
|
-
var import_passwordless = __toESM(require("supertokens-node/recipe/passwordless"));
|
|
850
|
-
var import_session = __toESM(require("supertokens-node/recipe/session"));
|
|
851
|
-
var import_claims = require("supertokens-node/recipe/session/claims");
|
|
852
|
-
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
853
|
-
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
854
|
-
|
|
855
848
|
// src/errors.ts
|
|
856
849
|
var ROWND_PLUGIN_ERROR_MESSAGES = {
|
|
857
850
|
MISSING_AUTHORIZATION_HEADER: "Missing authorization header",
|
|
@@ -864,7 +857,7 @@ var RowndPluginError = class extends Error {
|
|
|
864
857
|
}
|
|
865
858
|
};
|
|
866
859
|
|
|
867
|
-
// src/
|
|
860
|
+
// src/utils.ts
|
|
868
861
|
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
869
862
|
try {
|
|
870
863
|
const url = new URL(inputUrl);
|
|
@@ -883,7 +876,7 @@ function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
|
883
876
|
return queryString ? `${path}?${queryString}` : path;
|
|
884
877
|
}
|
|
885
878
|
}
|
|
886
|
-
function
|
|
879
|
+
function rewriteLinkToBaseUrl(inputUrl, targetPath, baseUrl, searchParams) {
|
|
887
880
|
const normalizedBaseUrl = baseUrl.endsWith("://") ? baseUrl : baseUrl.replace(/\/+$/, "") + "/";
|
|
888
881
|
const targetUrl = new URL(
|
|
889
882
|
`${normalizedBaseUrl}${targetPath.replace(/^\//, "")}`
|
|
@@ -903,23 +896,6 @@ function rewriteLinkToMobileDeepLink(inputUrl, targetPath, baseUrl, searchParams
|
|
|
903
896
|
}
|
|
904
897
|
return targetUrl.toString();
|
|
905
898
|
}
|
|
906
|
-
var rowndClient;
|
|
907
|
-
var pluginConfig;
|
|
908
|
-
function setRowndClient(client) {
|
|
909
|
-
rowndClient = client;
|
|
910
|
-
}
|
|
911
|
-
function getRowndClient() {
|
|
912
|
-
if (!rowndClient) {
|
|
913
|
-
throw new Error("Rownd client not initialized");
|
|
914
|
-
}
|
|
915
|
-
return rowndClient;
|
|
916
|
-
}
|
|
917
|
-
function setPluginConfig(config2) {
|
|
918
|
-
pluginConfig = config2;
|
|
919
|
-
}
|
|
920
|
-
function getPluginConfig() {
|
|
921
|
-
return pluginConfig;
|
|
922
|
-
}
|
|
923
899
|
async function parseRequest(req) {
|
|
924
900
|
const authHeader = req.getHeaderValue("authorization");
|
|
925
901
|
if (!authHeader) {
|
|
@@ -933,386 +909,37 @@ async function parseRequest(req) {
|
|
|
933
909
|
token
|
|
934
910
|
};
|
|
935
911
|
}
|
|
936
|
-
function
|
|
937
|
-
return
|
|
938
|
-
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
939
|
-
const appConfig = buildAppConfig(
|
|
940
|
-
deps.pluginConfig,
|
|
941
|
-
deps.stConfig,
|
|
942
|
-
appVariantId
|
|
943
|
-
);
|
|
944
|
-
if (!appConfig) {
|
|
945
|
-
return {
|
|
946
|
-
status: "ERROR",
|
|
947
|
-
message: `Unknown Rownd app variant: ${appVariantId}`
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
return {
|
|
951
|
-
status: "OK",
|
|
952
|
-
...appConfig
|
|
953
|
-
};
|
|
954
|
-
};
|
|
912
|
+
async function getJsonBody(req) {
|
|
913
|
+
return req.getJSONBody();
|
|
955
914
|
}
|
|
956
|
-
function
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
963
|
-
assertRowndAppVariantIsConfigured(appVariantId);
|
|
964
|
-
const thirdPartyId = body.authLevel === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
965
|
-
const thirdPartyUserId = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? `anon_${(0, import_crypto.randomUUID)()}` : guestId;
|
|
966
|
-
const response = await import_thirdparty.default.manuallyCreateOrUpdateUser(
|
|
967
|
-
PUBLIC_TENANT_ID,
|
|
968
|
-
thirdPartyId,
|
|
969
|
-
thirdPartyUserId,
|
|
970
|
-
`${thirdPartyUserId}@anonymous.local`,
|
|
971
|
-
false,
|
|
972
|
-
void 0,
|
|
973
|
-
userContext
|
|
974
|
-
);
|
|
975
|
-
if (response.status !== "OK") {
|
|
976
|
-
throw new Error(
|
|
977
|
-
`Guest user creation failed with status: ${response.status}`
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
await import_session.default.createNewSession(
|
|
981
|
-
req,
|
|
982
|
-
res,
|
|
983
|
-
PUBLIC_TENANT_ID,
|
|
984
|
-
response.recipeUserId,
|
|
985
|
-
{
|
|
986
|
-
...buildRowndAudience({}, appVariantId),
|
|
987
|
-
auth_level: GUEST_AUTH_METHOD_ID,
|
|
988
|
-
is_anonymous: true,
|
|
989
|
-
app_user_id: response.user.id
|
|
990
|
-
},
|
|
991
|
-
{},
|
|
992
|
-
userContext
|
|
993
|
-
);
|
|
994
|
-
logDebugMessage(`Guest session created for user: ${response.user.id}`);
|
|
995
|
-
deps.telemetryClient.recordSuccess({
|
|
996
|
-
outcome: "success",
|
|
997
|
-
durationMs: Date.now() - startedAt,
|
|
998
|
-
tenantId: PUBLIC_TENANT_ID,
|
|
999
|
-
superTokensUserId: response.user.id
|
|
1000
|
-
});
|
|
1001
|
-
return {
|
|
1002
|
-
status: "OK",
|
|
1003
|
-
createdNewRecipeUser: response.createdNewRecipeUser
|
|
1004
|
-
};
|
|
1005
|
-
} catch (error) {
|
|
1006
|
-
logDebugMessage(`Guest login failed. Error: ${getErrorMessage(error)}`);
|
|
1007
|
-
deps.telemetryClient.recordError({
|
|
1008
|
-
error,
|
|
1009
|
-
startedAt,
|
|
1010
|
-
tenantId: PUBLIC_TENANT_ID
|
|
1011
|
-
});
|
|
1012
|
-
return {
|
|
1013
|
-
status: "ERROR",
|
|
1014
|
-
message: "Guest login failed"
|
|
1015
|
-
};
|
|
1016
|
-
}
|
|
915
|
+
function parseGuestBody(value) {
|
|
916
|
+
if (!isJsonRecord(value)) {
|
|
917
|
+
return {};
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
authLevel: typeof value.auth_level === "string" ? value.auth_level : void 0
|
|
1017
921
|
};
|
|
1018
922
|
}
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
let recipeUserId;
|
|
1027
|
-
try {
|
|
1028
|
-
if (!deps.stConfig.supertokens) {
|
|
1029
|
-
throw new Error("Supertokens config not found");
|
|
1030
|
-
}
|
|
1031
|
-
const parsed = await parseRequest(req);
|
|
1032
|
-
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1033
|
-
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1034
|
-
rowndUserId = await validateRowndToken(parsed.token);
|
|
1035
|
-
user = await import_supertokens_node.default.getUser(rowndUserId, userContext);
|
|
1036
|
-
if (!user) {
|
|
1037
|
-
const rowndUser = await fetchRowndUserInfo(rowndUserId);
|
|
1038
|
-
const stUserImport = mapRowndUserToSuperTokens(rowndUser);
|
|
1039
|
-
try {
|
|
1040
|
-
const importedUser = await importUser(
|
|
1041
|
-
stUserImport,
|
|
1042
|
-
deps.stConfig.supertokens
|
|
1043
|
-
);
|
|
1044
|
-
superTokensUserId = importedUser.id;
|
|
1045
|
-
if (importedUser.loginMethods[0]?.recipeUserId) {
|
|
1046
|
-
recipeUserId = import_supertokens_node.default.convertToRecipeUserId(
|
|
1047
|
-
importedUser.loginMethods[0].recipeUserId
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
} catch (err) {
|
|
1051
|
-
user = await import_supertokens_node.default.getUser(rowndUserId, userContext);
|
|
1052
|
-
if (!user) {
|
|
1053
|
-
throw err;
|
|
1054
|
-
}
|
|
1055
|
-
superTokensUserId = user.id;
|
|
1056
|
-
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1057
|
-
logDebugMessage(
|
|
1058
|
-
`User already migrated (race condition). tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1059
|
-
);
|
|
1060
|
-
}
|
|
1061
|
-
logDebugMessage(
|
|
1062
|
-
`User migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1063
|
-
);
|
|
1064
|
-
} else {
|
|
1065
|
-
superTokensUserId = user.id;
|
|
1066
|
-
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1067
|
-
logDebugMessage(
|
|
1068
|
-
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1069
|
-
);
|
|
1070
|
-
}
|
|
1071
|
-
if (superTokensUserId) {
|
|
1072
|
-
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
1073
|
-
}
|
|
1074
|
-
if (!recipeUserId) {
|
|
1075
|
-
throw new Error("User not found or has no login methods");
|
|
1076
|
-
}
|
|
1077
|
-
await import_session.default.createNewSession(
|
|
1078
|
-
req,
|
|
1079
|
-
res,
|
|
1080
|
-
PUBLIC_TENANT_ID,
|
|
1081
|
-
recipeUserId,
|
|
1082
|
-
{
|
|
1083
|
-
...buildRowndAudience({}, appVariantId)
|
|
1084
|
-
},
|
|
1085
|
-
{},
|
|
1086
|
-
userContext
|
|
1087
|
-
);
|
|
1088
|
-
logDebugMessage(
|
|
1089
|
-
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
1090
|
-
);
|
|
1091
|
-
deps.telemetryClient.recordSuccess({
|
|
1092
|
-
outcome: "success",
|
|
1093
|
-
durationMs: Date.now() - startedAt,
|
|
1094
|
-
tenantId,
|
|
1095
|
-
rowndUserId,
|
|
1096
|
-
superTokensUserId
|
|
1097
|
-
});
|
|
1098
|
-
return { status: "OK" };
|
|
1099
|
-
} catch (error) {
|
|
1100
|
-
logDebugMessage(`Migration failed. Error: ${getErrorMessage(error)}`);
|
|
1101
|
-
deps.telemetryClient.recordError({
|
|
1102
|
-
error,
|
|
1103
|
-
startedAt,
|
|
1104
|
-
tenantId,
|
|
1105
|
-
rowndUserId,
|
|
1106
|
-
superTokensUserId
|
|
1107
|
-
});
|
|
1108
|
-
return {
|
|
1109
|
-
status: "ERROR",
|
|
1110
|
-
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
923
|
+
function parseUpdateUserBody(value) {
|
|
924
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.data)) {
|
|
925
|
+
return {};
|
|
926
|
+
}
|
|
927
|
+
return {
|
|
928
|
+
data: value.data,
|
|
929
|
+
...isJsonRecord(value.context) ? { context: value.context } : {}
|
|
1113
930
|
};
|
|
1114
931
|
}
|
|
1115
|
-
function
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
...await getUserById(session.getUserId())
|
|
1121
|
-
};
|
|
1122
|
-
};
|
|
932
|
+
function parseUpdateMetaBody(value) {
|
|
933
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.meta)) {
|
|
934
|
+
return {};
|
|
935
|
+
}
|
|
936
|
+
return { meta: value.meta };
|
|
1123
937
|
}
|
|
1124
|
-
function
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
const payload = parseUpdateUserBody(await getJsonBody(req));
|
|
1130
|
-
const inputData = payload.data ?? {};
|
|
1131
|
-
const requestUserContext = {
|
|
1132
|
-
...userContext,
|
|
1133
|
-
...payload.context
|
|
1134
|
-
};
|
|
1135
|
-
const { email, ...dataWithoutEmail } = inputData;
|
|
1136
|
-
const hasEmailUpdate = hasOwn(inputData, "email") && typeof email === "string";
|
|
1137
|
-
const permissionError = validateWritableFields(
|
|
1138
|
-
Object.keys(dataWithoutEmail)
|
|
1139
|
-
);
|
|
1140
|
-
if (permissionError) {
|
|
1141
|
-
return permissionError;
|
|
1142
|
-
}
|
|
1143
|
-
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
1144
|
-
await updateUserData(session.getUserId(), dataWithoutEmail);
|
|
1145
|
-
}
|
|
1146
|
-
if (hasEmailUpdate) {
|
|
1147
|
-
return {
|
|
1148
|
-
status: "OK",
|
|
1149
|
-
...await startPendingEmailVerification({
|
|
1150
|
-
userId: session.getUserId(),
|
|
1151
|
-
recipeUserId: session.getRecipeUserId(),
|
|
1152
|
-
tenantId: session.getTenantId(),
|
|
1153
|
-
email,
|
|
1154
|
-
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
1155
|
-
userContext: appVariantId ? { ...requestUserContext, rowndAppVariantId: appVariantId } : requestUserContext
|
|
1156
|
-
})
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
return {
|
|
1160
|
-
status: "OK",
|
|
1161
|
-
...await getUserById(session.getUserId())
|
|
1162
|
-
};
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
function handleDeleteUser() {
|
|
1166
|
-
return async (_req, _res, maybeSession) => {
|
|
1167
|
-
const session = requireSession(maybeSession);
|
|
1168
|
-
await import_supertokens_node.default.deleteUser(session.getUserId(), true);
|
|
1169
|
-
return { status: "OK" };
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
function handleSignOut() {
|
|
1173
|
-
return async (_req, _res, maybeSession, userContext) => {
|
|
1174
|
-
const session = requireSession(maybeSession);
|
|
1175
|
-
await import_session.default.revokeAllSessionsForUser(
|
|
1176
|
-
session.getUserId(),
|
|
1177
|
-
true,
|
|
1178
|
-
session.getTenantId(),
|
|
1179
|
-
userContext
|
|
1180
|
-
);
|
|
1181
|
-
return { status: "OK" };
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
function handleGetUserMeta() {
|
|
1185
|
-
return async (_req, _res, maybeSession) => {
|
|
1186
|
-
const session = requireSession(maybeSession);
|
|
1187
|
-
const metadata = await getUserMetadata(session.getUserId());
|
|
1188
|
-
return {
|
|
1189
|
-
status: "OK",
|
|
1190
|
-
id: session.getUserId(),
|
|
1191
|
-
meta: Object.fromEntries(
|
|
1192
|
-
Object.entries(metadata).filter(
|
|
1193
|
-
([key]) => !isInternalMetadataField(key)
|
|
1194
|
-
)
|
|
1195
|
-
)
|
|
1196
|
-
};
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
function handleUpdateUserMeta() {
|
|
1200
|
-
return async (req, _res, maybeSession) => {
|
|
1201
|
-
const session = requireSession(maybeSession);
|
|
1202
|
-
const payload = parseUpdateMetaBody(await getJsonBody(req));
|
|
1203
|
-
const internalField = Object.keys(payload.meta ?? {}).find(
|
|
1204
|
-
isInternalMetadataField
|
|
1205
|
-
);
|
|
1206
|
-
if (internalField) {
|
|
1207
|
-
return {
|
|
1208
|
-
status: "ERROR",
|
|
1209
|
-
code: 403,
|
|
1210
|
-
message: `field is not writable: ${internalField}`
|
|
1211
|
-
};
|
|
1212
|
-
}
|
|
1213
|
-
return {
|
|
1214
|
-
status: "OK",
|
|
1215
|
-
...await updateUserMetadata(session.getUserId(), payload.meta ?? {})
|
|
1216
|
-
};
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
function handleGetUserField() {
|
|
1220
|
-
return async (req, _res, maybeSession) => {
|
|
1221
|
-
const session = requireSession(maybeSession);
|
|
1222
|
-
const field = req.getKeyValueFromQuery("field");
|
|
1223
|
-
if (!field) {
|
|
1224
|
-
return missingFieldResponse();
|
|
1225
|
-
}
|
|
1226
|
-
const user = await getUserById(session.getUserId());
|
|
1227
|
-
return {
|
|
1228
|
-
status: "OK",
|
|
1229
|
-
value: user.data[field]
|
|
1230
|
-
};
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
function handleUpdateUserField() {
|
|
1234
|
-
return async (req, _res, maybeSession, userContext) => {
|
|
1235
|
-
const session = requireSession(maybeSession);
|
|
1236
|
-
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1237
|
-
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1238
|
-
const field = req.getKeyValueFromQuery("field");
|
|
1239
|
-
if (!field) {
|
|
1240
|
-
return missingFieldResponse();
|
|
1241
|
-
}
|
|
1242
|
-
const payload = parseUpdateFieldBody(await getJsonBody(req));
|
|
1243
|
-
if (field === "email" && typeof payload.value === "string") {
|
|
1244
|
-
return {
|
|
1245
|
-
status: "OK",
|
|
1246
|
-
...await startPendingEmailVerification({
|
|
1247
|
-
userId: session.getUserId(),
|
|
1248
|
-
recipeUserId: session.getRecipeUserId(),
|
|
1249
|
-
tenantId: session.getTenantId(),
|
|
1250
|
-
email: payload.value,
|
|
1251
|
-
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
1252
|
-
userContext: appVariantId ? { ...userContext, rowndAppVariantId: appVariantId } : userContext
|
|
1253
|
-
})
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
const permissionError = validateWritableFields([field]);
|
|
1257
|
-
if (permissionError) {
|
|
1258
|
-
return permissionError;
|
|
1259
|
-
}
|
|
1260
|
-
return {
|
|
1261
|
-
status: "OK",
|
|
1262
|
-
...await updateUserData(session.getUserId(), {
|
|
1263
|
-
[field]: payload.value
|
|
1264
|
-
})
|
|
1265
|
-
};
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
function requireSession(session) {
|
|
1269
|
-
if (!session) {
|
|
1270
|
-
throw new Error("Session not found");
|
|
1271
|
-
}
|
|
1272
|
-
return session;
|
|
1273
|
-
}
|
|
1274
|
-
async function getJsonBody(req) {
|
|
1275
|
-
return req.getJSONBody();
|
|
1276
|
-
}
|
|
1277
|
-
function parseGuestBody(value) {
|
|
1278
|
-
if (!isJsonRecord(value)) {
|
|
1279
|
-
return {};
|
|
1280
|
-
}
|
|
1281
|
-
return {
|
|
1282
|
-
authLevel: typeof value.auth_level === "string" ? value.auth_level : void 0
|
|
1283
|
-
};
|
|
1284
|
-
}
|
|
1285
|
-
function parseUpdateUserBody(value) {
|
|
1286
|
-
if (!isJsonRecord(value) || !isJsonRecord(value.data)) {
|
|
1287
|
-
return {};
|
|
1288
|
-
}
|
|
1289
|
-
return {
|
|
1290
|
-
data: value.data,
|
|
1291
|
-
...isJsonRecord(value.context) ? { context: value.context } : {}
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
function parseUpdateMetaBody(value) {
|
|
1295
|
-
if (!isJsonRecord(value) || !isJsonRecord(value.meta)) {
|
|
1296
|
-
return {};
|
|
1297
|
-
}
|
|
1298
|
-
return { meta: value.meta };
|
|
1299
|
-
}
|
|
1300
|
-
function parseUpdateFieldBody(value) {
|
|
1301
|
-
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
1302
|
-
return {};
|
|
1303
|
-
}
|
|
1304
|
-
return { value: value.value };
|
|
1305
|
-
}
|
|
1306
|
-
function validateWritableFields(fields) {
|
|
1307
|
-
const readOnlyField = fields.find((field) => !canUpdateUserDataField(field));
|
|
1308
|
-
if (!readOnlyField) {
|
|
1309
|
-
return void 0;
|
|
1310
|
-
}
|
|
1311
|
-
return {
|
|
1312
|
-
status: "ERROR",
|
|
1313
|
-
code: 403,
|
|
1314
|
-
message: `field is not writable: ${readOnlyField}`
|
|
1315
|
-
};
|
|
938
|
+
function parseUpdateFieldBody(value) {
|
|
939
|
+
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
940
|
+
return {};
|
|
941
|
+
}
|
|
942
|
+
return { value: value.value };
|
|
1316
943
|
}
|
|
1317
944
|
function missingFieldResponse() {
|
|
1318
945
|
return {
|
|
@@ -1324,138 +951,21 @@ function missingFieldResponse() {
|
|
|
1324
951
|
function isJsonRecord(value) {
|
|
1325
952
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1326
953
|
}
|
|
954
|
+
function isRecord(value) {
|
|
955
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
956
|
+
}
|
|
1327
957
|
function hasOwn(value, key) {
|
|
1328
958
|
return Object.prototype.hasOwnProperty.call(value, key);
|
|
1329
959
|
}
|
|
1330
|
-
function getErrorMessage(error) {
|
|
1331
|
-
return error instanceof Error ? error.message : "Unknown error";
|
|
1332
|
-
}
|
|
1333
|
-
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1334
|
-
const loginMethods = [];
|
|
1335
|
-
const rowndUserData = rowndUser.data || {};
|
|
1336
|
-
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
1337
|
-
if (!rowndUserData.user_id) {
|
|
1338
|
-
throw new Error("Rownd user has no user_id");
|
|
1339
|
-
}
|
|
1340
|
-
if (rowndUserData.google_id) {
|
|
1341
|
-
if (!rowndUserData.email) {
|
|
1342
|
-
throw new Error("Rownd Google user is missing email");
|
|
1343
|
-
}
|
|
1344
|
-
loginMethods.push({
|
|
1345
|
-
recipeId: "thirdparty",
|
|
1346
|
-
thirdPartyId: "google",
|
|
1347
|
-
thirdPartyUserId: rowndUserData.google_id,
|
|
1348
|
-
email: rowndUserData.email,
|
|
1349
|
-
isVerified: !!rowndUserVerifiedData.google_id
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
if (rowndUserData.apple_id) {
|
|
1353
|
-
if (!rowndUserData.email) {
|
|
1354
|
-
throw new Error("Rownd Apple user is missing email");
|
|
1355
|
-
}
|
|
1356
|
-
loginMethods.push({
|
|
1357
|
-
recipeId: "thirdparty",
|
|
1358
|
-
thirdPartyId: "apple",
|
|
1359
|
-
thirdPartyUserId: rowndUserData.apple_id,
|
|
1360
|
-
email: rowndUserData.email,
|
|
1361
|
-
isVerified: !!rowndUserVerifiedData.apple_id
|
|
1362
|
-
});
|
|
1363
|
-
}
|
|
1364
|
-
if (rowndUserData.phone_number) {
|
|
1365
|
-
loginMethods.push({
|
|
1366
|
-
recipeId: "passwordless",
|
|
1367
|
-
phoneNumber: rowndUserData.phone_number,
|
|
1368
|
-
isVerified: !!rowndUserVerifiedData.phone_number
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
1372
|
-
loginMethods.push({
|
|
1373
|
-
recipeId: "passwordless",
|
|
1374
|
-
email: rowndUserData.email,
|
|
1375
|
-
isVerified: !!rowndUserVerifiedData.email
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
let authLevel = rowndUser.auth_level;
|
|
1379
|
-
if (loginMethods.length === 0) {
|
|
1380
|
-
const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : ANONYMOUS_AUTH_METHOD_ID;
|
|
1381
|
-
if (!authLevel) authLevel = thirdPartyId;
|
|
1382
|
-
loginMethods.push({
|
|
1383
|
-
recipeId: "thirdparty",
|
|
1384
|
-
thirdPartyId,
|
|
1385
|
-
thirdPartyUserId: rowndUserData.user_id,
|
|
1386
|
-
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1387
|
-
isVerified: false
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
|
-
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
1391
|
-
return {
|
|
1392
|
-
externalUserId: rowndUserData.user_id,
|
|
1393
|
-
loginMethods,
|
|
1394
|
-
userMetadata
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
async function importUser(stUser, config2) {
|
|
1398
|
-
const headers = {
|
|
1399
|
-
"Content-Type": "application/json"
|
|
1400
|
-
};
|
|
1401
|
-
if (config2.apiKey) {
|
|
1402
|
-
headers["api-key"] = config2.apiKey;
|
|
1403
|
-
}
|
|
1404
|
-
const response = await fetch(`${config2.connectionURI}/bulk-import/import`, {
|
|
1405
|
-
method: "POST",
|
|
1406
|
-
headers,
|
|
1407
|
-
body: JSON.stringify(stUser)
|
|
1408
|
-
});
|
|
1409
|
-
if (!response.ok) {
|
|
1410
|
-
const errorText = await response.text();
|
|
1411
|
-
throw new Error(
|
|
1412
|
-
`Bulk import failed with status ${response.status}: ${errorText}`
|
|
1413
|
-
);
|
|
1414
|
-
}
|
|
1415
|
-
const importResponse = await response.json();
|
|
1416
|
-
if (importResponse.status !== "OK" || !importResponse.user) {
|
|
1417
|
-
throw new Error(
|
|
1418
|
-
`Bulk import failed: ${importResponse.message || "Missing user in response"}`
|
|
1419
|
-
);
|
|
1420
|
-
}
|
|
1421
|
-
return importResponse.user;
|
|
1422
|
-
}
|
|
1423
|
-
async function validateRowndToken(token) {
|
|
1424
|
-
const client = getRowndClient();
|
|
1425
|
-
const tokenInfo = await client.validateToken(token);
|
|
1426
|
-
return tokenInfo.user_id;
|
|
1427
|
-
}
|
|
1428
|
-
async function fetchRowndUserInfo(userId) {
|
|
1429
|
-
const client = getRowndClient();
|
|
1430
|
-
const rowndUser = await client.fetchUserInfo({ user_id: userId });
|
|
1431
|
-
if (!rowndUser) {
|
|
1432
|
-
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
1433
|
-
}
|
|
1434
|
-
return rowndUser;
|
|
1435
|
-
}
|
|
1436
|
-
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1437
|
-
"user_id",
|
|
1438
|
-
"email",
|
|
1439
|
-
"phone_number",
|
|
1440
|
-
"google_id",
|
|
1441
|
-
"apple_id"
|
|
1442
|
-
]);
|
|
1443
|
-
var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1444
|
-
"original_rownd_user",
|
|
1445
|
-
"rownd_pending_verification"
|
|
1446
|
-
]);
|
|
1447
|
-
function isIdentityField(field) {
|
|
1448
|
-
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1449
|
-
}
|
|
1450
|
-
function isInternalMetadataField(field) {
|
|
1451
|
-
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1452
|
-
}
|
|
1453
960
|
function getStringList(value) {
|
|
1454
961
|
if (Array.isArray(value)) {
|
|
1455
962
|
return value.filter((entry) => typeof entry === "string");
|
|
1456
963
|
}
|
|
1457
964
|
return typeof value === "string" ? [value] : [];
|
|
1458
965
|
}
|
|
966
|
+
function getErrorMessage(error) {
|
|
967
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
968
|
+
}
|
|
1459
969
|
function getRequestedAppVariantIdFromRequest(req) {
|
|
1460
970
|
return req.getKeyValueFromQuery("app_variant_id");
|
|
1461
971
|
}
|
|
@@ -1463,520 +973,628 @@ function getRequestedDisplayContextFromRequest(req) {
|
|
|
1463
973
|
const displayContext = req.getKeyValueFromQuery("rownd_display_context");
|
|
1464
974
|
return displayContext === "browser" || displayContext === "mobile_app" || displayContext === "customer_web_view" ? displayContext : void 0;
|
|
1465
975
|
}
|
|
976
|
+
function getRequestedClientDomainFromRequest(req) {
|
|
977
|
+
const clientDomain = req.getKeyValueFromQuery("rownd_client_domain");
|
|
978
|
+
return typeof clientDomain === "string" && clientDomain.length > 0 ? clientDomain : void 0;
|
|
979
|
+
}
|
|
1466
980
|
function getRequestedRedirectToPathFromRequest(req) {
|
|
1467
981
|
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1468
982
|
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1469
983
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
throw new Error(`Unknown Rownd app variant: ${appVariantId}`);
|
|
1476
|
-
}
|
|
984
|
+
|
|
985
|
+
// src/config.ts
|
|
986
|
+
var pluginConfig;
|
|
987
|
+
function setPluginConfig(config2) {
|
|
988
|
+
pluginConfig = config2;
|
|
1477
989
|
}
|
|
1478
|
-
|
|
990
|
+
function getPluginConfig() {
|
|
991
|
+
return pluginConfig;
|
|
992
|
+
}
|
|
993
|
+
function assertRowndAppVariantIsConfigured(appVariantId) {
|
|
1479
994
|
if (!appVariantId) {
|
|
1480
995
|
return;
|
|
1481
996
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
const originalRowndUser = isJsonRecord(metadata.original_rownd_user) ? metadata.original_rownd_user : {};
|
|
1485
|
-
const attributes = isJsonRecord(originalRowndUser.attributes) ? originalRowndUser.attributes : {};
|
|
1486
|
-
const appVariants = getStringList(attributes["rownd:app_variants"]);
|
|
1487
|
-
if (appVariants.includes(appVariantId)) {
|
|
1488
|
-
return;
|
|
997
|
+
if (pluginConfig?.subBrands && !pluginConfig.subBrands[appVariantId]) {
|
|
998
|
+
throw new Error(`Unknown Rownd app variant: ${appVariantId}`);
|
|
1489
999
|
}
|
|
1490
|
-
await import_usermetadata2.default.updateUserMetadata(userId, {
|
|
1491
|
-
...metadata,
|
|
1492
|
-
original_rownd_user: {
|
|
1493
|
-
...originalRowndUser,
|
|
1494
|
-
data: isJsonRecord(originalRowndUser.data) ? originalRowndUser.data : { user_id: userId },
|
|
1495
|
-
verified_data: isJsonRecord(originalRowndUser.verified_data) ? originalRowndUser.verified_data : {},
|
|
1496
|
-
attributes: {
|
|
1497
|
-
...attributes,
|
|
1498
|
-
"rownd:app_variants": [...appVariants, appVariantId]
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
});
|
|
1502
1000
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1001
|
+
var BUILTIN_SIGN_IN_METHOD_KEYS = [
|
|
1002
|
+
"email",
|
|
1003
|
+
"phone",
|
|
1004
|
+
"google",
|
|
1005
|
+
"apple",
|
|
1006
|
+
"anonymous"
|
|
1007
|
+
];
|
|
1008
|
+
function normalizeSchemaField(key, field) {
|
|
1009
|
+
let ownedBy = field.owned_by;
|
|
1010
|
+
if (key === "google_id" || key === "apple_id") {
|
|
1011
|
+
ownedBy = "app";
|
|
1012
|
+
} else if (!ownedBy) {
|
|
1013
|
+
ownedBy = "user";
|
|
1512
1014
|
}
|
|
1513
|
-
return
|
|
1015
|
+
return {
|
|
1016
|
+
display_name: field.display_name,
|
|
1017
|
+
type: field.type,
|
|
1018
|
+
owned_by: ownedBy,
|
|
1019
|
+
user_visible: field.user_visible,
|
|
1020
|
+
read_only: field.read_only ?? ownedBy === "app",
|
|
1021
|
+
show_empty: field.show_empty ?? false
|
|
1022
|
+
};
|
|
1514
1023
|
}
|
|
1515
|
-
var
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
|
|
1524
|
-
const user = await import_supertokens_node.default.getUser(userId);
|
|
1525
|
-
const metadata = user ? await getUserMetadata(user.id) : void 0;
|
|
1526
|
-
const originalRowndUser = metadata?.original_rownd_user;
|
|
1527
|
-
const verifiedData = originalRowndUser?.verified_data;
|
|
1528
|
-
const authLevel = getEffectiveAuthLevel(
|
|
1529
|
-
user,
|
|
1530
|
-
originalRowndUser?.auth_level,
|
|
1531
|
-
verifiedData
|
|
1024
|
+
var DEFAULT_PRIMARY_COLOR = "#5b5bd6";
|
|
1025
|
+
function buildSignInMethodsConfig(methodsArray) {
|
|
1026
|
+
const methods = (methodsArray ?? []).reduce(
|
|
1027
|
+
(acc, curr) => {
|
|
1028
|
+
acc[curr.method] = curr;
|
|
1029
|
+
return acc;
|
|
1030
|
+
},
|
|
1031
|
+
{}
|
|
1532
1032
|
);
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1033
|
+
const customProviders = Object.fromEntries(
|
|
1034
|
+
Object.entries(methods).filter(([key]) => !BUILTIN_SIGN_IN_METHOD_KEYS.includes(key)).map(([key, val]) => {
|
|
1035
|
+
return val ? [
|
|
1036
|
+
key,
|
|
1037
|
+
{
|
|
1038
|
+
enabled: true,
|
|
1039
|
+
display_name: getStringMethodProperty(val, "displayName") ?? key,
|
|
1040
|
+
icon_light_url: getStringMethodProperty(val, "iconLightUrl"),
|
|
1041
|
+
icon_dark_url: getStringMethodProperty(val, "iconDarkUrl")
|
|
1042
|
+
}
|
|
1043
|
+
] : [key, void 0];
|
|
1044
|
+
}).filter(([, v]) => v !== void 0)
|
|
1045
|
+
);
|
|
1046
|
+
const googleMethod = methods.google;
|
|
1047
|
+
const appleMethod = methods.apple;
|
|
1048
|
+
const anonymousMethod = methods.anonymous;
|
|
1049
|
+
const anonymousType = getAnonymousType(anonymousMethod);
|
|
1050
|
+
const googleOneTap = getOneTapConfig(googleMethod);
|
|
1539
1051
|
return {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1052
|
+
email: { enabled: !!methods.email },
|
|
1053
|
+
phone: { enabled: !!methods.phone },
|
|
1054
|
+
google: {
|
|
1055
|
+
enabled: !!googleMethod,
|
|
1056
|
+
client_id: getStringMethodProperty(googleMethod, "clientId") ?? "",
|
|
1057
|
+
ios_client_id: getStringMethodProperty(googleMethod, "iosClientId") ?? "",
|
|
1058
|
+
scopes: getStringArrayMethodProperty(googleMethod, "scopes") ?? [],
|
|
1059
|
+
...getSignInFasterWithGoogle(googleMethod) ? { sign_in_faster_with_google: getSignInFasterWithGoogle(googleMethod) } : {},
|
|
1060
|
+
one_tap: {
|
|
1061
|
+
browser: {
|
|
1062
|
+
auto_prompt: googleOneTap?.browser?.autoPrompt ?? false,
|
|
1063
|
+
delay: googleOneTap?.browser?.delay ?? 7e3
|
|
1064
|
+
},
|
|
1065
|
+
mobile_app: {
|
|
1066
|
+
auto_prompt: googleOneTap?.mobileApp?.autoPrompt ?? false,
|
|
1067
|
+
delay: googleOneTap?.mobileApp?.delay ?? 7e3
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
apple: {
|
|
1072
|
+
enabled: !!appleMethod,
|
|
1073
|
+
client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
|
|
1074
|
+
},
|
|
1075
|
+
anonymous: {
|
|
1076
|
+
enabled: !!anonymousMethod && anonymousType !== "instant",
|
|
1077
|
+
...anonymousMethod && anonymousType !== "instant" ? { type: anonymousType } : {},
|
|
1078
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "displayName") !== void 0 ? {
|
|
1079
|
+
display_name: getStringMethodProperty(
|
|
1080
|
+
anonymousMethod,
|
|
1081
|
+
"displayName"
|
|
1082
|
+
)
|
|
1083
|
+
} : {},
|
|
1084
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconLightUrl") !== void 0 ? {
|
|
1085
|
+
icon_light_url: getStringMethodProperty(
|
|
1086
|
+
anonymousMethod,
|
|
1087
|
+
"iconLightUrl"
|
|
1088
|
+
)
|
|
1089
|
+
} : {},
|
|
1090
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconDarkUrl") !== void 0 ? {
|
|
1091
|
+
icon_dark_url: getStringMethodProperty(
|
|
1092
|
+
anonymousMethod,
|
|
1093
|
+
"iconDarkUrl"
|
|
1094
|
+
)
|
|
1095
|
+
} : {}
|
|
1096
|
+
},
|
|
1097
|
+
...customProviders
|
|
1550
1098
|
};
|
|
1551
1099
|
}
|
|
1552
|
-
function
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1557
|
-
const claims = {};
|
|
1558
|
-
for (const [key, field] of Object.entries(schema)) {
|
|
1559
|
-
if (field.include_in_session_claims !== true) {
|
|
1560
|
-
continue;
|
|
1561
|
-
}
|
|
1562
|
-
const value = metadata.original_rownd_user?.data?.[key] ?? metadata[key];
|
|
1563
|
-
if (value !== void 0) {
|
|
1564
|
-
claims[field.session_claim_name || key] = value;
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
return claims;
|
|
1100
|
+
function isInstantAnonymousMethod(methods) {
|
|
1101
|
+
return (methods ?? []).some(
|
|
1102
|
+
(method) => method.method === "anonymous" && getAnonymousType(method) === "instant"
|
|
1103
|
+
);
|
|
1568
1104
|
}
|
|
1569
|
-
function
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
return originalUserId;
|
|
1573
|
-
}
|
|
1574
|
-
if (typeof currentPayload.app_user_id === "string") {
|
|
1575
|
-
return currentPayload.app_user_id;
|
|
1576
|
-
}
|
|
1577
|
-
return user?.id || userId;
|
|
1105
|
+
function getAnonymousType(method) {
|
|
1106
|
+
const type = getStringMethodProperty(method, "type");
|
|
1107
|
+
return type === "instant" ? "instant" : "guest";
|
|
1578
1108
|
}
|
|
1579
|
-
function
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
if (appId) {
|
|
1583
|
-
audience.push(`app:${appId}`);
|
|
1584
|
-
}
|
|
1585
|
-
if (appVariantId) {
|
|
1586
|
-
audience.push(`app_variant:${appVariantId}`);
|
|
1587
|
-
}
|
|
1588
|
-
return audience.length > 0 ? { aud: [...new Set(audience)] } : {};
|
|
1109
|
+
function getSignInFasterWithGoogle(method) {
|
|
1110
|
+
const value = getStringMethodProperty(method, "signInFasterWithGoogle");
|
|
1111
|
+
return value === "enabled" || value === "disabled" ? value : void 0;
|
|
1589
1112
|
}
|
|
1590
|
-
function
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
return originalAnonymousId;
|
|
1113
|
+
function getMethodProperty(method, property) {
|
|
1114
|
+
if (!method) {
|
|
1115
|
+
return void 0;
|
|
1594
1116
|
}
|
|
1595
|
-
|
|
1596
|
-
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1597
|
-
});
|
|
1598
|
-
const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
|
|
1599
|
-
return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
|
|
1117
|
+
return method[property];
|
|
1600
1118
|
}
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1119
|
+
function getStringMethodProperty(method, property) {
|
|
1120
|
+
const value = getMethodProperty(method, property);
|
|
1121
|
+
return typeof value === "string" ? value : void 0;
|
|
1122
|
+
}
|
|
1123
|
+
function getStringArrayMethodProperty(method, property) {
|
|
1124
|
+
const value = getMethodProperty(method, property);
|
|
1125
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
1126
|
+
}
|
|
1127
|
+
function getOneTapConfig(method) {
|
|
1128
|
+
const oneTap = getMethodProperty(method, "oneTap");
|
|
1129
|
+
if (!isRecord(oneTap)) {
|
|
1604
1130
|
return void 0;
|
|
1605
1131
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
if (!currentUser || isGuestAccountInfo(newAccountInfo)) {
|
|
1132
|
+
return {
|
|
1133
|
+
browser: parseOneTapPlatform(oneTap.browser),
|
|
1134
|
+
mobileApp: parseOneTapPlatform(oneTap.mobileApp)
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
function parseOneTapPlatform(value) {
|
|
1138
|
+
if (!isRecord(value)) {
|
|
1614
1139
|
return void 0;
|
|
1615
1140
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
return void 0;
|
|
1141
|
+
return {
|
|
1142
|
+
autoPrompt: typeof value.autoPrompt === "boolean" ? value.autoPrompt : void 0,
|
|
1143
|
+
delay: typeof value.delay === "number" ? value.delay : void 0
|
|
1144
|
+
};
|
|
1623
1145
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
}
|
|
1628
|
-
function getPendingVerifications(metadata) {
|
|
1629
|
-
const pendingVerification = metadata.rownd_pending_verification;
|
|
1630
|
-
if (Array.isArray(pendingVerification)) {
|
|
1631
|
-
return pendingVerification.filter(isPendingVerification);
|
|
1146
|
+
function getSubBrandVariant(app) {
|
|
1147
|
+
if (isRecord(app) && isRecord(app.variant) && typeof app.variant.id === "string") {
|
|
1148
|
+
return app.variant;
|
|
1632
1149
|
}
|
|
1633
|
-
return
|
|
1634
|
-
}
|
|
1635
|
-
function isPendingVerification(value) {
|
|
1636
|
-
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
1150
|
+
return void 0;
|
|
1637
1151
|
}
|
|
1638
|
-
|
|
1639
|
-
const
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
const originalRowndUser = metadata.original_rownd_user;
|
|
1645
|
-
const rowndUser = originalRowndUser?.data?.user_id || userId;
|
|
1646
|
-
const state = originalRowndUser?.state || "enabled";
|
|
1647
|
-
const dataFieldKeys = /* @__PURE__ */ new Set();
|
|
1648
|
-
const data = {
|
|
1649
|
-
user_id: userId
|
|
1650
|
-
};
|
|
1651
|
-
for (const [key, value] of Object.entries(originalRowndUser?.data || {})) {
|
|
1652
|
-
if (!isIdentityField(key)) {
|
|
1653
|
-
data[key] = value;
|
|
1654
|
-
dataFieldKeys.add(key);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1658
|
-
for (const key of Object.keys(schema)) {
|
|
1659
|
-
dataFieldKeys.add(key);
|
|
1660
|
-
if (!isInternalMetadataField(key) && !isIdentityField(key) && metadata[key] !== void 0) {
|
|
1661
|
-
data[key] = metadata[key];
|
|
1152
|
+
function mergeConfigInput(base, override) {
|
|
1153
|
+
const result = { ...base };
|
|
1154
|
+
for (const [key, value] of Object.entries(override)) {
|
|
1155
|
+
if (value === void 0) {
|
|
1156
|
+
continue;
|
|
1662
1157
|
}
|
|
1158
|
+
const existing = result[key];
|
|
1159
|
+
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
1663
1160
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
if (method.email) {
|
|
1674
|
-
verifiedData.email = method.email;
|
|
1675
|
-
if (data.email === void 0) data.email = method.email;
|
|
1676
|
-
}
|
|
1677
|
-
if (method.phoneNumber) {
|
|
1678
|
-
verifiedData.phone_number = method.phoneNumber;
|
|
1679
|
-
if (data.phone_number === void 0)
|
|
1680
|
-
data.phone_number = method.phoneNumber;
|
|
1681
|
-
}
|
|
1682
|
-
} else if (method.recipeId === "thirdparty") {
|
|
1683
|
-
const thirdPartyId = getThirdPartyId(method);
|
|
1684
|
-
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
1685
|
-
if (method.verified && method.email) {
|
|
1686
|
-
verifiedData.email = method.email;
|
|
1687
|
-
}
|
|
1688
|
-
if (method.email && data.email === void 0) {
|
|
1689
|
-
data.email = method.email;
|
|
1690
|
-
}
|
|
1691
|
-
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
1692
|
-
data.google_id = thirdPartyUserId;
|
|
1693
|
-
verifiedData.google_id = thirdPartyUserId;
|
|
1694
|
-
}
|
|
1695
|
-
if (thirdPartyId === "apple" && thirdPartyUserId) {
|
|
1696
|
-
data.apple_id = thirdPartyUserId;
|
|
1697
|
-
verifiedData.apple_id = thirdPartyUserId;
|
|
1698
|
-
}
|
|
1699
|
-
} else if (method.recipeId === "emailpassword") {
|
|
1700
|
-
if (method.email && data.email === void 0) {
|
|
1701
|
-
data.email = method.email;
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1161
|
+
return result;
|
|
1162
|
+
}
|
|
1163
|
+
function buildRowndAppConfig(config2, stConfig, appVariantId) {
|
|
1164
|
+
const userSchema = config2.schema ?? DEFAULT_ROWND_SCHEMA;
|
|
1165
|
+
const baseApp = config2.appConfig ?? {};
|
|
1166
|
+
const subBrand = appVariantId ? config2.subBrands?.[appVariantId] : void 0;
|
|
1167
|
+
const app = appVariantId ? subBrand && mergeConfigInput(baseApp, subBrand) : baseApp;
|
|
1168
|
+
if (!app) {
|
|
1169
|
+
return void 0;
|
|
1704
1170
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1171
|
+
const branding = app.branding ?? {};
|
|
1172
|
+
const auth = app.auth ?? {};
|
|
1173
|
+
const signInMethods = buildSignInMethodsConfig(app.signInMethods);
|
|
1174
|
+
const variant = getSubBrandVariant(app);
|
|
1175
|
+
const finalSchema = { ...userSchema };
|
|
1176
|
+
if (signInMethods.email.enabled && !finalSchema.email) {
|
|
1177
|
+
finalSchema.email = {
|
|
1178
|
+
display_name: "Email",
|
|
1179
|
+
type: "string",
|
|
1180
|
+
user_visible: true
|
|
1181
|
+
};
|
|
1707
1182
|
}
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1183
|
+
if (signInMethods.phone.enabled && !finalSchema.phone_number) {
|
|
1184
|
+
finalSchema.phone_number = {
|
|
1185
|
+
display_name: "Phone number",
|
|
1186
|
+
type: "string",
|
|
1187
|
+
user_visible: true
|
|
1188
|
+
};
|
|
1710
1189
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1190
|
+
if (signInMethods.google.enabled && !finalSchema.google_id) {
|
|
1191
|
+
finalSchema.google_id = {
|
|
1192
|
+
display_name: "Google ID",
|
|
1193
|
+
type: "string",
|
|
1194
|
+
user_visible: false
|
|
1195
|
+
};
|
|
1714
1196
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
if (data[key] === void 0 && field.type === "string") {
|
|
1722
|
-
data[key] = "";
|
|
1723
|
-
}
|
|
1197
|
+
if (signInMethods.apple.enabled && !finalSchema.apple_id) {
|
|
1198
|
+
finalSchema.apple_id = {
|
|
1199
|
+
display_name: "Apple ID",
|
|
1200
|
+
type: "string",
|
|
1201
|
+
user_visible: false
|
|
1202
|
+
};
|
|
1724
1203
|
}
|
|
1725
|
-
const mapMethod = (method) => {
|
|
1726
|
-
if (method.recipeId === "thirdparty") {
|
|
1727
|
-
if (getThirdPartyId(method) === "google") return "google";
|
|
1728
|
-
if (getThirdPartyId(method) === "apple") return "apple";
|
|
1729
|
-
} else if (method.recipeId === "passwordless") {
|
|
1730
|
-
if (method.email) return "email";
|
|
1731
|
-
if (method.phoneNumber) return "phone";
|
|
1732
|
-
} else if (method.recipeId === "emailpassword") {
|
|
1733
|
-
return "email";
|
|
1734
|
-
}
|
|
1735
|
-
return "email";
|
|
1736
|
-
};
|
|
1737
|
-
const sortedByJoined = [...stUser.loginMethods].sort(
|
|
1738
|
-
(a, b) => a.timeJoined - b.timeJoined
|
|
1739
|
-
);
|
|
1740
|
-
const sortedByLastUsed = [
|
|
1741
|
-
...stUser.loginMethods
|
|
1742
|
-
].sort((a, b) => (b.lastUsed || b.timeJoined) - (a.lastUsed || a.timeJoined));
|
|
1743
|
-
const firstMethod = sortedByJoined[0];
|
|
1744
|
-
const lastMethod = sortedByLastUsed[0];
|
|
1745
|
-
const metadataMeta = Object.fromEntries(
|
|
1746
|
-
Object.entries(metadata).filter(
|
|
1747
|
-
([key]) => !isInternalMetadataField(key) && !dataFieldKeys.has(key)
|
|
1748
|
-
)
|
|
1749
|
-
);
|
|
1750
|
-
const meta = {
|
|
1751
|
-
...metadataMeta,
|
|
1752
|
-
created: new Date(stUser.timeJoined).toISOString(),
|
|
1753
|
-
first_sign_in: new Date(stUser.timeJoined).toISOString(),
|
|
1754
|
-
last_sign_in: new Date(lastUsedAt).toISOString(),
|
|
1755
|
-
last_active: new Date(lastUsedAt).toISOString(),
|
|
1756
|
-
first_sign_in_method: firstMethod ? mapMethod(firstMethod) : "email",
|
|
1757
|
-
last_sign_in_method: lastMethod ? mapMethod(lastMethod) : "email"
|
|
1758
|
-
};
|
|
1759
1204
|
return {
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
const metadata = await getUserMetadata(userId);
|
|
1773
|
-
const updatedMetadata = {
|
|
1774
|
-
...metadata,
|
|
1775
|
-
...inputData
|
|
1776
|
-
};
|
|
1777
|
-
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
1778
|
-
return getUserById(userId);
|
|
1779
|
-
}
|
|
1780
|
-
async function startPendingEmailVerification(input) {
|
|
1781
|
-
const metadata = await getUserMetadata(input.userId);
|
|
1782
|
-
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
1783
|
-
if (currentEmail === input.email) {
|
|
1784
|
-
return getUserById(input.userId);
|
|
1785
|
-
}
|
|
1786
|
-
const pendingVerifications = getPendingVerifications(metadata);
|
|
1787
|
-
const pendingEmailVerifications = pendingVerifications.filter(
|
|
1788
|
-
(pendingVerification2) => pendingVerification2.field === "email"
|
|
1789
|
-
);
|
|
1790
|
-
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1791
|
-
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1792
|
-
input.tenantId,
|
|
1793
|
-
input.recipeUserId,
|
|
1794
|
-
pendingVerification2.value,
|
|
1795
|
-
input.userContext
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
const pendingVerification = {
|
|
1799
|
-
id: input.pendingVerificationId,
|
|
1800
|
-
field: "email",
|
|
1801
|
-
value: input.email,
|
|
1802
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1803
|
-
};
|
|
1804
|
-
await import_usermetadata2.default.updateUserMetadata(input.userId, {
|
|
1805
|
-
...metadata,
|
|
1806
|
-
rownd_pending_verification: [
|
|
1807
|
-
...pendingVerifications.filter(
|
|
1808
|
-
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
1205
|
+
config_type: appVariantId ? "variant" : "app",
|
|
1206
|
+
...variant ? { variant } : {},
|
|
1207
|
+
app: {
|
|
1208
|
+
id: app.id ?? "",
|
|
1209
|
+
name: app.name ?? stConfig.appInfo.appName,
|
|
1210
|
+
icon: app.icon ?? "",
|
|
1211
|
+
...app.userVerificationFields ? { user_verification_fields: app.userVerificationFields } : {},
|
|
1212
|
+
schema: Object.fromEntries(
|
|
1213
|
+
Object.entries(finalSchema).map(([key, field]) => [
|
|
1214
|
+
key,
|
|
1215
|
+
normalizeSchemaField(key, field)
|
|
1216
|
+
])
|
|
1809
1217
|
),
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
rowndPendingVerificationId: pendingVerification.id
|
|
1821
|
-
}
|
|
1822
|
-
);
|
|
1823
|
-
if (response.status === "EMAIL_ALREADY_VERIFIED_ERROR") {
|
|
1824
|
-
await completePendingEmailVerification({
|
|
1825
|
-
recipeUserId: input.recipeUserId,
|
|
1826
|
-
email: input.email,
|
|
1827
|
-
userContext: input.userContext
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
return getUserById(input.userId);
|
|
1831
|
-
}
|
|
1832
|
-
async function completePendingEmailVerification(input) {
|
|
1833
|
-
const user = await import_supertokens_node.default.getUser(
|
|
1834
|
-
input.recipeUserId.getAsString(),
|
|
1835
|
-
input.userContext
|
|
1836
|
-
);
|
|
1837
|
-
const userId = user?.id ?? input.recipeUserId.getAsString();
|
|
1838
|
-
const metadata = await getUserMetadata(userId);
|
|
1839
|
-
const pendingVerifications = getPendingVerifications(metadata);
|
|
1840
|
-
const pendingVerification = pendingVerifications.find(
|
|
1841
|
-
(pendingVerification2) => pendingVerification2.field === "email" && pendingVerification2.value === input.email
|
|
1842
|
-
);
|
|
1843
|
-
if (!pendingVerification) {
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
let metadataUserId = userId;
|
|
1847
|
-
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
|
|
1848
|
-
if (passwordlessEmailMethod) {
|
|
1849
|
-
const updateResult = await import_passwordless.default.updateUser({
|
|
1850
|
-
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
1851
|
-
email: input.email,
|
|
1852
|
-
userContext: input.userContext
|
|
1853
|
-
});
|
|
1854
|
-
if (updateResult.status !== "OK") {
|
|
1855
|
-
throw new Error(
|
|
1856
|
-
`Failed to update verified email method: ${updateResult.status}`
|
|
1857
|
-
);
|
|
1858
|
-
}
|
|
1859
|
-
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
1860
|
-
const isPasswordlessSignUpAllowed = await import_accountlinking.default.isSignUpAllowed(
|
|
1861
|
-
PUBLIC_TENANT_ID,
|
|
1862
|
-
{
|
|
1863
|
-
recipeId: "passwordless",
|
|
1864
|
-
email: input.email
|
|
1865
|
-
},
|
|
1866
|
-
true,
|
|
1867
|
-
void 0,
|
|
1868
|
-
input.userContext
|
|
1869
|
-
);
|
|
1870
|
-
if (!isPasswordlessSignUpAllowed) {
|
|
1871
|
-
throw new Error("Passwordless sign up is not allowed for this email");
|
|
1872
|
-
}
|
|
1873
|
-
const passwordlessUser = await import_passwordless.default.signInUp({
|
|
1874
|
-
email: input.email,
|
|
1875
|
-
tenantId: PUBLIC_TENANT_ID,
|
|
1876
|
-
userContext: input.userContext
|
|
1877
|
-
});
|
|
1878
|
-
const primaryUserResult = await import_accountlinking.default.createPrimaryUser(
|
|
1879
|
-
passwordlessUser.recipeUserId,
|
|
1880
|
-
input.userContext
|
|
1881
|
-
);
|
|
1882
|
-
const primaryUserId = primaryUserResult.status === "OK" ? primaryUserResult.user.id : primaryUserResult.status === "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" ? primaryUserResult.primaryUserId : passwordlessUser.user.id;
|
|
1883
|
-
if (userId !== primaryUserId) {
|
|
1884
|
-
const linkResult = await import_accountlinking.default.linkAccounts(
|
|
1885
|
-
input.recipeUserId,
|
|
1886
|
-
primaryUserId,
|
|
1887
|
-
input.userContext
|
|
1888
|
-
);
|
|
1889
|
-
if (linkResult.status !== "OK") {
|
|
1890
|
-
throw new Error(
|
|
1891
|
-
`Failed to link verified email method: ${linkResult.status}`
|
|
1892
|
-
);
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
metadataUserId = primaryUserId;
|
|
1896
|
-
}
|
|
1897
|
-
const updatedMetadata = {
|
|
1898
|
-
...metadata,
|
|
1899
|
-
...metadata.original_rownd_user ? {
|
|
1900
|
-
original_rownd_user: {
|
|
1901
|
-
...metadata.original_rownd_user,
|
|
1902
|
-
data: {
|
|
1903
|
-
...metadata.original_rownd_user.data,
|
|
1904
|
-
email: input.email
|
|
1218
|
+
config: {
|
|
1219
|
+
...app.capabilities ? { capabilities: app.capabilities } : {},
|
|
1220
|
+
...app.web ? { web: app.web } : {},
|
|
1221
|
+
...app.bottomSheet ? { bottom_sheet: app.bottomSheet } : {},
|
|
1222
|
+
...app.profileStorageVersion ? { profile_storage_version: app.profileStorageVersion } : {},
|
|
1223
|
+
customizations: {
|
|
1224
|
+
primary_color: branding.primaryColor ?? DEFAULT_PRIMARY_COLOR,
|
|
1225
|
+
...branding.logo ? { logo: branding.logo } : {},
|
|
1226
|
+
...branding.logoDarkMode ? { logo_dark_mode: branding.logoDarkMode } : {},
|
|
1227
|
+
...branding.animations ? { animations: branding.animations } : {}
|
|
1905
1228
|
},
|
|
1906
|
-
|
|
1907
|
-
...
|
|
1908
|
-
|
|
1229
|
+
hub: {
|
|
1230
|
+
...app.allowedWebOrigins ? { allowed_web_origins: app.allowedWebOrigins } : {},
|
|
1231
|
+
customizations: {
|
|
1232
|
+
rounded_corners: branding.roundedCorners ?? true,
|
|
1233
|
+
...branding.containerBorderRadius !== void 0 ? { container_border_radius: branding.containerBorderRadius } : {},
|
|
1234
|
+
...branding.placement !== void 0 ? { placement: branding.placement } : {},
|
|
1235
|
+
...branding.hubPrimaryColor !== void 0 ? { primary_color: branding.hubPrimaryColor } : {},
|
|
1236
|
+
...branding.primaryColorDarkMode !== void 0 ? { primary_color_dark_mode: branding.primaryColorDarkMode } : {},
|
|
1237
|
+
...branding.backgroundColor !== void 0 ? { background_color: branding.backgroundColor } : {},
|
|
1238
|
+
...branding.fontFamily !== void 0 ? { font_family: branding.fontFamily } : {},
|
|
1239
|
+
...branding.hideVerificationIcons !== void 0 ? { hide_verification_icons: branding.hideVerificationIcons } : {},
|
|
1240
|
+
visual_swoops: branding.visualSwoops ?? true,
|
|
1241
|
+
blur_background: branding.blurBackground ?? true,
|
|
1242
|
+
...branding.blurBackgroundOpacity !== void 0 ? { blur_background_opacity: branding.blurBackgroundOpacity } : {},
|
|
1243
|
+
...branding.offsetX !== void 0 ? { offset_x: branding.offsetX } : {},
|
|
1244
|
+
...branding.offsetY !== void 0 ? { offset_y: branding.offsetY } : {},
|
|
1245
|
+
...branding.propertyOverrides ? { property_overrides: branding.propertyOverrides } : {},
|
|
1246
|
+
dark_mode: branding.darkMode ?? "auto"
|
|
1247
|
+
},
|
|
1248
|
+
...branding.customScripts ? { custom_scripts: branding.customScripts } : {},
|
|
1249
|
+
...branding.customStyles ? { custom_styles: branding.customStyles } : {},
|
|
1250
|
+
auth: {
|
|
1251
|
+
email: buildAuthEmailConfig(auth.email),
|
|
1252
|
+
...auth.mobile ? { mobile: buildAuthMobileConfig(auth.mobile) } : {},
|
|
1253
|
+
sign_in_methods: signInMethods,
|
|
1254
|
+
additional_fields: auth.additionalFields ?? [],
|
|
1255
|
+
...auth.rememberSignInMethod !== void 0 ? { remember_sign_in_method: auth.rememberSignInMethod } : {},
|
|
1256
|
+
...auth.useExplicitSignUpFlow !== void 0 ? { use_explicit_sign_up_flow: auth.useExplicitSignUpFlow } : {},
|
|
1257
|
+
...auth.allowUnverifiedUsers !== void 0 ? { allow_unverified_users: auth.allowUnverifiedUsers } : {},
|
|
1258
|
+
...auth.primarySignUpMethod ? { primary_sign_up_method: auth.primarySignUpMethod } : {},
|
|
1259
|
+
...auth.preferredMethod ? { preferred_method: auth.preferredMethod } : {},
|
|
1260
|
+
...auth.order ? { order: auth.order } : {},
|
|
1261
|
+
...isInstantAnonymousMethod(app.signInMethods) ? { instant_user: { enabled: true } } : {},
|
|
1262
|
+
show_app_icon: branding.showAppIcon ?? false
|
|
1263
|
+
},
|
|
1264
|
+
legal: {
|
|
1265
|
+
...app.legal?.companyName ? { company_name: app.legal.companyName } : {},
|
|
1266
|
+
...app.legal?.privacyPolicyUrl ? { privacy_policy_url: app.legal.privacyPolicyUrl } : {},
|
|
1267
|
+
...app.legal?.termsConditionsUrl ? { terms_conditions_url: app.legal.termsConditionsUrl } : {},
|
|
1268
|
+
...app.legal?.supportEmail ? { support_email: app.legal.supportEmail } : {}
|
|
1269
|
+
},
|
|
1270
|
+
custom_content: {
|
|
1271
|
+
...app.customContent?.signInModal ? {
|
|
1272
|
+
sign_in_modal: {
|
|
1273
|
+
...app.customContent.signInModal.title ? { title: app.customContent.signInModal.title } : {},
|
|
1274
|
+
...app.customContent.signInModal.subtitle ? { subtitle: app.customContent.signInModal.subtitle } : {},
|
|
1275
|
+
...app.customContent.signInModal.signInTitle ? {
|
|
1276
|
+
sign_in_title: app.customContent.signInModal.signInTitle
|
|
1277
|
+
} : {},
|
|
1278
|
+
...app.customContent.signInModal.signUpTitle ? {
|
|
1279
|
+
sign_up_title: app.customContent.signInModal.signUpTitle
|
|
1280
|
+
} : {},
|
|
1281
|
+
...app.customContent.signInModal.signInSubtitle ? {
|
|
1282
|
+
sign_in_subtitle: app.customContent.signInModal.signInSubtitle
|
|
1283
|
+
} : {},
|
|
1284
|
+
...app.customContent.signInModal.signUpSubtitle ? {
|
|
1285
|
+
sign_up_subtitle: app.customContent.signInModal.signUpSubtitle
|
|
1286
|
+
} : {},
|
|
1287
|
+
...app.customContent.signInModal.signInButton ? {
|
|
1288
|
+
sign_in_button: app.customContent.signInModal.signInButton
|
|
1289
|
+
} : {},
|
|
1290
|
+
...app.customContent.signInModal.signUpButton ? {
|
|
1291
|
+
sign_up_button: app.customContent.signInModal.signUpButton
|
|
1292
|
+
} : {}
|
|
1293
|
+
}
|
|
1294
|
+
} : {},
|
|
1295
|
+
...app.customContent?.profileModal ? { profile_modal: app.customContent.profileModal } : {},
|
|
1296
|
+
...app.customContent?.verificationModal ? {
|
|
1297
|
+
verification_modal: {
|
|
1298
|
+
...app.customContent.verificationModal.title ? { title: app.customContent.verificationModal.title } : {},
|
|
1299
|
+
...app.customContent.verificationModal.subtitle ? { subtitle: app.customContent.verificationModal.subtitle } : {}
|
|
1300
|
+
}
|
|
1301
|
+
} : {},
|
|
1302
|
+
...app.customContent?.signInFailureModal ? {
|
|
1303
|
+
sign_in_failure_modal: {
|
|
1304
|
+
failure_message: app.customContent.signInFailureModal.failureMessage
|
|
1305
|
+
}
|
|
1306
|
+
} : {},
|
|
1307
|
+
...app.customContent?.noAccountMessage ? { no_account_message: app.customContent.noAccountMessage } : {},
|
|
1308
|
+
...app.customContent?.mobile ? { mobile: app.customContent.mobile } : {}
|
|
1309
|
+
},
|
|
1310
|
+
profile: {
|
|
1311
|
+
...app.profile?.accountInformation ? { account_information: app.profile.accountInformation } : {},
|
|
1312
|
+
...app.profile?.personalInformation ? { personal_information: app.profile.personalInformation } : {},
|
|
1313
|
+
...app.profile?.preferences ? { preferences: app.profile.preferences } : {},
|
|
1314
|
+
...app.profile?.signOutButton ? { sign_out_button: app.profile.signOutButton } : {},
|
|
1315
|
+
...app.profile?.deleteAccountButton ? { delete_account_button: app.profile.deleteAccountButton } : {},
|
|
1316
|
+
...app.profile?.addSignInMethodsButton ? { add_sign_in_methods_button: app.profile.addSignInMethodsButton } : {}
|
|
1317
|
+
}
|
|
1909
1318
|
}
|
|
1910
1319
|
}
|
|
1911
|
-
}
|
|
1912
|
-
rownd_pending_verification: pendingVerifications.filter(
|
|
1913
|
-
(verification) => verification !== pendingVerification
|
|
1914
|
-
)
|
|
1320
|
+
}
|
|
1915
1321
|
};
|
|
1916
|
-
await import_usermetadata2.default.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
1917
1322
|
}
|
|
1918
|
-
|
|
1919
|
-
const metadata = await getUserMetadata(userId);
|
|
1920
|
-
const updatedMetadata = {
|
|
1921
|
-
...metadata,
|
|
1922
|
-
...inputMeta
|
|
1923
|
-
};
|
|
1924
|
-
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
1323
|
+
function buildAuthEmailConfig(authEmail) {
|
|
1925
1324
|
return {
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1325
|
+
from_address: authEmail?.fromAddress ?? "no-reply@rownd.io",
|
|
1326
|
+
image: authEmail?.image ?? "",
|
|
1327
|
+
...authEmail?.subject ? { subject: authEmail.subject } : {},
|
|
1328
|
+
...authEmail?.callToActionText ? { call_to_action_text: authEmail.callToActionText } : {},
|
|
1329
|
+
...authEmail?.verifyTemplate ? { verify_template: authEmail.verifyTemplate } : {},
|
|
1330
|
+
...authEmail?.customContent ? { custom_content: authEmail.customContent } : {},
|
|
1331
|
+
...authEmail?.customClosingContent ? { custom_closing_content: authEmail.customClosingContent } : {}
|
|
1932
1332
|
};
|
|
1933
1333
|
}
|
|
1934
|
-
function
|
|
1935
|
-
return
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
}
|
|
1944
|
-
function hasAnonymousLoginMethod(user) {
|
|
1945
|
-
return !!user?.loginMethods.some((loginMethod) => {
|
|
1946
|
-
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1947
|
-
});
|
|
1948
|
-
}
|
|
1949
|
-
function getPasswordlessEmailLoginMethod(user) {
|
|
1950
|
-
return user?.loginMethods.find((method) => {
|
|
1951
|
-
return method.recipeId === "passwordless" && !!method.email;
|
|
1952
|
-
});
|
|
1953
|
-
}
|
|
1954
|
-
function hasOnlyGuestLoginMethods(user) {
|
|
1955
|
-
return !!user?.loginMethods.length && user.loginMethods.every(isGuestLoginMethod);
|
|
1334
|
+
function buildAuthMobileConfig(authMobile) {
|
|
1335
|
+
return {
|
|
1336
|
+
...authMobile?.title ? { title: authMobile.title } : {},
|
|
1337
|
+
...authMobile?.image ? { image: authMobile.image } : {},
|
|
1338
|
+
...authMobile?.callToActionText ? { call_to_action_text: authMobile.callToActionText } : {},
|
|
1339
|
+
...authMobile?.hyperlinkText ? { hyperlink_text: authMobile.hyperlinkText } : {},
|
|
1340
|
+
...authMobile?.hyperlinkRedirectUrl ? { hyperlink_redirect_url: authMobile.hyperlinkRedirectUrl } : {},
|
|
1341
|
+
...authMobile?.customContent ? { custom_content: authMobile.customContent } : {}
|
|
1342
|
+
};
|
|
1956
1343
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1344
|
+
|
|
1345
|
+
// src/rownd-compatibility.ts
|
|
1346
|
+
var import_supertokens_node = __toESM(require("supertokens-node"));
|
|
1347
|
+
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1348
|
+
"user_id",
|
|
1349
|
+
"email",
|
|
1350
|
+
"phone_number",
|
|
1351
|
+
"google_id",
|
|
1352
|
+
"apple_id"
|
|
1353
|
+
]);
|
|
1354
|
+
var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1355
|
+
"original_rownd_user",
|
|
1356
|
+
"rownd_pending_verification"
|
|
1357
|
+
]);
|
|
1358
|
+
function isIdentityField(field) {
|
|
1359
|
+
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1960
1360
|
}
|
|
1961
|
-
function
|
|
1962
|
-
return
|
|
1361
|
+
function isInternalMetadataField(field) {
|
|
1362
|
+
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1963
1363
|
}
|
|
1964
|
-
function
|
|
1965
|
-
|
|
1966
|
-
|
|
1364
|
+
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1365
|
+
const loginMethods = [];
|
|
1366
|
+
const rowndUserData = rowndUser.data || {};
|
|
1367
|
+
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
1368
|
+
if (!rowndUserData.user_id) {
|
|
1369
|
+
throw new Error("Rownd user has no user_id");
|
|
1967
1370
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1371
|
+
if (rowndUserData.google_id) {
|
|
1372
|
+
if (!rowndUserData.email) {
|
|
1373
|
+
throw new Error("Rownd Google user is missing email");
|
|
1374
|
+
}
|
|
1375
|
+
loginMethods.push({
|
|
1376
|
+
recipeId: "thirdparty",
|
|
1377
|
+
thirdPartyId: "google",
|
|
1378
|
+
thirdPartyUserId: rowndUserData.google_id,
|
|
1379
|
+
email: rowndUserData.email,
|
|
1380
|
+
isVerified: !!rowndUserVerifiedData.google_id
|
|
1975
1381
|
});
|
|
1976
1382
|
}
|
|
1977
|
-
if (
|
|
1978
|
-
|
|
1979
|
-
|
|
1383
|
+
if (rowndUserData.apple_id) {
|
|
1384
|
+
if (!rowndUserData.email) {
|
|
1385
|
+
throw new Error("Rownd Apple user is missing email");
|
|
1386
|
+
}
|
|
1387
|
+
loginMethods.push({
|
|
1388
|
+
recipeId: "thirdparty",
|
|
1389
|
+
thirdPartyId: "apple",
|
|
1390
|
+
thirdPartyUserId: rowndUserData.apple_id,
|
|
1391
|
+
email: rowndUserData.email,
|
|
1392
|
+
isVerified: !!rowndUserVerifiedData.apple_id
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
if (rowndUserData.phone_number) {
|
|
1396
|
+
loginMethods.push({
|
|
1397
|
+
recipeId: "passwordless",
|
|
1398
|
+
phoneNumber: rowndUserData.phone_number,
|
|
1399
|
+
isVerified: !!rowndUserVerifiedData.phone_number
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
1403
|
+
loginMethods.push({
|
|
1404
|
+
recipeId: "passwordless",
|
|
1405
|
+
email: rowndUserData.email,
|
|
1406
|
+
isVerified: !!rowndUserVerifiedData.email
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
let authLevel = rowndUser.auth_level;
|
|
1410
|
+
if (loginMethods.length === 0) {
|
|
1411
|
+
const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : ANONYMOUS_AUTH_METHOD_ID;
|
|
1412
|
+
if (!authLevel) authLevel = thirdPartyId;
|
|
1413
|
+
loginMethods.push({
|
|
1414
|
+
recipeId: "thirdparty",
|
|
1415
|
+
thirdPartyId,
|
|
1416
|
+
thirdPartyUserId: rowndUserData.user_id,
|
|
1417
|
+
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1418
|
+
isVerified: false
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
1422
|
+
return {
|
|
1423
|
+
externalUserId: rowndUserData.user_id,
|
|
1424
|
+
loginMethods,
|
|
1425
|
+
userMetadata
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
function buildRowndUserMetadata(rowndUser) {
|
|
1429
|
+
const metadata = {
|
|
1430
|
+
...rowndUser.meta || {},
|
|
1431
|
+
original_rownd_user: rowndUser
|
|
1432
|
+
};
|
|
1433
|
+
for (const [key, value] of Object.entries(rowndUser.data || {})) {
|
|
1434
|
+
if (!isIdentityField(key) && value !== void 0) {
|
|
1435
|
+
metadata[key] = value;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
return metadata;
|
|
1439
|
+
}
|
|
1440
|
+
function buildRowndAudience(currentPayload, appVariantId) {
|
|
1441
|
+
const audience = getStringList(currentPayload.aud);
|
|
1442
|
+
const appId = getPluginConfig()?.appConfig?.id;
|
|
1443
|
+
if (appId) {
|
|
1444
|
+
audience.push(`app:${appId}`);
|
|
1445
|
+
}
|
|
1446
|
+
if (appVariantId) {
|
|
1447
|
+
audience.push(`app_variant:${appVariantId}`);
|
|
1448
|
+
}
|
|
1449
|
+
return audience.length > 0 ? { aud: [...new Set(audience)] } : {};
|
|
1450
|
+
}
|
|
1451
|
+
function buildConfiguredSessionClaims(metadata) {
|
|
1452
|
+
if (!metadata) {
|
|
1453
|
+
return {};
|
|
1454
|
+
}
|
|
1455
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1456
|
+
const claims = {};
|
|
1457
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1458
|
+
if (field.include_in_session_claims !== true) {
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
const value = metadata.original_rownd_user?.data?.[key] ?? metadata[key];
|
|
1462
|
+
if (value !== void 0) {
|
|
1463
|
+
claims[field.session_claim_name || key] = value;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return claims;
|
|
1467
|
+
}
|
|
1468
|
+
function getRowndAppUserId(userId, user, currentPayload, metadata) {
|
|
1469
|
+
const originalUserId = metadata?.original_rownd_user?.data?.user_id;
|
|
1470
|
+
if (typeof originalUserId === "string") {
|
|
1471
|
+
return originalUserId;
|
|
1472
|
+
}
|
|
1473
|
+
if (typeof currentPayload.app_user_id === "string") {
|
|
1474
|
+
return currentPayload.app_user_id;
|
|
1475
|
+
}
|
|
1476
|
+
return user?.id || userId;
|
|
1477
|
+
}
|
|
1478
|
+
function getAnonymousId(userId, user, metadata) {
|
|
1479
|
+
const originalAnonymousId = metadata?.original_rownd_user?.data?.anonymous_id;
|
|
1480
|
+
if (typeof originalAnonymousId === "string") {
|
|
1481
|
+
return originalAnonymousId;
|
|
1482
|
+
}
|
|
1483
|
+
const anonymousMethod = user?.loginMethods.find((loginMethod) => {
|
|
1484
|
+
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1485
|
+
});
|
|
1486
|
+
const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
|
|
1487
|
+
return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
|
|
1488
|
+
}
|
|
1489
|
+
function buildRowndSessionClaimPayload(input) {
|
|
1490
|
+
const currentPayload = input.currentPayload ?? {};
|
|
1491
|
+
const originalRowndUser = input.metadata?.original_rownd_user;
|
|
1492
|
+
const verifiedData = originalRowndUser?.verified_data;
|
|
1493
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1494
|
+
input.user,
|
|
1495
|
+
originalRowndUser?.auth_level,
|
|
1496
|
+
verifiedData
|
|
1497
|
+
);
|
|
1498
|
+
const appUserId = getRowndAppUserId(
|
|
1499
|
+
input.userId,
|
|
1500
|
+
input.user,
|
|
1501
|
+
currentPayload,
|
|
1502
|
+
input.metadata
|
|
1503
|
+
);
|
|
1504
|
+
const isAnonymous = authLevel === GUEST_AUTH_METHOD_ID;
|
|
1505
|
+
const anonymousId = getAnonymousId(input.userId, input.user, input.metadata);
|
|
1506
|
+
const isVerifiedUser = authLevel !== "unverified";
|
|
1507
|
+
const audience = buildRowndAudience(currentPayload, input.appVariantId);
|
|
1508
|
+
const configuredClaims = buildConfiguredSessionClaims(input.metadata);
|
|
1509
|
+
return {
|
|
1510
|
+
...audience,
|
|
1511
|
+
...configuredClaims,
|
|
1512
|
+
app_user_id: appUserId,
|
|
1513
|
+
auth_level: authLevel,
|
|
1514
|
+
is_verified_user: isVerifiedUser,
|
|
1515
|
+
[ROWND_JWT_CLAIMS.AppUserId]: appUserId,
|
|
1516
|
+
[ROWND_JWT_CLAIMS.AuthLevel]: authLevel,
|
|
1517
|
+
[ROWND_JWT_CLAIMS.IsVerifiedUser]: isVerifiedUser,
|
|
1518
|
+
[ROWND_JWT_CLAIMS.IsAnonymous]: isAnonymous,
|
|
1519
|
+
...anonymousId ? { anonymous_id: anonymousId } : {}
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
async function shouldLinkRowndAccounts(input) {
|
|
1523
|
+
const [newAccountInfo, , session] = input;
|
|
1524
|
+
if (!session) {
|
|
1525
|
+
return void 0;
|
|
1526
|
+
}
|
|
1527
|
+
const currentUser = await import_supertokens_node.default.getUser(session.getUserId());
|
|
1528
|
+
if (hasOnlyGuestLoginMethods(currentUser)) {
|
|
1529
|
+
return {
|
|
1530
|
+
shouldAutomaticallyLink: true,
|
|
1531
|
+
shouldRequireVerification: false
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
if (!currentUser || isGuestAccountInfo(newAccountInfo)) {
|
|
1535
|
+
return void 0;
|
|
1536
|
+
}
|
|
1537
|
+
if (doesAccountInfoMatchAuthMethod(currentUser, newAccountInfo)) {
|
|
1538
|
+
return {
|
|
1539
|
+
shouldAutomaticallyLink: true,
|
|
1540
|
+
shouldRequireVerification: true
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
return void 0;
|
|
1544
|
+
}
|
|
1545
|
+
function mapMethod(method) {
|
|
1546
|
+
if (method.recipeId === "thirdparty") {
|
|
1547
|
+
if (getThirdPartyId(method) === "google") return "google";
|
|
1548
|
+
if (getThirdPartyId(method) === "apple") return "apple";
|
|
1549
|
+
} else if (method.recipeId === "passwordless") {
|
|
1550
|
+
if (method.email) return "email";
|
|
1551
|
+
if (method.phoneNumber) return "phone";
|
|
1552
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1553
|
+
return "email";
|
|
1554
|
+
}
|
|
1555
|
+
return "email";
|
|
1556
|
+
}
|
|
1557
|
+
function getThirdPartyId(method) {
|
|
1558
|
+
return method.thirdParty?.id;
|
|
1559
|
+
}
|
|
1560
|
+
function getThirdPartyUserId(method) {
|
|
1561
|
+
return method.thirdParty?.userId;
|
|
1562
|
+
}
|
|
1563
|
+
function getGuestAuthLevel(user) {
|
|
1564
|
+
const guestMethod = user?.loginMethods.find(isGuestLoginMethod);
|
|
1565
|
+
return guestMethod ? GUEST_AUTH_METHOD_ID : void 0;
|
|
1566
|
+
}
|
|
1567
|
+
function hasAnonymousLoginMethod(user) {
|
|
1568
|
+
return !!user?.loginMethods.some((loginMethod) => {
|
|
1569
|
+
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
function hasOnlyGuestLoginMethods(user) {
|
|
1573
|
+
return !!user?.loginMethods.length && user.loginMethods.every(isGuestLoginMethod);
|
|
1574
|
+
}
|
|
1575
|
+
function isGuestLoginMethod(method) {
|
|
1576
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1577
|
+
return method.recipeId === "thirdparty" && (thirdPartyId === GUEST_AUTH_METHOD_ID || thirdPartyId === ANONYMOUS_AUTH_METHOD_ID);
|
|
1578
|
+
}
|
|
1579
|
+
function isGuestAccountInfo(input) {
|
|
1580
|
+
return input?.recipeId === "thirdparty" && (input.thirdParty?.id === GUEST_AUTH_METHOD_ID || input.thirdParty?.id === ANONYMOUS_AUTH_METHOD_ID);
|
|
1581
|
+
}
|
|
1582
|
+
function doesAccountInfoMatchAuthMethod(user, accountInfo) {
|
|
1583
|
+
if (!user) {
|
|
1584
|
+
return false;
|
|
1585
|
+
}
|
|
1586
|
+
const normalizedEmail = accountInfo.email?.toLowerCase();
|
|
1587
|
+
if (normalizedEmail) {
|
|
1588
|
+
return user.loginMethods.some((method) => {
|
|
1589
|
+
if (isGuestLoginMethod(method) || !method.email) {
|
|
1590
|
+
return false;
|
|
1591
|
+
}
|
|
1592
|
+
return method.email.toLowerCase() === normalizedEmail;
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
if (accountInfo.phoneNumber) {
|
|
1596
|
+
return user.loginMethods.some((method) => {
|
|
1597
|
+
return !isGuestLoginMethod(method) && method.phoneNumber === accountInfo.phoneNumber;
|
|
1980
1598
|
});
|
|
1981
1599
|
}
|
|
1982
1600
|
return false;
|
|
@@ -2005,7 +1623,7 @@ function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
|
|
|
2005
1623
|
return getGuestAuthLevel(user) || originalAuthLevel || (verifiedData && Object.keys(verifiedData).length > 0 ? "verified" : "unverified");
|
|
2006
1624
|
}
|
|
2007
1625
|
function canUpdateUserDataField(field) {
|
|
2008
|
-
const schema =
|
|
1626
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
2009
1627
|
const schemaField = schema[field];
|
|
2010
1628
|
if (!schemaField) {
|
|
2011
1629
|
return false;
|
|
@@ -2013,350 +1631,808 @@ function canUpdateUserDataField(field) {
|
|
|
2013
1631
|
const ownedBy = field === "google_id" || field === "apple_id" ? "app" : schemaField.owned_by || "user";
|
|
2014
1632
|
return ownedBy !== "app" && schemaField.read_only !== true;
|
|
2015
1633
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
if (key === "google_id" || key === "apple_id") {
|
|
2026
|
-
ownedBy = "app";
|
|
2027
|
-
} else if (!ownedBy) {
|
|
2028
|
-
ownedBy = "user";
|
|
1634
|
+
|
|
1635
|
+
// src/rownd-repository.ts
|
|
1636
|
+
var rowndClient;
|
|
1637
|
+
function setRowndClient(client) {
|
|
1638
|
+
rowndClient = client;
|
|
1639
|
+
}
|
|
1640
|
+
function getRowndClient() {
|
|
1641
|
+
if (!rowndClient) {
|
|
1642
|
+
throw new Error("Rownd client not initialized");
|
|
2029
1643
|
}
|
|
2030
|
-
return
|
|
2031
|
-
display_name: field.display_name,
|
|
2032
|
-
type: field.type,
|
|
2033
|
-
owned_by: ownedBy,
|
|
2034
|
-
user_visible: field.user_visible,
|
|
2035
|
-
read_only: field.read_only ?? ownedBy === "app",
|
|
2036
|
-
show_empty: field.show_empty ?? false
|
|
2037
|
-
};
|
|
1644
|
+
return rowndClient;
|
|
2038
1645
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
acc[curr.method] = curr;
|
|
2044
|
-
return acc;
|
|
2045
|
-
},
|
|
2046
|
-
{}
|
|
2047
|
-
);
|
|
2048
|
-
const customProviders = Object.fromEntries(
|
|
2049
|
-
Object.entries(methods).filter(([key]) => !BUILTIN_SIGN_IN_METHOD_KEYS.includes(key)).map(([key, val]) => {
|
|
2050
|
-
return val ? [
|
|
2051
|
-
key,
|
|
2052
|
-
{
|
|
2053
|
-
enabled: true,
|
|
2054
|
-
display_name: getStringMethodProperty(val, "displayName") ?? key,
|
|
2055
|
-
icon_light_url: getStringMethodProperty(val, "iconLightUrl"),
|
|
2056
|
-
icon_dark_url: getStringMethodProperty(val, "iconDarkUrl")
|
|
2057
|
-
}
|
|
2058
|
-
] : [key, void 0];
|
|
2059
|
-
}).filter(([, v]) => v !== void 0)
|
|
2060
|
-
);
|
|
2061
|
-
const googleMethod = methods.google;
|
|
2062
|
-
const appleMethod = methods.apple;
|
|
2063
|
-
const anonymousMethod = methods.anonymous;
|
|
2064
|
-
const anonymousType = getAnonymousType(anonymousMethod);
|
|
2065
|
-
const googleOneTap = getOneTapConfig(googleMethod);
|
|
2066
|
-
return {
|
|
2067
|
-
email: { enabled: !!methods.email },
|
|
2068
|
-
phone: { enabled: !!methods.phone },
|
|
2069
|
-
google: {
|
|
2070
|
-
enabled: !!googleMethod,
|
|
2071
|
-
client_id: getStringMethodProperty(googleMethod, "clientId") ?? "",
|
|
2072
|
-
ios_client_id: getStringMethodProperty(googleMethod, "iosClientId") ?? "",
|
|
2073
|
-
scopes: getStringArrayMethodProperty(googleMethod, "scopes") ?? [],
|
|
2074
|
-
...getSignInFasterWithGoogle(googleMethod) ? { sign_in_faster_with_google: getSignInFasterWithGoogle(googleMethod) } : {},
|
|
2075
|
-
one_tap: {
|
|
2076
|
-
browser: {
|
|
2077
|
-
auto_prompt: googleOneTap?.browser?.autoPrompt ?? false,
|
|
2078
|
-
delay: googleOneTap?.browser?.delay ?? 7e3
|
|
2079
|
-
},
|
|
2080
|
-
mobile_app: {
|
|
2081
|
-
auto_prompt: googleOneTap?.mobileApp?.autoPrompt ?? false,
|
|
2082
|
-
delay: googleOneTap?.mobileApp?.delay ?? 7e3
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
},
|
|
2086
|
-
apple: {
|
|
2087
|
-
enabled: !!appleMethod,
|
|
2088
|
-
client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
|
|
2089
|
-
},
|
|
2090
|
-
anonymous: {
|
|
2091
|
-
enabled: !!anonymousMethod && anonymousType !== "instant",
|
|
2092
|
-
...anonymousMethod && anonymousType !== "instant" ? { type: anonymousType } : {},
|
|
2093
|
-
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "displayName") !== void 0 ? {
|
|
2094
|
-
display_name: getStringMethodProperty(
|
|
2095
|
-
anonymousMethod,
|
|
2096
|
-
"displayName"
|
|
2097
|
-
)
|
|
2098
|
-
} : {},
|
|
2099
|
-
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconLightUrl") !== void 0 ? {
|
|
2100
|
-
icon_light_url: getStringMethodProperty(
|
|
2101
|
-
anonymousMethod,
|
|
2102
|
-
"iconLightUrl"
|
|
2103
|
-
)
|
|
2104
|
-
} : {},
|
|
2105
|
-
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconDarkUrl") !== void 0 ? {
|
|
2106
|
-
icon_dark_url: getStringMethodProperty(
|
|
2107
|
-
anonymousMethod,
|
|
2108
|
-
"iconDarkUrl"
|
|
2109
|
-
)
|
|
2110
|
-
} : {}
|
|
2111
|
-
},
|
|
2112
|
-
...customProviders
|
|
2113
|
-
};
|
|
2114
|
-
}
|
|
2115
|
-
function isInstantAnonymousMethod(methods) {
|
|
2116
|
-
return (methods ?? []).some(
|
|
2117
|
-
(method) => method.method === "anonymous" && getAnonymousType(method) === "instant"
|
|
2118
|
-
);
|
|
1646
|
+
async function validateRowndToken(token) {
|
|
1647
|
+
const client = getRowndClient();
|
|
1648
|
+
const tokenInfo = await client.validateToken(token);
|
|
1649
|
+
return tokenInfo.user_id;
|
|
2119
1650
|
}
|
|
2120
|
-
function
|
|
2121
|
-
const
|
|
2122
|
-
return
|
|
1651
|
+
async function fetchOptionalRowndUserInfo(userId) {
|
|
1652
|
+
const client = getRowndClient();
|
|
1653
|
+
return client.fetchUserInfo({ user_id: userId });
|
|
2123
1654
|
}
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
1655
|
+
|
|
1656
|
+
// src/supertokens-repository.ts
|
|
1657
|
+
var import_supertokens_node2 = __toESM(require("supertokens-node"));
|
|
1658
|
+
var import_accountlinking = __toESM(require("supertokens-node/recipe/accountlinking"));
|
|
1659
|
+
var import_emailverification = __toESM(require("supertokens-node/recipe/emailverification"));
|
|
1660
|
+
var import_passwordless = __toESM(require("supertokens-node/recipe/passwordless"));
|
|
1661
|
+
var import_session = __toESM(require("supertokens-node/recipe/session"));
|
|
1662
|
+
var import_claims = require("supertokens-node/recipe/session/claims");
|
|
1663
|
+
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1664
|
+
async function importUser(stUser, config2) {
|
|
1665
|
+
const headers = {
|
|
1666
|
+
"Content-Type": "application/json"
|
|
1667
|
+
};
|
|
1668
|
+
if (config2.apiKey) {
|
|
1669
|
+
headers["api-key"] = config2.apiKey;
|
|
1670
|
+
}
|
|
1671
|
+
const response = await fetch(`${config2.connectionURI}/bulk-import/import`, {
|
|
1672
|
+
method: "POST",
|
|
1673
|
+
headers,
|
|
1674
|
+
body: JSON.stringify(stUser)
|
|
1675
|
+
});
|
|
1676
|
+
if (!response.ok) {
|
|
1677
|
+
const errorText = await response.text();
|
|
1678
|
+
throw new Error(
|
|
1679
|
+
`Bulk import failed with status ${response.status}: ${errorText}`
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1682
|
+
const importResponse = await response.json();
|
|
1683
|
+
if (importResponse.status !== "OK" || !importResponse.user) {
|
|
1684
|
+
throw new Error(
|
|
1685
|
+
`Bulk import failed: ${importResponse.message || "Missing user in response"}`
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
return importResponse.user;
|
|
2127
1689
|
}
|
|
2128
|
-
function
|
|
2129
|
-
if (!
|
|
2130
|
-
return
|
|
1690
|
+
async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
1691
|
+
if (!appVariantId) {
|
|
1692
|
+
return;
|
|
2131
1693
|
}
|
|
2132
|
-
|
|
1694
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1695
|
+
const metadata = await getUserMetadata(userId);
|
|
1696
|
+
const originalRowndUser = isJsonRecord(metadata.original_rownd_user) ? metadata.original_rownd_user : {};
|
|
1697
|
+
const attributes = isJsonRecord(originalRowndUser.attributes) ? originalRowndUser.attributes : {};
|
|
1698
|
+
const appVariants = getStringList(attributes["rownd:app_variants"]);
|
|
1699
|
+
if (appVariants.includes(appVariantId)) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
await import_usermetadata2.default.updateUserMetadata(userId, {
|
|
1703
|
+
...metadata,
|
|
1704
|
+
original_rownd_user: {
|
|
1705
|
+
...originalRowndUser,
|
|
1706
|
+
data: isJsonRecord(originalRowndUser.data) ? originalRowndUser.data : { user_id: userId },
|
|
1707
|
+
verified_data: isJsonRecord(originalRowndUser.verified_data) ? originalRowndUser.verified_data : {},
|
|
1708
|
+
attributes: {
|
|
1709
|
+
...attributes,
|
|
1710
|
+
"rownd:app_variants": [...appVariants, appVariantId]
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
});
|
|
2133
1714
|
}
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
1715
|
+
var RowndIsAnonymousClaim = new import_claims.BooleanClaim({
|
|
1716
|
+
key: "is_anonymous",
|
|
1717
|
+
fetchValue: async (userId) => {
|
|
1718
|
+
const user = await import_supertokens_node2.default.getUser(userId);
|
|
1719
|
+
const effectiveAuthLevel = getEffectiveAuthLevel(user);
|
|
1720
|
+
return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
|
|
1724
|
+
const user = await import_supertokens_node2.default.getUser(userId);
|
|
1725
|
+
const metadata = user ? await getUserMetadata(user.id) : void 0;
|
|
1726
|
+
return buildRowndSessionClaimPayload({
|
|
1727
|
+
userId,
|
|
1728
|
+
user,
|
|
1729
|
+
metadata,
|
|
1730
|
+
currentPayload,
|
|
1731
|
+
appVariantId
|
|
1732
|
+
});
|
|
2137
1733
|
}
|
|
2138
|
-
function
|
|
2139
|
-
const
|
|
2140
|
-
return
|
|
1734
|
+
async function getUserMetadata(userId) {
|
|
1735
|
+
const metadata = await import_usermetadata2.default.getUserMetadata(userId);
|
|
1736
|
+
return metadata.metadata || {};
|
|
2141
1737
|
}
|
|
2142
|
-
function
|
|
2143
|
-
const
|
|
2144
|
-
if (
|
|
2145
|
-
return
|
|
1738
|
+
function getPendingVerifications(metadata) {
|
|
1739
|
+
const pendingVerification = metadata.rownd_pending_verification;
|
|
1740
|
+
if (Array.isArray(pendingVerification)) {
|
|
1741
|
+
return pendingVerification.filter(isPendingVerification);
|
|
2146
1742
|
}
|
|
2147
|
-
return
|
|
2148
|
-
browser: parseOneTapPlatform(oneTap.browser),
|
|
2149
|
-
mobileApp: parseOneTapPlatform(oneTap.mobileApp)
|
|
2150
|
-
};
|
|
1743
|
+
return [];
|
|
2151
1744
|
}
|
|
2152
|
-
function
|
|
2153
|
-
|
|
2154
|
-
|
|
1745
|
+
function isPendingVerification(value) {
|
|
1746
|
+
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
1747
|
+
}
|
|
1748
|
+
async function getUserById(userId) {
|
|
1749
|
+
const metadata = await getUserMetadata(userId);
|
|
1750
|
+
const stUser = await import_supertokens_node2.default.getUser(userId);
|
|
1751
|
+
if (!stUser) {
|
|
1752
|
+
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
2155
1753
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
1754
|
+
const originalRowndUser = metadata.original_rownd_user;
|
|
1755
|
+
const rowndUser = originalRowndUser?.data?.user_id || userId;
|
|
1756
|
+
const state = originalRowndUser?.state || "enabled";
|
|
1757
|
+
const dataFieldKeys = /* @__PURE__ */ new Set();
|
|
1758
|
+
const data = {
|
|
1759
|
+
user_id: userId
|
|
2159
1760
|
};
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
if (isRecord(app) && isRecord(app.variant) && typeof app.variant.id === "string") {
|
|
2166
|
-
return app.variant;
|
|
1761
|
+
for (const [key, value] of Object.entries(originalRowndUser?.data || {})) {
|
|
1762
|
+
if (!isIdentityField(key)) {
|
|
1763
|
+
data[key] = value;
|
|
1764
|
+
dataFieldKeys.add(key);
|
|
1765
|
+
}
|
|
2167
1766
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
if (value === void 0) {
|
|
2174
|
-
continue;
|
|
1767
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1768
|
+
for (const key of Object.keys(schema)) {
|
|
1769
|
+
dataFieldKeys.add(key);
|
|
1770
|
+
if (!isInternalMetadataField(key) && !isIdentityField(key) && metadata[key] !== void 0) {
|
|
1771
|
+
data[key] = metadata[key];
|
|
2175
1772
|
}
|
|
2176
|
-
const existing = result[key];
|
|
2177
|
-
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
2178
1773
|
}
|
|
2179
|
-
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
const
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
1774
|
+
const verifiedData = {
|
|
1775
|
+
...originalRowndUser?.verified_data || {}
|
|
1776
|
+
};
|
|
1777
|
+
for (const method of stUser.loginMethods) {
|
|
1778
|
+
if (method.recipeId === "passwordless") {
|
|
1779
|
+
if (method.email) {
|
|
1780
|
+
verifiedData.email = method.email;
|
|
1781
|
+
if (data.email === void 0) data.email = method.email;
|
|
1782
|
+
}
|
|
1783
|
+
if (method.phoneNumber) {
|
|
1784
|
+
verifiedData.phone_number = method.phoneNumber;
|
|
1785
|
+
if (data.phone_number === void 0)
|
|
1786
|
+
data.phone_number = method.phoneNumber;
|
|
1787
|
+
}
|
|
1788
|
+
} else if (method.recipeId === "thirdparty") {
|
|
1789
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1790
|
+
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
1791
|
+
if (method.verified && method.email) {
|
|
1792
|
+
verifiedData.email = method.email;
|
|
1793
|
+
}
|
|
1794
|
+
if (method.email && data.email === void 0) {
|
|
1795
|
+
data.email = method.email;
|
|
1796
|
+
}
|
|
1797
|
+
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
1798
|
+
data.google_id = thirdPartyUserId;
|
|
1799
|
+
verifiedData.google_id = thirdPartyUserId;
|
|
1800
|
+
}
|
|
1801
|
+
if (thirdPartyId === "apple" && thirdPartyUserId) {
|
|
1802
|
+
data.apple_id = thirdPartyUserId;
|
|
1803
|
+
verifiedData.apple_id = thirdPartyUserId;
|
|
1804
|
+
}
|
|
1805
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1806
|
+
if (method.email && data.email === void 0) {
|
|
1807
|
+
data.email = method.email;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
2188
1810
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
const signInMethods = buildSignInMethodsConfig(app.signInMethods);
|
|
2192
|
-
const variant = getSubBrandVariant(app);
|
|
2193
|
-
const finalSchema = { ...userSchema };
|
|
2194
|
-
if (signInMethods.email.enabled && !finalSchema.email) {
|
|
2195
|
-
finalSchema.email = {
|
|
2196
|
-
display_name: "Email",
|
|
2197
|
-
type: "string",
|
|
2198
|
-
user_visible: true
|
|
2199
|
-
};
|
|
1811
|
+
if (verifiedData.email === true && typeof data.email === "string") {
|
|
1812
|
+
verifiedData.email = data.email;
|
|
2200
1813
|
}
|
|
2201
|
-
if (
|
|
2202
|
-
|
|
2203
|
-
display_name: "Phone number",
|
|
2204
|
-
type: "string",
|
|
2205
|
-
user_visible: true
|
|
2206
|
-
};
|
|
1814
|
+
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
1815
|
+
verifiedData.phone_number = data.phone_number;
|
|
2207
1816
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
type: "string",
|
|
2212
|
-
user_visible: false
|
|
2213
|
-
};
|
|
1817
|
+
const anonymousId = getAnonymousId(stUser.id, stUser, metadata);
|
|
1818
|
+
if (anonymousId && data.anonymous_id === void 0) {
|
|
1819
|
+
data.anonymous_id = anonymousId;
|
|
2214
1820
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
1821
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1822
|
+
stUser,
|
|
1823
|
+
originalRowndUser?.auth_level,
|
|
1824
|
+
verifiedData
|
|
1825
|
+
);
|
|
1826
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1827
|
+
if (data[key] === void 0 && field.type === "string") {
|
|
1828
|
+
data[key] = "";
|
|
1829
|
+
}
|
|
2221
1830
|
}
|
|
1831
|
+
const sortedByJoined = [...stUser.loginMethods].sort(
|
|
1832
|
+
(a, b) => a.timeJoined - b.timeJoined
|
|
1833
|
+
);
|
|
1834
|
+
const latestSessionInfo = await getLatestSessionInfo(stUser.id);
|
|
1835
|
+
const firstMethod = sortedByJoined[0];
|
|
1836
|
+
const latestSessionRecipeUserId = latestSessionInfo?.recipeUserId.getAsString();
|
|
1837
|
+
const lastMethod = latestSessionRecipeUserId ? stUser.loginMethods.find(
|
|
1838
|
+
(method) => method.recipeUserId.getAsString() === latestSessionRecipeUserId
|
|
1839
|
+
) : [...stUser.loginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
|
|
1840
|
+
const lastSignInAt = latestSessionInfo?.timeCreated ?? stUser.timeJoined;
|
|
1841
|
+
const metadataMeta = Object.fromEntries(
|
|
1842
|
+
Object.entries(metadata).filter(
|
|
1843
|
+
([key]) => !isInternalMetadataField(key) && !dataFieldKeys.has(key)
|
|
1844
|
+
)
|
|
1845
|
+
);
|
|
1846
|
+
const meta = {
|
|
1847
|
+
...metadataMeta,
|
|
1848
|
+
created: new Date(stUser.timeJoined).toISOString(),
|
|
1849
|
+
first_sign_in: new Date(stUser.timeJoined).toISOString(),
|
|
1850
|
+
last_sign_in: new Date(lastSignInAt).toISOString(),
|
|
1851
|
+
last_active: new Date(lastSignInAt).toISOString(),
|
|
1852
|
+
first_sign_in_method: firstMethod ? mapMethod(firstMethod) : "email",
|
|
1853
|
+
last_sign_in_method: lastMethod ? mapMethod(lastMethod) : "email"
|
|
1854
|
+
};
|
|
2222
1855
|
return {
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
1856
|
+
rownd_user: rowndUser,
|
|
1857
|
+
data,
|
|
1858
|
+
meta,
|
|
1859
|
+
verified_data: verifiedData,
|
|
1860
|
+
state,
|
|
1861
|
+
auth_level: authLevel,
|
|
1862
|
+
redacted: [],
|
|
1863
|
+
groups: originalRowndUser?.groups || [],
|
|
1864
|
+
attributes: originalRowndUser?.attributes || {}
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
async function getLatestSessionInfo(userId) {
|
|
1868
|
+
const sessionHandles = await import_session.default.getAllSessionHandlesForUser(
|
|
1869
|
+
userId,
|
|
1870
|
+
true,
|
|
1871
|
+
PUBLIC_TENANT_ID
|
|
1872
|
+
);
|
|
1873
|
+
const sessionInfos = await Promise.all(
|
|
1874
|
+
sessionHandles.map(
|
|
1875
|
+
(sessionHandle) => import_session.default.getSessionInformation(sessionHandle)
|
|
1876
|
+
)
|
|
1877
|
+
);
|
|
1878
|
+
let latestSessionInfo;
|
|
1879
|
+
for (const sessionInfo of sessionInfos) {
|
|
1880
|
+
if (sessionInfo && (!latestSessionInfo || sessionInfo.timeCreated > latestSessionInfo.timeCreated)) {
|
|
1881
|
+
latestSessionInfo = sessionInfo;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
return latestSessionInfo;
|
|
1885
|
+
}
|
|
1886
|
+
async function updateUserData(userId, inputData) {
|
|
1887
|
+
const metadata = await getUserMetadata(userId);
|
|
1888
|
+
const updatedMetadata = {
|
|
1889
|
+
...metadata,
|
|
1890
|
+
...inputData
|
|
1891
|
+
};
|
|
1892
|
+
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
1893
|
+
return getUserById(userId);
|
|
1894
|
+
}
|
|
1895
|
+
async function startPendingEmailVerification(input) {
|
|
1896
|
+
const metadata = await getUserMetadata(input.userId);
|
|
1897
|
+
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
1898
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1899
|
+
const pendingEmailVerifications = pendingVerifications.filter(
|
|
1900
|
+
(pendingVerification2) => pendingVerification2.field === "email"
|
|
1901
|
+
);
|
|
1902
|
+
if (currentEmail === input.email) {
|
|
1903
|
+
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1904
|
+
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1905
|
+
input.tenantId,
|
|
1906
|
+
input.recipeUserId,
|
|
1907
|
+
pendingVerification2.value,
|
|
1908
|
+
input.userContext
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
if (pendingEmailVerifications.length > 0) {
|
|
1912
|
+
await import_usermetadata2.default.updateUserMetadata(input.userId, {
|
|
1913
|
+
...metadata,
|
|
1914
|
+
rownd_pending_verification: pendingVerifications.filter(
|
|
1915
|
+
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
1916
|
+
)
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
return getUserById(input.userId);
|
|
1920
|
+
}
|
|
1921
|
+
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1922
|
+
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1923
|
+
input.tenantId,
|
|
1924
|
+
input.recipeUserId,
|
|
1925
|
+
pendingVerification2.value,
|
|
1926
|
+
input.userContext
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
const pendingVerification = {
|
|
1930
|
+
id: input.pendingVerificationId,
|
|
1931
|
+
field: "email",
|
|
1932
|
+
value: input.email,
|
|
1933
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1934
|
+
};
|
|
1935
|
+
await import_usermetadata2.default.updateUserMetadata(input.userId, {
|
|
1936
|
+
...metadata,
|
|
1937
|
+
rownd_pending_verification: [
|
|
1938
|
+
...pendingVerifications.filter(
|
|
1939
|
+
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
2235
1940
|
),
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
1941
|
+
pendingVerification
|
|
1942
|
+
]
|
|
1943
|
+
});
|
|
1944
|
+
const response = await import_emailverification.default.sendEmailVerificationEmail(
|
|
1945
|
+
input.tenantId,
|
|
1946
|
+
input.userId,
|
|
1947
|
+
input.recipeUserId,
|
|
1948
|
+
input.email,
|
|
1949
|
+
{
|
|
1950
|
+
...input.userContext,
|
|
1951
|
+
rowndPendingVerificationId: pendingVerification.id
|
|
1952
|
+
}
|
|
1953
|
+
);
|
|
1954
|
+
if (response.status === "EMAIL_ALREADY_VERIFIED_ERROR") {
|
|
1955
|
+
await completePendingEmailVerification({
|
|
1956
|
+
recipeUserId: input.recipeUserId,
|
|
1957
|
+
email: input.email,
|
|
1958
|
+
userContext: input.userContext
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
return getUserById(input.userId);
|
|
1962
|
+
}
|
|
1963
|
+
async function completePendingEmailVerification(input) {
|
|
1964
|
+
const user = await import_supertokens_node2.default.getUser(
|
|
1965
|
+
input.recipeUserId.getAsString(),
|
|
1966
|
+
input.userContext
|
|
1967
|
+
);
|
|
1968
|
+
const userId = user?.id ?? input.recipeUserId.getAsString();
|
|
1969
|
+
const metadata = await getUserMetadata(userId);
|
|
1970
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1971
|
+
const pendingVerification = pendingVerifications.find(
|
|
1972
|
+
(pendingVerification2) => isMatchingPendingEmailVerification(
|
|
1973
|
+
pendingVerification2,
|
|
1974
|
+
input.email
|
|
1975
|
+
)
|
|
1976
|
+
);
|
|
1977
|
+
if (!pendingVerification) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
let metadataUserId = userId;
|
|
1981
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
|
|
1982
|
+
if (passwordlessEmailMethod) {
|
|
1983
|
+
const updateResult = await import_passwordless.default.updateUser({
|
|
1984
|
+
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
1985
|
+
email: input.email,
|
|
1986
|
+
userContext: input.userContext
|
|
1987
|
+
});
|
|
1988
|
+
if (updateResult.status !== "OK") {
|
|
1989
|
+
throw new Error(
|
|
1990
|
+
`Failed to update verified email method: ${updateResult.status}`
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
1994
|
+
const isPasswordlessSignUpAllowed = await import_accountlinking.default.isSignUpAllowed(
|
|
1995
|
+
PUBLIC_TENANT_ID,
|
|
1996
|
+
{
|
|
1997
|
+
recipeId: "passwordless",
|
|
1998
|
+
email: input.email
|
|
1999
|
+
},
|
|
2000
|
+
true,
|
|
2001
|
+
void 0,
|
|
2002
|
+
input.userContext
|
|
2003
|
+
);
|
|
2004
|
+
if (!isPasswordlessSignUpAllowed) {
|
|
2005
|
+
throw new Error("Passwordless sign up is not allowed for this email");
|
|
2006
|
+
}
|
|
2007
|
+
const passwordlessUser = await import_passwordless.default.signInUp({
|
|
2008
|
+
email: input.email,
|
|
2009
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
2010
|
+
userContext: input.userContext
|
|
2011
|
+
});
|
|
2012
|
+
const primaryUserResult = await import_accountlinking.default.createPrimaryUser(
|
|
2013
|
+
passwordlessUser.recipeUserId,
|
|
2014
|
+
input.userContext
|
|
2015
|
+
);
|
|
2016
|
+
const primaryUserId = primaryUserResult.status === "OK" ? primaryUserResult.user.id : primaryUserResult.status === "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" ? primaryUserResult.primaryUserId : passwordlessUser.user.id;
|
|
2017
|
+
if (userId !== primaryUserId) {
|
|
2018
|
+
const linkResult = await import_accountlinking.default.linkAccounts(
|
|
2019
|
+
input.recipeUserId,
|
|
2020
|
+
primaryUserId,
|
|
2021
|
+
input.userContext
|
|
2022
|
+
);
|
|
2023
|
+
if (linkResult.status !== "OK") {
|
|
2024
|
+
throw new Error(
|
|
2025
|
+
`Failed to link verified email method: ${linkResult.status}`
|
|
2026
|
+
);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
metadataUserId = primaryUserId;
|
|
2030
|
+
}
|
|
2031
|
+
const targetMetadata = metadataUserId === userId ? metadata : await getUserMetadata(metadataUserId);
|
|
2032
|
+
const originalRowndUser = targetMetadata.original_rownd_user ?? metadata.original_rownd_user;
|
|
2033
|
+
const targetPendingVerifications = getPendingVerifications(targetMetadata);
|
|
2034
|
+
const updatedMetadata = {
|
|
2035
|
+
...targetMetadata,
|
|
2036
|
+
...originalRowndUser ? {
|
|
2037
|
+
original_rownd_user: {
|
|
2038
|
+
...originalRowndUser,
|
|
2039
|
+
data: {
|
|
2040
|
+
...originalRowndUser.data,
|
|
2041
|
+
email: input.email
|
|
2246
2042
|
},
|
|
2247
|
-
|
|
2248
|
-
...
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2043
|
+
verified_data: {
|
|
2044
|
+
...originalRowndUser.verified_data,
|
|
2045
|
+
email: input.email
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
} : {},
|
|
2049
|
+
rownd_pending_verification: targetPendingVerifications.filter(
|
|
2050
|
+
(verification) => !isMatchingPendingEmailVerification(
|
|
2051
|
+
verification,
|
|
2052
|
+
input.email
|
|
2053
|
+
)
|
|
2054
|
+
)
|
|
2055
|
+
};
|
|
2056
|
+
await import_usermetadata2.default.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
2057
|
+
}
|
|
2058
|
+
function isMatchingPendingEmailVerification(verification, email) {
|
|
2059
|
+
return verification.field === "email" && verification.value === email;
|
|
2060
|
+
}
|
|
2061
|
+
async function updateUserMetadata(userId, inputMeta) {
|
|
2062
|
+
const metadata = await getUserMetadata(userId);
|
|
2063
|
+
const updatedMetadata = {
|
|
2064
|
+
...metadata,
|
|
2065
|
+
...inputMeta
|
|
2066
|
+
};
|
|
2067
|
+
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
2068
|
+
return {
|
|
2069
|
+
id: userId,
|
|
2070
|
+
meta: Object.fromEntries(
|
|
2071
|
+
Object.entries(updatedMetadata).filter(
|
|
2072
|
+
([key]) => !isInternalMetadataField(key)
|
|
2073
|
+
)
|
|
2074
|
+
)
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function getPasswordlessEmailLoginMethod(user) {
|
|
2078
|
+
return user?.loginMethods.find((method) => {
|
|
2079
|
+
return method.recipeId === "passwordless" && !!method.email;
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// src/pluginImplementation.ts
|
|
2084
|
+
var import_crypto = require("crypto");
|
|
2085
|
+
var import_supertokens_node3 = __toESM(require("supertokens-node"));
|
|
2086
|
+
var import_session2 = __toESM(require("supertokens-node/recipe/session"));
|
|
2087
|
+
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
2088
|
+
function handleGetAppConfig(deps) {
|
|
2089
|
+
return async (req) => {
|
|
2090
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2091
|
+
const appConfig = buildRowndAppConfig(
|
|
2092
|
+
deps.pluginConfig,
|
|
2093
|
+
deps.stConfig,
|
|
2094
|
+
appVariantId
|
|
2095
|
+
);
|
|
2096
|
+
if (!appConfig) {
|
|
2097
|
+
return {
|
|
2098
|
+
status: "ERROR",
|
|
2099
|
+
message: `Unknown Rownd app variant: ${appVariantId}`
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
return {
|
|
2103
|
+
status: "OK",
|
|
2104
|
+
...appConfig
|
|
2105
|
+
};
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
function handleGuestLogin(deps) {
|
|
2109
|
+
return async (req, res, _session, userContext) => {
|
|
2110
|
+
const startedAt = Date.now();
|
|
2111
|
+
const guestId = `guest_${(0, import_crypto.randomUUID)()}`;
|
|
2112
|
+
try {
|
|
2113
|
+
const body = parseGuestBody(await getJsonBody(req));
|
|
2114
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2115
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2116
|
+
const thirdPartyId = body.authLevel === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
2117
|
+
const thirdPartyUserId = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? `anon_${(0, import_crypto.randomUUID)()}` : guestId;
|
|
2118
|
+
const response = await import_thirdparty.default.manuallyCreateOrUpdateUser(
|
|
2119
|
+
PUBLIC_TENANT_ID,
|
|
2120
|
+
thirdPartyId,
|
|
2121
|
+
thirdPartyUserId,
|
|
2122
|
+
`${thirdPartyUserId}@anonymous.local`,
|
|
2123
|
+
false,
|
|
2124
|
+
void 0,
|
|
2125
|
+
userContext
|
|
2126
|
+
);
|
|
2127
|
+
if (response.status !== "OK") {
|
|
2128
|
+
throw new Error(
|
|
2129
|
+
`Guest user creation failed with status: ${response.status}`
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
await import_session2.default.createNewSession(
|
|
2133
|
+
req,
|
|
2134
|
+
res,
|
|
2135
|
+
PUBLIC_TENANT_ID,
|
|
2136
|
+
response.recipeUserId,
|
|
2137
|
+
{
|
|
2138
|
+
...buildRowndAudience({}, appVariantId),
|
|
2139
|
+
auth_level: GUEST_AUTH_METHOD_ID,
|
|
2140
|
+
is_anonymous: true,
|
|
2141
|
+
app_user_id: response.user.id
|
|
2142
|
+
},
|
|
2143
|
+
{},
|
|
2144
|
+
userContext
|
|
2145
|
+
);
|
|
2146
|
+
logDebugMessage(`Guest session created for user: ${response.user.id}`);
|
|
2147
|
+
deps.telemetryClient.recordSuccess({
|
|
2148
|
+
outcome: "success",
|
|
2149
|
+
durationMs: Date.now() - startedAt,
|
|
2150
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
2151
|
+
superTokensUserId: response.user.id
|
|
2152
|
+
});
|
|
2153
|
+
return {
|
|
2154
|
+
status: "OK",
|
|
2155
|
+
createdNewRecipeUser: response.createdNewRecipeUser
|
|
2156
|
+
};
|
|
2157
|
+
} catch (error) {
|
|
2158
|
+
logDebugMessage(`Guest login failed. Error: ${getErrorMessage(error)}`);
|
|
2159
|
+
deps.telemetryClient.recordError({
|
|
2160
|
+
error,
|
|
2161
|
+
startedAt,
|
|
2162
|
+
tenantId: PUBLIC_TENANT_ID
|
|
2163
|
+
});
|
|
2164
|
+
return {
|
|
2165
|
+
status: "ERROR",
|
|
2166
|
+
message: "Guest login failed"
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
function handleMigrate(deps) {
|
|
2172
|
+
return async (req, res, _session, userContext) => {
|
|
2173
|
+
const startedAt = Date.now();
|
|
2174
|
+
let tenantId = PUBLIC_TENANT_ID;
|
|
2175
|
+
let rowndUserId;
|
|
2176
|
+
let superTokensUserId;
|
|
2177
|
+
let user;
|
|
2178
|
+
let recipeUserId;
|
|
2179
|
+
try {
|
|
2180
|
+
if (!deps.stConfig.supertokens) {
|
|
2181
|
+
throw new Error("Supertokens config not found");
|
|
2182
|
+
}
|
|
2183
|
+
const parsed = await parseRequest(req);
|
|
2184
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2185
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2186
|
+
rowndUserId = await validateRowndToken(parsed.token);
|
|
2187
|
+
const rowndUser = await fetchOptionalRowndUserInfo(rowndUserId);
|
|
2188
|
+
if (!rowndUser) {
|
|
2189
|
+
logDebugMessage(
|
|
2190
|
+
`Skipping migration because user does not exist in Rownd. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2191
|
+
);
|
|
2192
|
+
return { status: "OK" };
|
|
2193
|
+
}
|
|
2194
|
+
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2195
|
+
if (!user) {
|
|
2196
|
+
const stUserImport = mapRowndUserToSuperTokens(rowndUser);
|
|
2197
|
+
try {
|
|
2198
|
+
const importedUser = await importUser(
|
|
2199
|
+
stUserImport,
|
|
2200
|
+
deps.stConfig.supertokens
|
|
2201
|
+
);
|
|
2202
|
+
superTokensUserId = importedUser.id;
|
|
2203
|
+
if (importedUser.loginMethods[0]?.recipeUserId) {
|
|
2204
|
+
recipeUserId = import_supertokens_node3.default.convertToRecipeUserId(
|
|
2205
|
+
importedUser.loginMethods[0].recipeUserId
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
} catch (err) {
|
|
2209
|
+
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2210
|
+
if (!user) {
|
|
2211
|
+
throw err;
|
|
2335
2212
|
}
|
|
2213
|
+
superTokensUserId = user.id;
|
|
2214
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2215
|
+
logDebugMessage(
|
|
2216
|
+
`User already migrated (race condition). tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2217
|
+
);
|
|
2336
2218
|
}
|
|
2219
|
+
logDebugMessage(
|
|
2220
|
+
`User migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2221
|
+
);
|
|
2222
|
+
} else {
|
|
2223
|
+
superTokensUserId = user.id;
|
|
2224
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2225
|
+
logDebugMessage(
|
|
2226
|
+
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2227
|
+
);
|
|
2228
|
+
}
|
|
2229
|
+
if (superTokensUserId) {
|
|
2230
|
+
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
2231
|
+
}
|
|
2232
|
+
if (!recipeUserId) {
|
|
2233
|
+
throw new Error("User not found or has no login methods");
|
|
2337
2234
|
}
|
|
2235
|
+
await import_session2.default.createNewSession(
|
|
2236
|
+
req,
|
|
2237
|
+
res,
|
|
2238
|
+
PUBLIC_TENANT_ID,
|
|
2239
|
+
recipeUserId,
|
|
2240
|
+
{
|
|
2241
|
+
...buildRowndAudience({}, appVariantId)
|
|
2242
|
+
},
|
|
2243
|
+
{},
|
|
2244
|
+
userContext
|
|
2245
|
+
);
|
|
2246
|
+
logDebugMessage(
|
|
2247
|
+
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
2248
|
+
);
|
|
2249
|
+
deps.telemetryClient.recordSuccess({
|
|
2250
|
+
outcome: "success",
|
|
2251
|
+
durationMs: Date.now() - startedAt,
|
|
2252
|
+
tenantId,
|
|
2253
|
+
rowndUserId,
|
|
2254
|
+
superTokensUserId
|
|
2255
|
+
});
|
|
2256
|
+
return { status: "OK" };
|
|
2257
|
+
} catch (error) {
|
|
2258
|
+
logDebugMessage(`Migration failed. Error: ${getErrorMessage(error)}`);
|
|
2259
|
+
deps.telemetryClient.recordError({
|
|
2260
|
+
error,
|
|
2261
|
+
startedAt,
|
|
2262
|
+
tenantId,
|
|
2263
|
+
rowndUserId,
|
|
2264
|
+
superTokensUserId
|
|
2265
|
+
});
|
|
2266
|
+
return {
|
|
2267
|
+
status: "ERROR",
|
|
2268
|
+
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
2269
|
+
};
|
|
2338
2270
|
}
|
|
2339
2271
|
};
|
|
2340
2272
|
}
|
|
2341
|
-
function
|
|
2342
|
-
return {
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
...authEmail?.customContent ? { custom_content: authEmail.customContent } : {},
|
|
2349
|
-
...authEmail?.customClosingContent ? { custom_closing_content: authEmail.customClosingContent } : {}
|
|
2273
|
+
function handleGetUser() {
|
|
2274
|
+
return async (_req, _res, session) => {
|
|
2275
|
+
const user = await getUserById(session.getUserId());
|
|
2276
|
+
return {
|
|
2277
|
+
status: "OK",
|
|
2278
|
+
...user
|
|
2279
|
+
};
|
|
2350
2280
|
};
|
|
2351
2281
|
}
|
|
2352
|
-
function
|
|
2282
|
+
function handleUpdateUser() {
|
|
2283
|
+
return async (req, _res, session, userContext) => {
|
|
2284
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2285
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2286
|
+
const payload = parseUpdateUserBody(await getJsonBody(req));
|
|
2287
|
+
const inputData = payload.data ?? {};
|
|
2288
|
+
const requestUserContext = {
|
|
2289
|
+
...userContext,
|
|
2290
|
+
...payload.context
|
|
2291
|
+
};
|
|
2292
|
+
const { email, ...dataWithoutEmail } = inputData;
|
|
2293
|
+
const hasEmailUpdate = hasOwn(inputData, "email") && typeof email === "string";
|
|
2294
|
+
const permissionError = validateWritableFields(
|
|
2295
|
+
Object.keys(dataWithoutEmail)
|
|
2296
|
+
);
|
|
2297
|
+
if (permissionError) {
|
|
2298
|
+
return permissionError;
|
|
2299
|
+
}
|
|
2300
|
+
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
2301
|
+
await updateUserData(session.getUserId(), dataWithoutEmail);
|
|
2302
|
+
}
|
|
2303
|
+
if (hasEmailUpdate) {
|
|
2304
|
+
const pendingVerificationResult = await startPendingEmailVerification({
|
|
2305
|
+
userId: session.getUserId(),
|
|
2306
|
+
recipeUserId: session.getRecipeUserId(),
|
|
2307
|
+
tenantId: session.getTenantId(),
|
|
2308
|
+
email,
|
|
2309
|
+
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
2310
|
+
userContext: appVariantId ? { ...requestUserContext, rowndAppVariantId: appVariantId } : requestUserContext
|
|
2311
|
+
});
|
|
2312
|
+
return {
|
|
2313
|
+
status: "OK",
|
|
2314
|
+
...pendingVerificationResult
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
const user = await getUserById(session.getUserId());
|
|
2318
|
+
return {
|
|
2319
|
+
status: "OK",
|
|
2320
|
+
...user
|
|
2321
|
+
};
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
function handleDeleteUser() {
|
|
2325
|
+
return async (_req, _res, session) => {
|
|
2326
|
+
await import_supertokens_node3.default.deleteUser(session.getUserId(), true);
|
|
2327
|
+
return { status: "OK" };
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
function handleSignOut() {
|
|
2331
|
+
return async (_req, _res, session, userContext) => {
|
|
2332
|
+
await import_session2.default.revokeAllSessionsForUser(
|
|
2333
|
+
session.getUserId(),
|
|
2334
|
+
true,
|
|
2335
|
+
session.getTenantId(),
|
|
2336
|
+
userContext
|
|
2337
|
+
);
|
|
2338
|
+
return { status: "OK" };
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
function handleGetUserMeta() {
|
|
2342
|
+
return async (_req, _res, session) => {
|
|
2343
|
+
const metadata = await getUserMetadata(session.getUserId());
|
|
2344
|
+
return {
|
|
2345
|
+
status: "OK",
|
|
2346
|
+
id: session.getUserId(),
|
|
2347
|
+
meta: Object.fromEntries(
|
|
2348
|
+
Object.entries(metadata).filter(
|
|
2349
|
+
([key]) => !isInternalMetadataField(key)
|
|
2350
|
+
)
|
|
2351
|
+
)
|
|
2352
|
+
};
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
function handleUpdateUserMeta() {
|
|
2356
|
+
return async (req, _res, session) => {
|
|
2357
|
+
const payload = parseUpdateMetaBody(await getJsonBody(req));
|
|
2358
|
+
const internalField = Object.keys(payload.meta ?? {}).find(
|
|
2359
|
+
isInternalMetadataField
|
|
2360
|
+
);
|
|
2361
|
+
if (internalField) {
|
|
2362
|
+
return {
|
|
2363
|
+
status: "ERROR",
|
|
2364
|
+
code: 403,
|
|
2365
|
+
message: `field is not writable: ${internalField}`
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
const updateMetadataResult = await updateUserMetadata(
|
|
2369
|
+
session.getUserId(),
|
|
2370
|
+
payload.meta ?? {}
|
|
2371
|
+
);
|
|
2372
|
+
return {
|
|
2373
|
+
status: "OK",
|
|
2374
|
+
...updateMetadataResult
|
|
2375
|
+
};
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
function handleGetUserField() {
|
|
2379
|
+
return async (req, _res, session) => {
|
|
2380
|
+
const field = req.getKeyValueFromQuery("field");
|
|
2381
|
+
if (!field) {
|
|
2382
|
+
return missingFieldResponse();
|
|
2383
|
+
}
|
|
2384
|
+
const user = await getUserById(session.getUserId());
|
|
2385
|
+
return {
|
|
2386
|
+
status: "OK",
|
|
2387
|
+
value: user.data[field]
|
|
2388
|
+
};
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
function handleUpdateUserField() {
|
|
2392
|
+
return async (req, _res, session, userContext) => {
|
|
2393
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2394
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2395
|
+
const field = req.getKeyValueFromQuery("field");
|
|
2396
|
+
if (!field) {
|
|
2397
|
+
return missingFieldResponse();
|
|
2398
|
+
}
|
|
2399
|
+
const payload = parseUpdateFieldBody(await getJsonBody(req));
|
|
2400
|
+
if (field === "email" && typeof payload.value === "string") {
|
|
2401
|
+
const pendingVerificationResult = await startPendingEmailVerification({
|
|
2402
|
+
userId: session.getUserId(),
|
|
2403
|
+
recipeUserId: session.getRecipeUserId(),
|
|
2404
|
+
tenantId: session.getTenantId(),
|
|
2405
|
+
email: payload.value,
|
|
2406
|
+
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
2407
|
+
userContext: appVariantId ? { ...userContext, rowndAppVariantId: appVariantId } : userContext
|
|
2408
|
+
});
|
|
2409
|
+
return {
|
|
2410
|
+
status: "OK",
|
|
2411
|
+
...pendingVerificationResult
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
2414
|
+
const permissionError = validateWritableFields([field]);
|
|
2415
|
+
if (permissionError) {
|
|
2416
|
+
return permissionError;
|
|
2417
|
+
}
|
|
2418
|
+
const updateUserDataResult = await updateUserData(session.getUserId(), {
|
|
2419
|
+
[field]: payload.value
|
|
2420
|
+
});
|
|
2421
|
+
return {
|
|
2422
|
+
status: "OK",
|
|
2423
|
+
...updateUserDataResult
|
|
2424
|
+
};
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function validateWritableFields(fields) {
|
|
2428
|
+
const readOnlyField = fields.find((field) => !canUpdateUserDataField(field));
|
|
2429
|
+
if (!readOnlyField) {
|
|
2430
|
+
return void 0;
|
|
2431
|
+
}
|
|
2353
2432
|
return {
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
...authMobile?.hyperlinkText ? { hyperlink_text: authMobile.hyperlinkText } : {},
|
|
2358
|
-
...authMobile?.hyperlinkRedirectUrl ? { hyperlink_redirect_url: authMobile.hyperlinkRedirectUrl } : {},
|
|
2359
|
-
...authMobile?.customContent ? { custom_content: authMobile.customContent } : {}
|
|
2433
|
+
status: "ERROR",
|
|
2434
|
+
code: 403,
|
|
2435
|
+
message: `field is not writable: ${readOnlyField}`
|
|
2360
2436
|
};
|
|
2361
2437
|
}
|
|
2362
2438
|
|
|
@@ -2373,6 +2449,9 @@ var init = createPluginInitFunction(
|
|
|
2373
2449
|
const appVariantId = input?.userContext?.rowndAppVariantId;
|
|
2374
2450
|
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2375
2451
|
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2452
|
+
const clientDomain = input?.userContext?.rowndClientDomain;
|
|
2453
|
+
const clientDomainKey = clientDomain ?? (displayContext === "mobile_app" ? "mobile" : "browser");
|
|
2454
|
+
const clientBaseUrl = pluginConfig2.clientDomains?.[clientDomainKey];
|
|
2376
2455
|
const bootstrapParams = {
|
|
2377
2456
|
appKey: pluginConfig2.rowndAppKey,
|
|
2378
2457
|
...hubBootstrapParams ?? {},
|
|
@@ -2380,10 +2459,10 @@ var init = createPluginInitFunction(
|
|
|
2380
2459
|
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2381
2460
|
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2382
2461
|
};
|
|
2383
|
-
const rewrittenLink = input[linkKey] ?
|
|
2462
|
+
const rewrittenLink = input[linkKey] ? clientBaseUrl ? rewriteLinkToBaseUrl(
|
|
2384
2463
|
input[linkKey],
|
|
2385
2464
|
targetPath,
|
|
2386
|
-
|
|
2465
|
+
clientBaseUrl,
|
|
2387
2466
|
bootstrapParams
|
|
2388
2467
|
) : rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey];
|
|
2389
2468
|
return {
|
|
@@ -2401,17 +2480,17 @@ var init = createPluginInitFunction(
|
|
|
2401
2480
|
id: PLUGIN_ID,
|
|
2402
2481
|
compatibleSDKVersions: PLUGIN_SDK_VERSION,
|
|
2403
2482
|
init: async () => {
|
|
2404
|
-
if (!
|
|
2483
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("session")) {
|
|
2405
2484
|
console.warn(
|
|
2406
2485
|
"RowndMigrationPlugin: Session recipe is not initialized. Session migration will fail."
|
|
2407
2486
|
);
|
|
2408
2487
|
}
|
|
2409
|
-
if (!
|
|
2488
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("thirdparty")) {
|
|
2410
2489
|
console.warn(
|
|
2411
2490
|
"RowndMigrationPlugin: ThirdParty recipe is not initialized. Guest login will fail."
|
|
2412
2491
|
);
|
|
2413
2492
|
}
|
|
2414
|
-
if (!
|
|
2493
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("emailverification")) {
|
|
2415
2494
|
console.warn(
|
|
2416
2495
|
"RowndMigrationPlugin: EmailVerification recipe is not initialized. Verified email profile updates will fail."
|
|
2417
2496
|
);
|
|
@@ -2524,13 +2603,13 @@ var init = createPluginInitFunction(
|
|
|
2524
2603
|
return {
|
|
2525
2604
|
...implementation,
|
|
2526
2605
|
sendEmail: async function(input) {
|
|
2527
|
-
return implementation.sendEmail(
|
|
2528
|
-
|
|
2606
|
+
return implementation.sendEmail(
|
|
2607
|
+
addHubBootstrapParams(
|
|
2529
2608
|
input,
|
|
2530
2609
|
"urlWithLinkCode",
|
|
2531
|
-
|
|
2610
|
+
HUB_LOGIN_PAGE_PATH
|
|
2532
2611
|
)
|
|
2533
|
-
|
|
2612
|
+
);
|
|
2534
2613
|
}
|
|
2535
2614
|
};
|
|
2536
2615
|
}
|
|
@@ -2545,13 +2624,13 @@ var init = createPluginInitFunction(
|
|
|
2545
2624
|
return {
|
|
2546
2625
|
...implementation,
|
|
2547
2626
|
sendSms: async function(input) {
|
|
2548
|
-
return implementation.sendSms(
|
|
2549
|
-
|
|
2627
|
+
return implementation.sendSms(
|
|
2628
|
+
addHubBootstrapParams(
|
|
2550
2629
|
input,
|
|
2551
2630
|
"urlWithLinkCode",
|
|
2552
|
-
|
|
2631
|
+
HUB_LOGIN_PAGE_PATH
|
|
2553
2632
|
)
|
|
2554
|
-
|
|
2633
|
+
);
|
|
2555
2634
|
}
|
|
2556
2635
|
};
|
|
2557
2636
|
}
|
|
@@ -2567,22 +2646,29 @@ var init = createPluginInitFunction(
|
|
|
2567
2646
|
const displayContext = getRequestedDisplayContextFromRequest(
|
|
2568
2647
|
input.options.req
|
|
2569
2648
|
);
|
|
2649
|
+
if (displayContext) {
|
|
2650
|
+
input.userContext.rowndDisplayContext = displayContext;
|
|
2651
|
+
}
|
|
2570
2652
|
const redirectToPath = getRequestedRedirectToPathFromRequest(
|
|
2571
2653
|
input.options.req
|
|
2572
2654
|
);
|
|
2655
|
+
if (redirectToPath) {
|
|
2656
|
+
input.userContext.rowndRedirectToPath = redirectToPath;
|
|
2657
|
+
}
|
|
2658
|
+
const clientDomain = getRequestedClientDomainFromRequest(
|
|
2659
|
+
input.options.req
|
|
2660
|
+
);
|
|
2661
|
+
if (clientDomain) {
|
|
2662
|
+
input.userContext.rowndClientDomain = clientDomain;
|
|
2663
|
+
}
|
|
2573
2664
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2574
2665
|
input.options.req
|
|
2575
2666
|
);
|
|
2667
|
+
if (appVariantId) {
|
|
2668
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2669
|
+
}
|
|
2576
2670
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2577
|
-
return originalImplementation.createCodePOST(
|
|
2578
|
-
...input,
|
|
2579
|
-
userContext: displayContext || redirectToPath || appVariantId ? {
|
|
2580
|
-
...input.userContext,
|
|
2581
|
-
...appVariantId ? { rowndAppVariantId: appVariantId } : {},
|
|
2582
|
-
...displayContext ? { rowndDisplayContext: displayContext } : {},
|
|
2583
|
-
...redirectToPath ? { rowndRedirectToPath: redirectToPath } : {}
|
|
2584
|
-
} : input.userContext
|
|
2585
|
-
});
|
|
2671
|
+
return originalImplementation.createCodePOST(input);
|
|
2586
2672
|
},
|
|
2587
2673
|
consumeCodePOST: async (input) => {
|
|
2588
2674
|
if (originalImplementation.consumeCodePOST === void 0) {
|
|
@@ -2593,11 +2679,11 @@ var init = createPluginInitFunction(
|
|
|
2593
2679
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2594
2680
|
input.options.req
|
|
2595
2681
|
);
|
|
2682
|
+
if (appVariantId) {
|
|
2683
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2684
|
+
}
|
|
2596
2685
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2597
|
-
const response = await originalImplementation.consumeCodePOST(
|
|
2598
|
-
...input,
|
|
2599
|
-
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2600
|
-
});
|
|
2686
|
+
const response = await originalImplementation.consumeCodePOST(input);
|
|
2601
2687
|
if (response.status === "OK") {
|
|
2602
2688
|
await recordRowndAppVariantForUser(
|
|
2603
2689
|
response.user.id,
|
|
@@ -2618,11 +2704,11 @@ var init = createPluginInitFunction(
|
|
|
2618
2704
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2619
2705
|
input.options.req
|
|
2620
2706
|
);
|
|
2707
|
+
if (appVariantId) {
|
|
2708
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2709
|
+
}
|
|
2621
2710
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2622
|
-
const response = await originalImplementation.signInUpPOST(
|
|
2623
|
-
...input,
|
|
2624
|
-
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2625
|
-
});
|
|
2711
|
+
const response = await originalImplementation.signInUpPOST(input);
|
|
2626
2712
|
if (response.status === "OK") {
|
|
2627
2713
|
await recordRowndAppVariantForUser(
|
|
2628
2714
|
response.user.id,
|
|
@@ -2660,19 +2746,23 @@ var init = createPluginInitFunction(
|
|
|
2660
2746
|
functions: (originalImplementation) => ({
|
|
2661
2747
|
...originalImplementation,
|
|
2662
2748
|
createNewSession: async (input) => {
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
...await buildRowndSessionClaims(
|
|
2749
|
+
const [rowndSessionClaims, rowndIsAnonymousClaim] = await Promise.all([
|
|
2750
|
+
buildRowndSessionClaims(
|
|
2666
2751
|
input.userId,
|
|
2667
2752
|
input.accessTokenPayload
|
|
2668
2753
|
),
|
|
2669
|
-
|
|
2754
|
+
RowndIsAnonymousClaim.build(
|
|
2670
2755
|
input.userId,
|
|
2671
2756
|
input.recipeUserId,
|
|
2672
2757
|
input.tenantId,
|
|
2673
2758
|
input.accessTokenPayload,
|
|
2674
2759
|
input.userContext
|
|
2675
2760
|
)
|
|
2761
|
+
]);
|
|
2762
|
+
input.accessTokenPayload = {
|
|
2763
|
+
...input.accessTokenPayload,
|
|
2764
|
+
...rowndSessionClaims,
|
|
2765
|
+
...rowndIsAnonymousClaim
|
|
2676
2766
|
};
|
|
2677
2767
|
return originalImplementation.createNewSession(input);
|
|
2678
2768
|
}
|
|
@@ -2698,7 +2788,7 @@ var init = createPluginInitFunction(
|
|
|
2698
2788
|
...addHubBootstrapParams(
|
|
2699
2789
|
input,
|
|
2700
2790
|
"emailVerifyLink",
|
|
2701
|
-
|
|
2791
|
+
HUB_VERIFY_EMAIL_PAGE_PATH
|
|
2702
2792
|
)
|
|
2703
2793
|
});
|
|
2704
2794
|
}
|
|
@@ -2751,21 +2841,14 @@ var init = createPluginInitFunction(
|
|
|
2751
2841
|
);
|
|
2752
2842
|
}
|
|
2753
2843
|
}
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
if (!/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(config2.mobileDeepLinkBaseUrl)) {
|
|
2757
|
-
throw new Error();
|
|
2758
|
-
}
|
|
2759
|
-
new URL(config2.mobileDeepLinkBaseUrl);
|
|
2760
|
-
} catch {
|
|
2761
|
-
throw new Error("Invalid mobileDeepLinkBaseUrl in plugin config");
|
|
2762
|
-
}
|
|
2844
|
+
for (const [key, value] of Object.entries(config2.clientDomains ?? {})) {
|
|
2845
|
+
validateClientDomainUrl(key, value);
|
|
2763
2846
|
}
|
|
2764
2847
|
return {
|
|
2765
2848
|
rowndAppKey: config2.rowndAppKey,
|
|
2766
2849
|
rowndAppSecret: config2.rowndAppSecret,
|
|
2767
2850
|
enableDebugLogs: config2.enableDebugLogs,
|
|
2768
|
-
|
|
2851
|
+
clientDomains: config2.clientDomains,
|
|
2769
2852
|
telemetry: config2.telemetry,
|
|
2770
2853
|
schema: config2.schema,
|
|
2771
2854
|
appConfig: config2.appConfig,
|
|
@@ -2773,110 +2856,15 @@ var init = createPluginInitFunction(
|
|
|
2773
2856
|
};
|
|
2774
2857
|
}
|
|
2775
2858
|
);
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
const baseApp = config2?.appConfig;
|
|
2781
|
-
const appVariantId = userContext?.rowndAppVariantId;
|
|
2782
|
-
if (typeof appVariantId === "string" && config2?.subBrands?.[appVariantId]) {
|
|
2783
|
-
return deepMerge(baseApp ?? {}, config2.subBrands[appVariantId]);
|
|
2784
|
-
}
|
|
2785
|
-
return baseApp;
|
|
2786
|
-
}
|
|
2787
|
-
function deepMerge(base, overlay) {
|
|
2788
|
-
const result = { ...base };
|
|
2789
|
-
for (const [key, value] of Object.entries(overlay)) {
|
|
2790
|
-
if (value === void 0) {
|
|
2791
|
-
continue;
|
|
2792
|
-
}
|
|
2793
|
-
const existingValue = result[key];
|
|
2794
|
-
if (isRecord2(existingValue) && isRecord2(value)) {
|
|
2795
|
-
result[key] = deepMerge(existingValue, value);
|
|
2796
|
-
} else {
|
|
2797
|
-
result[key] = value;
|
|
2859
|
+
function validateClientDomainUrl(key, value) {
|
|
2860
|
+
try {
|
|
2861
|
+
if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(value)) {
|
|
2862
|
+
throw new Error();
|
|
2798
2863
|
}
|
|
2864
|
+
new URL(value);
|
|
2865
|
+
} catch {
|
|
2866
|
+
throw new Error(`Invalid clientDomains.${key} in plugin config`);
|
|
2799
2867
|
}
|
|
2800
|
-
return result;
|
|
2801
|
-
}
|
|
2802
|
-
function isRecord2(value) {
|
|
2803
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2804
|
-
}
|
|
2805
|
-
function getEmailConfig(userContext) {
|
|
2806
|
-
return getActiveAppConfig(userContext)?.auth?.email;
|
|
2807
|
-
}
|
|
2808
|
-
function getEmailLink(input) {
|
|
2809
|
-
if (hasOwn2(input, "urlWithLinkCode")) {
|
|
2810
|
-
return input.urlWithLinkCode;
|
|
2811
|
-
}
|
|
2812
|
-
return hasOwn2(input, "emailVerifyLink") ? input.emailVerifyLink : void 0;
|
|
2813
|
-
}
|
|
2814
|
-
function getEmailAddress(input) {
|
|
2815
|
-
if (hasOwn2(input, "email")) {
|
|
2816
|
-
return input.email;
|
|
2817
|
-
}
|
|
2818
|
-
return input.user.email;
|
|
2819
|
-
}
|
|
2820
|
-
function hasOwn2(value, key) {
|
|
2821
|
-
return Object.prototype.hasOwnProperty.call(value, key);
|
|
2822
|
-
}
|
|
2823
|
-
function buildRowndEmailContent(input, originalContent, emailConfig) {
|
|
2824
|
-
if (!emailConfig) {
|
|
2825
|
-
return originalContent;
|
|
2826
|
-
}
|
|
2827
|
-
const appConfig = getActiveAppConfig(input.userContext) ?? {};
|
|
2828
|
-
const link = getEmailLink(input);
|
|
2829
|
-
const callToActionText = emailConfig.callToActionText ?? "Verify your email";
|
|
2830
|
-
if (!link) {
|
|
2831
|
-
return {
|
|
2832
|
-
...originalContent,
|
|
2833
|
-
subject: emailConfig.subject ?? originalContent.subject
|
|
2834
|
-
};
|
|
2835
|
-
}
|
|
2836
|
-
return {
|
|
2837
|
-
...originalContent,
|
|
2838
|
-
toEmail: getEmailAddress(input),
|
|
2839
|
-
subject: emailConfig.subject ?? originalContent.subject,
|
|
2840
|
-
isHtml: true,
|
|
2841
|
-
body: renderEmailHtml({
|
|
2842
|
-
appConfig,
|
|
2843
|
-
emailConfig,
|
|
2844
|
-
link,
|
|
2845
|
-
callToActionText,
|
|
2846
|
-
originalBody: originalContent.body
|
|
2847
|
-
})
|
|
2848
|
-
};
|
|
2849
|
-
}
|
|
2850
|
-
function renderEmailHtml(input) {
|
|
2851
|
-
const primaryColor = input.appConfig.branding?.primaryColor ?? DEFAULT_PRIMARY_COLOR;
|
|
2852
|
-
const image = input.emailConfig.image || input.appConfig.branding?.logo || input.appConfig.icon;
|
|
2853
|
-
const appName = input.appConfig.name ?? "Application";
|
|
2854
|
-
const content = input.emailConfig.customContent ?? input.originalBody;
|
|
2855
|
-
return `<!doctype html>
|
|
2856
|
-
<html>
|
|
2857
|
-
<body style="font-family: Arial, sans-serif; color: #1f2937; margin: 0; padding: 32px; background: #f9fafb;">
|
|
2858
|
-
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 560px; margin: 0 auto; background: #ffffff; border-radius: 16px; padding: 32px;">
|
|
2859
|
-
<tr>
|
|
2860
|
-
<td style="text-align: center;">
|
|
2861
|
-
${image ? `<img src="${escapeHtml(image)}" alt="${escapeHtml(appName)}" style="max-width: 96px; max-height: 96px; margin-bottom: 24px;" />` : ""}
|
|
2862
|
-
<div style="font-size: 18px; font-weight: 600; margin-bottom: 16px;">${escapeHtml(appName)}</div>
|
|
2863
|
-
<div style="font-size: 15px; line-height: 1.6; margin-bottom: 24px;">${content}</div>
|
|
2864
|
-
<a href="${escapeHtml(input.link)}" style="display: inline-block; background: ${escapeHtml(primaryColor)}; color: #ffffff; text-decoration: none; padding: 12px 20px; border-radius: 10px; font-weight: 600;">${escapeHtml(input.callToActionText)}</a>
|
|
2865
|
-
${input.emailConfig.customClosingContent ? `<div style="font-size: 14px; line-height: 1.6; margin-top: 24px;">${input.emailConfig.customClosingContent}</div>` : ""}
|
|
2866
|
-
</td>
|
|
2867
|
-
</tr>
|
|
2868
|
-
</table>
|
|
2869
|
-
</body>
|
|
2870
|
-
</html>`;
|
|
2871
|
-
}
|
|
2872
|
-
function escapeHtml(value) {
|
|
2873
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2874
|
-
}
|
|
2875
|
-
async function getRowndPasswordlessEmailContent(input, originalContent) {
|
|
2876
|
-
return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
|
|
2877
|
-
}
|
|
2878
|
-
async function getRowndEmailVerificationContent(input, originalContent) {
|
|
2879
|
-
return buildRowndEmailContent(input, originalContent, getEmailConfig(input.userContext));
|
|
2880
2868
|
}
|
|
2881
2869
|
|
|
2882
2870
|
// src/index.ts
|
|
@@ -2887,15 +2875,16 @@ var index_default = { init };
|
|
|
2887
2875
|
DEFAULT_ROWND_SCHEMA,
|
|
2888
2876
|
GUEST_AUTH_METHOD_ID,
|
|
2889
2877
|
HANDLE_BASE_PATH,
|
|
2878
|
+
HUB_LOGIN_PAGE_PATH,
|
|
2879
|
+
HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
2890
2880
|
PLUGIN_ID,
|
|
2891
2881
|
PLUGIN_SDK_VERSION,
|
|
2882
|
+
PLUGIN_VERSION,
|
|
2892
2883
|
PUBLIC_TENANT_ID,
|
|
2893
2884
|
ROWND_JWT_CLAIMS,
|
|
2894
2885
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
2895
2886
|
RowndPluginError,
|
|
2896
2887
|
getRowndClient,
|
|
2897
|
-
getRowndEmailVerificationContent,
|
|
2898
|
-
getRowndPasswordlessEmailContent,
|
|
2899
2888
|
init,
|
|
2900
2889
|
setRowndClient
|
|
2901
2890
|
});
|