@supertokens-plugins/rownd-nodejs 0.3.0-beta.1 → 0.3.0-beta.2
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 +1173 -1186
- package/dist/index.mjs +1446 -1460
- 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,404 +909,459 @@ 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
|
-
|
|
1121
|
-
|
|
932
|
+
function parseUpdateMetaBody(value) {
|
|
933
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.meta)) {
|
|
934
|
+
return {};
|
|
935
|
+
}
|
|
936
|
+
return { meta: value.meta };
|
|
937
|
+
}
|
|
938
|
+
function parseUpdateFieldBody(value) {
|
|
939
|
+
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
940
|
+
return {};
|
|
941
|
+
}
|
|
942
|
+
return { value: value.value };
|
|
943
|
+
}
|
|
944
|
+
function missingFieldResponse() {
|
|
945
|
+
return {
|
|
946
|
+
status: "ERROR",
|
|
947
|
+
code: 400,
|
|
948
|
+
message: "field is required"
|
|
1122
949
|
};
|
|
1123
950
|
}
|
|
1124
|
-
function
|
|
1125
|
-
return
|
|
1126
|
-
const session = requireSession(maybeSession);
|
|
1127
|
-
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1128
|
-
assertRowndAppVariantIsConfigured(appVariantId);
|
|
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
|
-
};
|
|
951
|
+
function isJsonRecord(value) {
|
|
952
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1164
953
|
}
|
|
1165
|
-
function
|
|
1166
|
-
return
|
|
1167
|
-
const session = requireSession(maybeSession);
|
|
1168
|
-
await import_supertokens_node.default.deleteUser(session.getUserId(), true);
|
|
1169
|
-
return { status: "OK" };
|
|
1170
|
-
};
|
|
954
|
+
function isRecord(value) {
|
|
955
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1171
956
|
}
|
|
1172
|
-
function
|
|
1173
|
-
return
|
|
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
|
-
};
|
|
957
|
+
function hasOwn(value, key) {
|
|
958
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
1183
959
|
}
|
|
1184
|
-
function
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
status: "OK",
|
|
1190
|
-
id: session.getUserId(),
|
|
1191
|
-
meta: Object.fromEntries(
|
|
1192
|
-
Object.entries(metadata).filter(
|
|
1193
|
-
([key]) => !isInternalMetadataField(key)
|
|
1194
|
-
)
|
|
1195
|
-
)
|
|
1196
|
-
};
|
|
1197
|
-
};
|
|
960
|
+
function getStringList(value) {
|
|
961
|
+
if (Array.isArray(value)) {
|
|
962
|
+
return value.filter((entry) => typeof entry === "string");
|
|
963
|
+
}
|
|
964
|
+
return typeof value === "string" ? [value] : [];
|
|
1198
965
|
}
|
|
1199
|
-
function
|
|
1200
|
-
return
|
|
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
|
-
};
|
|
966
|
+
function getErrorMessage(error) {
|
|
967
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
1218
968
|
}
|
|
1219
|
-
function
|
|
1220
|
-
return
|
|
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
|
-
};
|
|
969
|
+
function getRequestedAppVariantIdFromRequest(req) {
|
|
970
|
+
return req.getKeyValueFromQuery("app_variant_id");
|
|
1232
971
|
}
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
-
};
|
|
972
|
+
function getRequestedDisplayContextFromRequest(req) {
|
|
973
|
+
const displayContext = req.getKeyValueFromQuery("rownd_display_context");
|
|
974
|
+
return displayContext === "browser" || displayContext === "mobile_app" || displayContext === "customer_web_view" ? displayContext : void 0;
|
|
1267
975
|
}
|
|
1268
|
-
function
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
}
|
|
1272
|
-
return session;
|
|
976
|
+
function getRequestedClientDomainFromRequest(req) {
|
|
977
|
+
const clientDomain = req.getKeyValueFromQuery("rownd_client_domain");
|
|
978
|
+
return typeof clientDomain === "string" && clientDomain.length > 0 ? clientDomain : void 0;
|
|
1273
979
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
980
|
+
function getRequestedRedirectToPathFromRequest(req) {
|
|
981
|
+
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
982
|
+
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1276
983
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
authLevel: typeof value.auth_level === "string" ? value.auth_level : void 0
|
|
1283
|
-
};
|
|
984
|
+
|
|
985
|
+
// src/config.ts
|
|
986
|
+
var pluginConfig;
|
|
987
|
+
function setPluginConfig(config2) {
|
|
988
|
+
pluginConfig = config2;
|
|
1284
989
|
}
|
|
1285
|
-
function
|
|
1286
|
-
|
|
1287
|
-
return {};
|
|
1288
|
-
}
|
|
1289
|
-
return {
|
|
1290
|
-
data: value.data,
|
|
1291
|
-
...isJsonRecord(value.context) ? { context: value.context } : {}
|
|
1292
|
-
};
|
|
990
|
+
function getPluginConfig() {
|
|
991
|
+
return pluginConfig;
|
|
1293
992
|
}
|
|
1294
|
-
function
|
|
1295
|
-
if (!
|
|
1296
|
-
return
|
|
993
|
+
function assertRowndAppVariantIsConfigured(appVariantId) {
|
|
994
|
+
if (!appVariantId) {
|
|
995
|
+
return;
|
|
1297
996
|
}
|
|
1298
|
-
|
|
1299
|
-
}
|
|
1300
|
-
function parseUpdateFieldBody(value) {
|
|
1301
|
-
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
1302
|
-
return {};
|
|
997
|
+
if (pluginConfig?.subBrands && !pluginConfig.subBrands[appVariantId]) {
|
|
998
|
+
throw new Error(`Unknown Rownd app variant: ${appVariantId}`);
|
|
1303
999
|
}
|
|
1304
|
-
return { value: value.value };
|
|
1305
1000
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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";
|
|
1310
1014
|
}
|
|
1311
1015
|
return {
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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
|
|
1315
1022
|
};
|
|
1316
1023
|
}
|
|
1317
|
-
|
|
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
|
+
{}
|
|
1032
|
+
);
|
|
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);
|
|
1318
1051
|
return {
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
|
1322
1098
|
};
|
|
1323
1099
|
}
|
|
1324
|
-
function
|
|
1325
|
-
return
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
return Object.prototype.hasOwnProperty.call(value, key);
|
|
1100
|
+
function isInstantAnonymousMethod(methods) {
|
|
1101
|
+
return (methods ?? []).some(
|
|
1102
|
+
(method) => method.method === "anonymous" && getAnonymousType(method) === "instant"
|
|
1103
|
+
);
|
|
1329
1104
|
}
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1105
|
+
function getAnonymousType(method) {
|
|
1106
|
+
const type = getStringMethodProperty(method, "type");
|
|
1107
|
+
return type === "instant" ? "instant" : "guest";
|
|
1332
1108
|
}
|
|
1333
|
-
function
|
|
1109
|
+
function getSignInFasterWithGoogle(method) {
|
|
1110
|
+
const value = getStringMethodProperty(method, "signInFasterWithGoogle");
|
|
1111
|
+
return value === "enabled" || value === "disabled" ? value : void 0;
|
|
1112
|
+
}
|
|
1113
|
+
function getMethodProperty(method, property) {
|
|
1114
|
+
if (!method) {
|
|
1115
|
+
return void 0;
|
|
1116
|
+
}
|
|
1117
|
+
return method[property];
|
|
1118
|
+
}
|
|
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)) {
|
|
1130
|
+
return void 0;
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
browser: parseOneTapPlatform(oneTap.browser),
|
|
1134
|
+
mobileApp: parseOneTapPlatform(oneTap.mobileApp)
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
function parseOneTapPlatform(value) {
|
|
1138
|
+
if (!isRecord(value)) {
|
|
1139
|
+
return void 0;
|
|
1140
|
+
}
|
|
1141
|
+
return {
|
|
1142
|
+
autoPrompt: typeof value.autoPrompt === "boolean" ? value.autoPrompt : void 0,
|
|
1143
|
+
delay: typeof value.delay === "number" ? value.delay : void 0
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
function getSubBrandVariant(app) {
|
|
1147
|
+
if (isRecord(app) && isRecord(app.variant) && typeof app.variant.id === "string") {
|
|
1148
|
+
return app.variant;
|
|
1149
|
+
}
|
|
1150
|
+
return void 0;
|
|
1151
|
+
}
|
|
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;
|
|
1157
|
+
}
|
|
1158
|
+
const existing = result[key];
|
|
1159
|
+
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
1160
|
+
}
|
|
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;
|
|
1170
|
+
}
|
|
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
|
+
};
|
|
1182
|
+
}
|
|
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
|
+
};
|
|
1189
|
+
}
|
|
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
|
+
};
|
|
1196
|
+
}
|
|
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
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
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
|
+
])
|
|
1217
|
+
),
|
|
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 } : {}
|
|
1228
|
+
},
|
|
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
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function buildAuthEmailConfig(authEmail) {
|
|
1324
|
+
return {
|
|
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 } : {}
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
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
|
+
};
|
|
1343
|
+
}
|
|
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);
|
|
1360
|
+
}
|
|
1361
|
+
function isInternalMetadataField(field) {
|
|
1362
|
+
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1363
|
+
}
|
|
1364
|
+
function mapRowndUserToSuperTokens(rowndUser) {
|
|
1334
1365
|
const loginMethods = [];
|
|
1335
1366
|
const rowndUserData = rowndUser.data || {};
|
|
1336
1367
|
const rowndUserVerifiedData = rowndUser.verified_data || {};
|
|
@@ -1394,112 +1425,6 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
1394
1425
|
userMetadata
|
|
1395
1426
|
};
|
|
1396
1427
|
}
|
|
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
|
-
function getStringList(value) {
|
|
1454
|
-
if (Array.isArray(value)) {
|
|
1455
|
-
return value.filter((entry) => typeof entry === "string");
|
|
1456
|
-
}
|
|
1457
|
-
return typeof value === "string" ? [value] : [];
|
|
1458
|
-
}
|
|
1459
|
-
function getRequestedAppVariantIdFromRequest(req) {
|
|
1460
|
-
return req.getKeyValueFromQuery("app_variant_id");
|
|
1461
|
-
}
|
|
1462
|
-
function getRequestedDisplayContextFromRequest(req) {
|
|
1463
|
-
const displayContext = req.getKeyValueFromQuery("rownd_display_context");
|
|
1464
|
-
return displayContext === "browser" || displayContext === "mobile_app" || displayContext === "customer_web_view" ? displayContext : void 0;
|
|
1465
|
-
}
|
|
1466
|
-
function getRequestedRedirectToPathFromRequest(req) {
|
|
1467
|
-
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1468
|
-
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1469
|
-
}
|
|
1470
|
-
function assertRowndAppVariantIsConfigured(appVariantId) {
|
|
1471
|
-
if (!appVariantId) {
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
if (pluginConfig?.subBrands && !pluginConfig.subBrands[appVariantId]) {
|
|
1475
|
-
throw new Error(`Unknown Rownd app variant: ${appVariantId}`);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
1479
|
-
if (!appVariantId) {
|
|
1480
|
-
return;
|
|
1481
|
-
}
|
|
1482
|
-
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1483
|
-
const metadata = await getUserMetadata(userId);
|
|
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;
|
|
1489
|
-
}
|
|
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
|
-
}
|
|
1503
1428
|
function buildRowndUserMetadata(rowndUser) {
|
|
1504
1429
|
const metadata = {
|
|
1505
1430
|
...rowndUser.meta || {},
|
|
@@ -1512,48 +1437,22 @@ function buildRowndUserMetadata(rowndUser) {
|
|
|
1512
1437
|
}
|
|
1513
1438
|
return metadata;
|
|
1514
1439
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
|
|
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}`);
|
|
1521
1445
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
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
|
|
1532
|
-
);
|
|
1533
|
-
const appUserId = getRowndAppUserId(userId, user, currentPayload, metadata);
|
|
1534
|
-
const isAnonymous = authLevel === GUEST_AUTH_METHOD_ID;
|
|
1535
|
-
const anonymousId = getAnonymousId(userId, user, metadata);
|
|
1536
|
-
const isVerifiedUser = authLevel !== "unverified";
|
|
1537
|
-
const audience = buildRowndAudience(currentPayload, appVariantId);
|
|
1538
|
-
const configuredClaims = buildConfiguredSessionClaims(metadata);
|
|
1539
|
-
return {
|
|
1540
|
-
...audience,
|
|
1541
|
-
...configuredClaims,
|
|
1542
|
-
app_user_id: appUserId,
|
|
1543
|
-
auth_level: authLevel,
|
|
1544
|
-
is_verified_user: isVerifiedUser,
|
|
1545
|
-
[ROWND_JWT_CLAIMS.AppUserId]: appUserId,
|
|
1546
|
-
[ROWND_JWT_CLAIMS.AuthLevel]: authLevel,
|
|
1547
|
-
[ROWND_JWT_CLAIMS.IsVerifiedUser]: isVerifiedUser,
|
|
1548
|
-
[ROWND_JWT_CLAIMS.IsAnonymous]: isAnonymous,
|
|
1549
|
-
...anonymousId ? { anonymous_id: anonymousId } : {}
|
|
1550
|
-
};
|
|
1446
|
+
if (appVariantId) {
|
|
1447
|
+
audience.push(`app_variant:${appVariantId}`);
|
|
1448
|
+
}
|
|
1449
|
+
return audience.length > 0 ? { aud: [...new Set(audience)] } : {};
|
|
1551
1450
|
}
|
|
1552
1451
|
function buildConfiguredSessionClaims(metadata) {
|
|
1553
1452
|
if (!metadata) {
|
|
1554
1453
|
return {};
|
|
1555
1454
|
}
|
|
1556
|
-
const schema =
|
|
1455
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1557
1456
|
const claims = {};
|
|
1558
1457
|
for (const [key, field] of Object.entries(schema)) {
|
|
1559
1458
|
if (field.include_in_session_claims !== true) {
|
|
@@ -1576,17 +1475,6 @@ function getRowndAppUserId(userId, user, currentPayload, metadata) {
|
|
|
1576
1475
|
}
|
|
1577
1476
|
return user?.id || userId;
|
|
1578
1477
|
}
|
|
1579
|
-
function buildRowndAudience(currentPayload, appVariantId) {
|
|
1580
|
-
const audience = getStringList(currentPayload.aud);
|
|
1581
|
-
const appId = pluginConfig?.appConfig?.id;
|
|
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)] } : {};
|
|
1589
|
-
}
|
|
1590
1478
|
function getAnonymousId(userId, user, metadata) {
|
|
1591
1479
|
const originalAnonymousId = metadata?.original_rownd_user?.data?.anonymous_id;
|
|
1592
1480
|
if (typeof originalAnonymousId === "string") {
|
|
@@ -1598,6 +1486,39 @@ function getAnonymousId(userId, user, metadata) {
|
|
|
1598
1486
|
const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
|
|
1599
1487
|
return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
|
|
1600
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
|
+
}
|
|
1601
1522
|
async function shouldLinkRowndAccounts(input) {
|
|
1602
1523
|
const [newAccountInfo, , session] = input;
|
|
1603
1524
|
if (!session) {
|
|
@@ -1621,59 +1542,248 @@ async function shouldLinkRowndAccounts(input) {
|
|
|
1621
1542
|
}
|
|
1622
1543
|
return void 0;
|
|
1623
1544
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
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";
|
|
1632
1554
|
}
|
|
1633
|
-
return
|
|
1555
|
+
return "email";
|
|
1634
1556
|
}
|
|
1635
|
-
function
|
|
1636
|
-
return
|
|
1557
|
+
function getThirdPartyId(method) {
|
|
1558
|
+
return method.thirdParty?.id;
|
|
1637
1559
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
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;
|
|
1643
1585
|
}
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
if (!isIdentityField(key)) {
|
|
1653
|
-
data[key] = value;
|
|
1654
|
-
dataFieldKeys.add(key);
|
|
1655
|
-
}
|
|
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
|
+
});
|
|
1656
1594
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
data[key] = metadata[key];
|
|
1662
|
-
}
|
|
1595
|
+
if (accountInfo.phoneNumber) {
|
|
1596
|
+
return user.loginMethods.some((method) => {
|
|
1597
|
+
return !isGuestLoginMethod(method) && method.phoneNumber === accountInfo.phoneNumber;
|
|
1598
|
+
});
|
|
1663
1599
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
lastUsedAt = method.lastUsed;
|
|
1600
|
+
return false;
|
|
1601
|
+
}
|
|
1602
|
+
function hasVerifiedRealLoginMethod(user) {
|
|
1603
|
+
return !!user?.loginMethods.some((method) => {
|
|
1604
|
+
if (isGuestLoginMethod(method)) {
|
|
1605
|
+
return false;
|
|
1671
1606
|
}
|
|
1672
1607
|
if (method.recipeId === "passwordless") {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1608
|
+
return !!(method.email || method.phoneNumber);
|
|
1609
|
+
}
|
|
1610
|
+
if (method.recipeId === "thirdparty") {
|
|
1611
|
+
return !!getThirdPartyUserId(method) && method.verified === true;
|
|
1612
|
+
}
|
|
1613
|
+
if (method.recipeId === "emailpassword") {
|
|
1614
|
+
return !!method.email && method.verified === true;
|
|
1615
|
+
}
|
|
1616
|
+
return method.verified === true;
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
|
|
1620
|
+
if (hasVerifiedRealLoginMethod(user)) {
|
|
1621
|
+
return "verified";
|
|
1622
|
+
}
|
|
1623
|
+
return getGuestAuthLevel(user) || originalAuthLevel || (verifiedData && Object.keys(verifiedData).length > 0 ? "verified" : "unverified");
|
|
1624
|
+
}
|
|
1625
|
+
function canUpdateUserDataField(field) {
|
|
1626
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1627
|
+
const schemaField = schema[field];
|
|
1628
|
+
if (!schemaField) {
|
|
1629
|
+
return false;
|
|
1630
|
+
}
|
|
1631
|
+
const ownedBy = field === "google_id" || field === "apple_id" ? "app" : schemaField.owned_by || "user";
|
|
1632
|
+
return ownedBy !== "app" && schemaField.read_only !== true;
|
|
1633
|
+
}
|
|
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");
|
|
1643
|
+
}
|
|
1644
|
+
return rowndClient;
|
|
1645
|
+
}
|
|
1646
|
+
async function validateRowndToken(token) {
|
|
1647
|
+
const client = getRowndClient();
|
|
1648
|
+
const tokenInfo = await client.validateToken(token);
|
|
1649
|
+
return tokenInfo.user_id;
|
|
1650
|
+
}
|
|
1651
|
+
async function fetchRowndUserInfo(userId) {
|
|
1652
|
+
const client = getRowndClient();
|
|
1653
|
+
const rowndUser = await client.fetchUserInfo({ user_id: userId });
|
|
1654
|
+
if (!rowndUser) {
|
|
1655
|
+
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
1656
|
+
}
|
|
1657
|
+
return rowndUser;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// src/supertokens-repository.ts
|
|
1661
|
+
var import_supertokens_node2 = __toESM(require("supertokens-node"));
|
|
1662
|
+
var import_accountlinking = __toESM(require("supertokens-node/recipe/accountlinking"));
|
|
1663
|
+
var import_emailverification = __toESM(require("supertokens-node/recipe/emailverification"));
|
|
1664
|
+
var import_passwordless = __toESM(require("supertokens-node/recipe/passwordless"));
|
|
1665
|
+
var import_session = __toESM(require("supertokens-node/recipe/session"));
|
|
1666
|
+
var import_claims = require("supertokens-node/recipe/session/claims");
|
|
1667
|
+
var import_usermetadata2 = __toESM(require("supertokens-node/recipe/usermetadata"));
|
|
1668
|
+
async function importUser(stUser, config2) {
|
|
1669
|
+
const headers = {
|
|
1670
|
+
"Content-Type": "application/json"
|
|
1671
|
+
};
|
|
1672
|
+
if (config2.apiKey) {
|
|
1673
|
+
headers["api-key"] = config2.apiKey;
|
|
1674
|
+
}
|
|
1675
|
+
const response = await fetch(`${config2.connectionURI}/bulk-import/import`, {
|
|
1676
|
+
method: "POST",
|
|
1677
|
+
headers,
|
|
1678
|
+
body: JSON.stringify(stUser)
|
|
1679
|
+
});
|
|
1680
|
+
if (!response.ok) {
|
|
1681
|
+
const errorText = await response.text();
|
|
1682
|
+
throw new Error(
|
|
1683
|
+
`Bulk import failed with status ${response.status}: ${errorText}`
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
const importResponse = await response.json();
|
|
1687
|
+
if (importResponse.status !== "OK" || !importResponse.user) {
|
|
1688
|
+
throw new Error(
|
|
1689
|
+
`Bulk import failed: ${importResponse.message || "Missing user in response"}`
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
return importResponse.user;
|
|
1693
|
+
}
|
|
1694
|
+
async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
1695
|
+
if (!appVariantId) {
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1699
|
+
const metadata = await getUserMetadata(userId);
|
|
1700
|
+
const originalRowndUser = isJsonRecord(metadata.original_rownd_user) ? metadata.original_rownd_user : {};
|
|
1701
|
+
const attributes = isJsonRecord(originalRowndUser.attributes) ? originalRowndUser.attributes : {};
|
|
1702
|
+
const appVariants = getStringList(attributes["rownd:app_variants"]);
|
|
1703
|
+
if (appVariants.includes(appVariantId)) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
await import_usermetadata2.default.updateUserMetadata(userId, {
|
|
1707
|
+
...metadata,
|
|
1708
|
+
original_rownd_user: {
|
|
1709
|
+
...originalRowndUser,
|
|
1710
|
+
data: isJsonRecord(originalRowndUser.data) ? originalRowndUser.data : { user_id: userId },
|
|
1711
|
+
verified_data: isJsonRecord(originalRowndUser.verified_data) ? originalRowndUser.verified_data : {},
|
|
1712
|
+
attributes: {
|
|
1713
|
+
...attributes,
|
|
1714
|
+
"rownd:app_variants": [...appVariants, appVariantId]
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
var RowndIsAnonymousClaim = new import_claims.BooleanClaim({
|
|
1720
|
+
key: "is_anonymous",
|
|
1721
|
+
fetchValue: async (userId) => {
|
|
1722
|
+
const user = await import_supertokens_node2.default.getUser(userId);
|
|
1723
|
+
const effectiveAuthLevel = getEffectiveAuthLevel(user);
|
|
1724
|
+
return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
|
|
1728
|
+
const user = await import_supertokens_node2.default.getUser(userId);
|
|
1729
|
+
const metadata = user ? await getUserMetadata(user.id) : void 0;
|
|
1730
|
+
return buildRowndSessionClaimPayload({
|
|
1731
|
+
userId,
|
|
1732
|
+
user,
|
|
1733
|
+
metadata,
|
|
1734
|
+
currentPayload,
|
|
1735
|
+
appVariantId
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
async function getUserMetadata(userId) {
|
|
1739
|
+
const metadata = await import_usermetadata2.default.getUserMetadata(userId);
|
|
1740
|
+
return metadata.metadata || {};
|
|
1741
|
+
}
|
|
1742
|
+
function getPendingVerifications(metadata) {
|
|
1743
|
+
const pendingVerification = metadata.rownd_pending_verification;
|
|
1744
|
+
if (Array.isArray(pendingVerification)) {
|
|
1745
|
+
return pendingVerification.filter(isPendingVerification);
|
|
1746
|
+
}
|
|
1747
|
+
return [];
|
|
1748
|
+
}
|
|
1749
|
+
function isPendingVerification(value) {
|
|
1750
|
+
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
1751
|
+
}
|
|
1752
|
+
async function getUserById(userId) {
|
|
1753
|
+
const metadata = await getUserMetadata(userId);
|
|
1754
|
+
const stUser = await import_supertokens_node2.default.getUser(userId);
|
|
1755
|
+
if (!stUser) {
|
|
1756
|
+
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
1757
|
+
}
|
|
1758
|
+
const originalRowndUser = metadata.original_rownd_user;
|
|
1759
|
+
const rowndUser = originalRowndUser?.data?.user_id || userId;
|
|
1760
|
+
const state = originalRowndUser?.state || "enabled";
|
|
1761
|
+
const dataFieldKeys = /* @__PURE__ */ new Set();
|
|
1762
|
+
const data = {
|
|
1763
|
+
user_id: userId
|
|
1764
|
+
};
|
|
1765
|
+
for (const [key, value] of Object.entries(originalRowndUser?.data || {})) {
|
|
1766
|
+
if (!isIdentityField(key)) {
|
|
1767
|
+
data[key] = value;
|
|
1768
|
+
dataFieldKeys.add(key);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
const schema = getPluginConfig()?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1772
|
+
for (const key of Object.keys(schema)) {
|
|
1773
|
+
dataFieldKeys.add(key);
|
|
1774
|
+
if (!isInternalMetadataField(key) && !isIdentityField(key) && metadata[key] !== void 0) {
|
|
1775
|
+
data[key] = metadata[key];
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
const verifiedData = {
|
|
1779
|
+
...originalRowndUser?.verified_data || {}
|
|
1780
|
+
};
|
|
1781
|
+
for (const method of stUser.loginMethods) {
|
|
1782
|
+
if (method.recipeId === "passwordless") {
|
|
1783
|
+
if (method.email) {
|
|
1784
|
+
verifiedData.email = method.email;
|
|
1785
|
+
if (data.email === void 0) data.email = method.email;
|
|
1786
|
+
}
|
|
1677
1787
|
if (method.phoneNumber) {
|
|
1678
1788
|
verifiedData.phone_number = method.phoneNumber;
|
|
1679
1789
|
if (data.phone_number === void 0)
|
|
@@ -1722,26 +1832,16 @@ async function getUserById(userId) {
|
|
|
1722
1832
|
data[key] = "";
|
|
1723
1833
|
}
|
|
1724
1834
|
}
|
|
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
1835
|
const sortedByJoined = [...stUser.loginMethods].sort(
|
|
1738
1836
|
(a, b) => a.timeJoined - b.timeJoined
|
|
1739
1837
|
);
|
|
1740
|
-
const
|
|
1741
|
-
...stUser.loginMethods
|
|
1742
|
-
].sort((a, b) => (b.lastUsed || b.timeJoined) - (a.lastUsed || a.timeJoined));
|
|
1838
|
+
const latestSessionInfo = await getLatestSessionInfo(stUser.id);
|
|
1743
1839
|
const firstMethod = sortedByJoined[0];
|
|
1744
|
-
const
|
|
1840
|
+
const latestSessionRecipeUserId = latestSessionInfo?.recipeUserId.getAsString();
|
|
1841
|
+
const lastMethod = latestSessionRecipeUserId ? stUser.loginMethods.find(
|
|
1842
|
+
(method) => method.recipeUserId.getAsString() === latestSessionRecipeUserId
|
|
1843
|
+
) : [...stUser.loginMethods].sort((a, b) => b.timeJoined - a.timeJoined)[0];
|
|
1844
|
+
const lastSignInAt = latestSessionInfo?.timeCreated ?? stUser.timeJoined;
|
|
1745
1845
|
const metadataMeta = Object.fromEntries(
|
|
1746
1846
|
Object.entries(metadata).filter(
|
|
1747
1847
|
([key]) => !isInternalMetadataField(key) && !dataFieldKeys.has(key)
|
|
@@ -1751,8 +1851,8 @@ async function getUserById(userId) {
|
|
|
1751
1851
|
...metadataMeta,
|
|
1752
1852
|
created: new Date(stUser.timeJoined).toISOString(),
|
|
1753
1853
|
first_sign_in: new Date(stUser.timeJoined).toISOString(),
|
|
1754
|
-
last_sign_in: new Date(
|
|
1755
|
-
last_active: new Date(
|
|
1854
|
+
last_sign_in: new Date(lastSignInAt).toISOString(),
|
|
1855
|
+
last_active: new Date(lastSignInAt).toISOString(),
|
|
1756
1856
|
first_sign_in_method: firstMethod ? mapMethod(firstMethod) : "email",
|
|
1757
1857
|
last_sign_in_method: lastMethod ? mapMethod(lastMethod) : "email"
|
|
1758
1858
|
};
|
|
@@ -1768,6 +1868,25 @@ async function getUserById(userId) {
|
|
|
1768
1868
|
attributes: originalRowndUser?.attributes || {}
|
|
1769
1869
|
};
|
|
1770
1870
|
}
|
|
1871
|
+
async function getLatestSessionInfo(userId) {
|
|
1872
|
+
const sessionHandles = await import_session.default.getAllSessionHandlesForUser(
|
|
1873
|
+
userId,
|
|
1874
|
+
true,
|
|
1875
|
+
PUBLIC_TENANT_ID
|
|
1876
|
+
);
|
|
1877
|
+
const sessionInfos = await Promise.all(
|
|
1878
|
+
sessionHandles.map(
|
|
1879
|
+
(sessionHandle) => import_session.default.getSessionInformation(sessionHandle)
|
|
1880
|
+
)
|
|
1881
|
+
);
|
|
1882
|
+
let latestSessionInfo;
|
|
1883
|
+
for (const sessionInfo of sessionInfos) {
|
|
1884
|
+
if (sessionInfo && (!latestSessionInfo || sessionInfo.timeCreated > latestSessionInfo.timeCreated)) {
|
|
1885
|
+
latestSessionInfo = sessionInfo;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return latestSessionInfo;
|
|
1889
|
+
}
|
|
1771
1890
|
async function updateUserData(userId, inputData) {
|
|
1772
1891
|
const metadata = await getUserMetadata(userId);
|
|
1773
1892
|
const updatedMetadata = {
|
|
@@ -1780,13 +1899,29 @@ async function updateUserData(userId, inputData) {
|
|
|
1780
1899
|
async function startPendingEmailVerification(input) {
|
|
1781
1900
|
const metadata = await getUserMetadata(input.userId);
|
|
1782
1901
|
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
1783
|
-
if (currentEmail === input.email) {
|
|
1784
|
-
return getUserById(input.userId);
|
|
1785
|
-
}
|
|
1786
1902
|
const pendingVerifications = getPendingVerifications(metadata);
|
|
1787
1903
|
const pendingEmailVerifications = pendingVerifications.filter(
|
|
1788
1904
|
(pendingVerification2) => pendingVerification2.field === "email"
|
|
1789
1905
|
);
|
|
1906
|
+
if (currentEmail === input.email) {
|
|
1907
|
+
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1908
|
+
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1909
|
+
input.tenantId,
|
|
1910
|
+
input.recipeUserId,
|
|
1911
|
+
pendingVerification2.value,
|
|
1912
|
+
input.userContext
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
if (pendingEmailVerifications.length > 0) {
|
|
1916
|
+
await import_usermetadata2.default.updateUserMetadata(input.userId, {
|
|
1917
|
+
...metadata,
|
|
1918
|
+
rownd_pending_verification: pendingVerifications.filter(
|
|
1919
|
+
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
1920
|
+
)
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
return getUserById(input.userId);
|
|
1924
|
+
}
|
|
1790
1925
|
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1791
1926
|
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1792
1927
|
input.tenantId,
|
|
@@ -1830,7 +1965,7 @@ async function startPendingEmailVerification(input) {
|
|
|
1830
1965
|
return getUserById(input.userId);
|
|
1831
1966
|
}
|
|
1832
1967
|
async function completePendingEmailVerification(input) {
|
|
1833
|
-
const user = await
|
|
1968
|
+
const user = await import_supertokens_node2.default.getUser(
|
|
1834
1969
|
input.recipeUserId.getAsString(),
|
|
1835
1970
|
input.userContext
|
|
1836
1971
|
);
|
|
@@ -1838,7 +1973,10 @@ async function completePendingEmailVerification(input) {
|
|
|
1838
1973
|
const metadata = await getUserMetadata(userId);
|
|
1839
1974
|
const pendingVerifications = getPendingVerifications(metadata);
|
|
1840
1975
|
const pendingVerification = pendingVerifications.find(
|
|
1841
|
-
(pendingVerification2) =>
|
|
1976
|
+
(pendingVerification2) => isMatchingPendingEmailVerification(
|
|
1977
|
+
pendingVerification2,
|
|
1978
|
+
input.email
|
|
1979
|
+
)
|
|
1842
1980
|
);
|
|
1843
1981
|
if (!pendingVerification) {
|
|
1844
1982
|
return;
|
|
@@ -1894,27 +2032,36 @@ async function completePendingEmailVerification(input) {
|
|
|
1894
2032
|
}
|
|
1895
2033
|
metadataUserId = primaryUserId;
|
|
1896
2034
|
}
|
|
2035
|
+
const targetMetadata = metadataUserId === userId ? metadata : await getUserMetadata(metadataUserId);
|
|
2036
|
+
const originalRowndUser = targetMetadata.original_rownd_user ?? metadata.original_rownd_user;
|
|
2037
|
+
const targetPendingVerifications = getPendingVerifications(targetMetadata);
|
|
1897
2038
|
const updatedMetadata = {
|
|
1898
|
-
...
|
|
1899
|
-
...
|
|
2039
|
+
...targetMetadata,
|
|
2040
|
+
...originalRowndUser ? {
|
|
1900
2041
|
original_rownd_user: {
|
|
1901
|
-
...
|
|
2042
|
+
...originalRowndUser,
|
|
1902
2043
|
data: {
|
|
1903
|
-
...
|
|
2044
|
+
...originalRowndUser.data,
|
|
1904
2045
|
email: input.email
|
|
1905
2046
|
},
|
|
1906
2047
|
verified_data: {
|
|
1907
|
-
...
|
|
2048
|
+
...originalRowndUser.verified_data,
|
|
1908
2049
|
email: input.email
|
|
1909
2050
|
}
|
|
1910
2051
|
}
|
|
1911
2052
|
} : {},
|
|
1912
|
-
rownd_pending_verification:
|
|
1913
|
-
(verification) =>
|
|
2053
|
+
rownd_pending_verification: targetPendingVerifications.filter(
|
|
2054
|
+
(verification) => !isMatchingPendingEmailVerification(
|
|
2055
|
+
verification,
|
|
2056
|
+
input.email
|
|
2057
|
+
)
|
|
1914
2058
|
)
|
|
1915
2059
|
};
|
|
1916
2060
|
await import_usermetadata2.default.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
1917
2061
|
}
|
|
2062
|
+
function isMatchingPendingEmailVerification(verification, email) {
|
|
2063
|
+
return verification.field === "email" && verification.value === email;
|
|
2064
|
+
}
|
|
1918
2065
|
async function updateUserMetadata(userId, inputMeta) {
|
|
1919
2066
|
const metadata = await getUserMetadata(userId);
|
|
1920
2067
|
const updatedMetadata = {
|
|
@@ -1931,432 +2078,359 @@ async function updateUserMetadata(userId, inputMeta) {
|
|
|
1931
2078
|
)
|
|
1932
2079
|
};
|
|
1933
2080
|
}
|
|
1934
|
-
function getThirdPartyId(method) {
|
|
1935
|
-
return method.thirdPartyId || method.thirdParty?.id;
|
|
1936
|
-
}
|
|
1937
|
-
function getThirdPartyUserId(method) {
|
|
1938
|
-
return method.thirdPartyUserId || method.thirdParty?.userId;
|
|
1939
|
-
}
|
|
1940
|
-
function getGuestAuthLevel(user) {
|
|
1941
|
-
const guestMethod = user?.loginMethods.find(isGuestLoginMethod);
|
|
1942
|
-
return guestMethod ? GUEST_AUTH_METHOD_ID : void 0;
|
|
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
2081
|
function getPasswordlessEmailLoginMethod(user) {
|
|
1950
2082
|
return user?.loginMethods.find((method) => {
|
|
1951
2083
|
return method.recipeId === "passwordless" && !!method.email;
|
|
1952
2084
|
});
|
|
1953
2085
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2086
|
+
|
|
2087
|
+
// src/pluginImplementation.ts
|
|
2088
|
+
var import_crypto = require("crypto");
|
|
2089
|
+
var import_supertokens_node3 = __toESM(require("supertokens-node"));
|
|
2090
|
+
var import_session2 = __toESM(require("supertokens-node/recipe/session"));
|
|
2091
|
+
var import_thirdparty = __toESM(require("supertokens-node/recipe/thirdparty"));
|
|
2092
|
+
function handleGetAppConfig(deps) {
|
|
2093
|
+
return async (req) => {
|
|
2094
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2095
|
+
const appConfig = buildRowndAppConfig(
|
|
2096
|
+
deps.pluginConfig,
|
|
2097
|
+
deps.stConfig,
|
|
2098
|
+
appVariantId
|
|
2099
|
+
);
|
|
2100
|
+
if (!appConfig) {
|
|
2101
|
+
return {
|
|
2102
|
+
status: "ERROR",
|
|
2103
|
+
message: `Unknown Rownd app variant: ${appVariantId}`
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
return {
|
|
2107
|
+
status: "OK",
|
|
2108
|
+
...appConfig
|
|
2109
|
+
};
|
|
2110
|
+
};
|
|
1963
2111
|
}
|
|
1964
|
-
function
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
return !!getThirdPartyUserId(method) && method.verified === true;
|
|
1994
|
-
}
|
|
1995
|
-
if (method.recipeId === "emailpassword") {
|
|
1996
|
-
return !!method.email && method.verified === true;
|
|
1997
|
-
}
|
|
1998
|
-
return method.verified === true;
|
|
1999
|
-
});
|
|
2000
|
-
}
|
|
2001
|
-
function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
|
|
2002
|
-
if (hasVerifiedRealLoginMethod(user)) {
|
|
2003
|
-
return "verified";
|
|
2004
|
-
}
|
|
2005
|
-
return getGuestAuthLevel(user) || originalAuthLevel || (verifiedData && Object.keys(verifiedData).length > 0 ? "verified" : "unverified");
|
|
2006
|
-
}
|
|
2007
|
-
function canUpdateUserDataField(field) {
|
|
2008
|
-
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
2009
|
-
const schemaField = schema[field];
|
|
2010
|
-
if (!schemaField) {
|
|
2011
|
-
return false;
|
|
2012
|
-
}
|
|
2013
|
-
const ownedBy = field === "google_id" || field === "apple_id" ? "app" : schemaField.owned_by || "user";
|
|
2014
|
-
return ownedBy !== "app" && schemaField.read_only !== true;
|
|
2015
|
-
}
|
|
2016
|
-
var BUILTIN_SIGN_IN_METHOD_KEYS = [
|
|
2017
|
-
"email",
|
|
2018
|
-
"phone",
|
|
2019
|
-
"google",
|
|
2020
|
-
"apple",
|
|
2021
|
-
"anonymous"
|
|
2022
|
-
];
|
|
2023
|
-
function normalizeSchemaField(key, field) {
|
|
2024
|
-
let ownedBy = field.owned_by;
|
|
2025
|
-
if (key === "google_id" || key === "apple_id") {
|
|
2026
|
-
ownedBy = "app";
|
|
2027
|
-
} else if (!ownedBy) {
|
|
2028
|
-
ownedBy = "user";
|
|
2029
|
-
}
|
|
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
|
-
};
|
|
2038
|
-
}
|
|
2039
|
-
var DEFAULT_PRIMARY_COLOR = "#5b5bd6";
|
|
2040
|
-
function buildSignInMethodsConfig(methodsArray) {
|
|
2041
|
-
const methods = (methodsArray ?? []).reduce(
|
|
2042
|
-
(acc, curr) => {
|
|
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,
|
|
2112
|
+
function handleGuestLogin(deps) {
|
|
2113
|
+
return async (req, res, _session, userContext) => {
|
|
2114
|
+
const startedAt = Date.now();
|
|
2115
|
+
const guestId = `guest_${(0, import_crypto.randomUUID)()}`;
|
|
2116
|
+
try {
|
|
2117
|
+
const body = parseGuestBody(await getJsonBody(req));
|
|
2118
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2119
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2120
|
+
const thirdPartyId = body.authLevel === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
2121
|
+
const thirdPartyUserId = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? `anon_${(0, import_crypto.randomUUID)()}` : guestId;
|
|
2122
|
+
const response = await import_thirdparty.default.manuallyCreateOrUpdateUser(
|
|
2123
|
+
PUBLIC_TENANT_ID,
|
|
2124
|
+
thirdPartyId,
|
|
2125
|
+
thirdPartyUserId,
|
|
2126
|
+
`${thirdPartyUserId}@anonymous.local`,
|
|
2127
|
+
false,
|
|
2128
|
+
void 0,
|
|
2129
|
+
userContext
|
|
2130
|
+
);
|
|
2131
|
+
if (response.status !== "OK") {
|
|
2132
|
+
throw new Error(
|
|
2133
|
+
`Guest user creation failed with status: ${response.status}`
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
2136
|
+
await import_session2.default.createNewSession(
|
|
2137
|
+
req,
|
|
2138
|
+
res,
|
|
2139
|
+
PUBLIC_TENANT_ID,
|
|
2140
|
+
response.recipeUserId,
|
|
2052
2141
|
{
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
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
|
|
2142
|
+
...buildRowndAudience({}, appVariantId),
|
|
2143
|
+
auth_level: GUEST_AUTH_METHOD_ID,
|
|
2144
|
+
is_anonymous: true,
|
|
2145
|
+
app_user_id: response.user.id
|
|
2079
2146
|
},
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
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
|
-
);
|
|
2119
|
-
}
|
|
2120
|
-
function getAnonymousType(method) {
|
|
2121
|
-
const type = getStringMethodProperty(method, "type");
|
|
2122
|
-
return type === "instant" ? "instant" : "guest";
|
|
2123
|
-
}
|
|
2124
|
-
function getSignInFasterWithGoogle(method) {
|
|
2125
|
-
const value = getStringMethodProperty(method, "signInFasterWithGoogle");
|
|
2126
|
-
return value === "enabled" || value === "disabled" ? value : void 0;
|
|
2127
|
-
}
|
|
2128
|
-
function getMethodProperty(method, property) {
|
|
2129
|
-
if (!method) {
|
|
2130
|
-
return void 0;
|
|
2131
|
-
}
|
|
2132
|
-
return method[property];
|
|
2133
|
-
}
|
|
2134
|
-
function getStringMethodProperty(method, property) {
|
|
2135
|
-
const value = getMethodProperty(method, property);
|
|
2136
|
-
return typeof value === "string" ? value : void 0;
|
|
2137
|
-
}
|
|
2138
|
-
function getStringArrayMethodProperty(method, property) {
|
|
2139
|
-
const value = getMethodProperty(method, property);
|
|
2140
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
2141
|
-
}
|
|
2142
|
-
function getOneTapConfig(method) {
|
|
2143
|
-
const oneTap = getMethodProperty(method, "oneTap");
|
|
2144
|
-
if (!isRecord(oneTap)) {
|
|
2145
|
-
return void 0;
|
|
2146
|
-
}
|
|
2147
|
-
return {
|
|
2148
|
-
browser: parseOneTapPlatform(oneTap.browser),
|
|
2149
|
-
mobileApp: parseOneTapPlatform(oneTap.mobileApp)
|
|
2150
|
-
};
|
|
2151
|
-
}
|
|
2152
|
-
function parseOneTapPlatform(value) {
|
|
2153
|
-
if (!isRecord(value)) {
|
|
2154
|
-
return void 0;
|
|
2155
|
-
}
|
|
2156
|
-
return {
|
|
2157
|
-
autoPrompt: typeof value.autoPrompt === "boolean" ? value.autoPrompt : void 0,
|
|
2158
|
-
delay: typeof value.delay === "number" ? value.delay : void 0
|
|
2159
|
-
};
|
|
2160
|
-
}
|
|
2161
|
-
function isRecord(value) {
|
|
2162
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2163
|
-
}
|
|
2164
|
-
function getSubBrandVariant(app) {
|
|
2165
|
-
if (isRecord(app) && isRecord(app.variant) && typeof app.variant.id === "string") {
|
|
2166
|
-
return app.variant;
|
|
2167
|
-
}
|
|
2168
|
-
return void 0;
|
|
2169
|
-
}
|
|
2170
|
-
function mergeConfigInput(base, override) {
|
|
2171
|
-
const result = { ...base };
|
|
2172
|
-
for (const [key, value] of Object.entries(override)) {
|
|
2173
|
-
if (value === void 0) {
|
|
2174
|
-
continue;
|
|
2147
|
+
{},
|
|
2148
|
+
userContext
|
|
2149
|
+
);
|
|
2150
|
+
logDebugMessage(`Guest session created for user: ${response.user.id}`);
|
|
2151
|
+
deps.telemetryClient.recordSuccess({
|
|
2152
|
+
outcome: "success",
|
|
2153
|
+
durationMs: Date.now() - startedAt,
|
|
2154
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
2155
|
+
superTokensUserId: response.user.id
|
|
2156
|
+
});
|
|
2157
|
+
return {
|
|
2158
|
+
status: "OK",
|
|
2159
|
+
createdNewRecipeUser: response.createdNewRecipeUser
|
|
2160
|
+
};
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
logDebugMessage(`Guest login failed. Error: ${getErrorMessage(error)}`);
|
|
2163
|
+
deps.telemetryClient.recordError({
|
|
2164
|
+
error,
|
|
2165
|
+
startedAt,
|
|
2166
|
+
tenantId: PUBLIC_TENANT_ID
|
|
2167
|
+
});
|
|
2168
|
+
return {
|
|
2169
|
+
status: "ERROR",
|
|
2170
|
+
message: "Guest login failed"
|
|
2171
|
+
};
|
|
2175
2172
|
}
|
|
2176
|
-
|
|
2177
|
-
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
2178
|
-
}
|
|
2179
|
-
return result;
|
|
2173
|
+
};
|
|
2180
2174
|
}
|
|
2181
|
-
function
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
finalSchema.apple_id = {
|
|
2217
|
-
display_name: "Apple ID",
|
|
2218
|
-
type: "string",
|
|
2219
|
-
user_visible: false
|
|
2220
|
-
};
|
|
2221
|
-
}
|
|
2222
|
-
return {
|
|
2223
|
-
config_type: appVariantId ? "variant" : "app",
|
|
2224
|
-
...variant ? { variant } : {},
|
|
2225
|
-
app: {
|
|
2226
|
-
id: app.id ?? "",
|
|
2227
|
-
name: app.name ?? stConfig.appInfo.appName,
|
|
2228
|
-
icon: app.icon ?? "",
|
|
2229
|
-
...app.userVerificationFields ? { user_verification_fields: app.userVerificationFields } : {},
|
|
2230
|
-
schema: Object.fromEntries(
|
|
2231
|
-
Object.entries(finalSchema).map(([key, field]) => [
|
|
2232
|
-
key,
|
|
2233
|
-
normalizeSchemaField(key, field)
|
|
2234
|
-
])
|
|
2235
|
-
),
|
|
2236
|
-
config: {
|
|
2237
|
-
...app.capabilities ? { capabilities: app.capabilities } : {},
|
|
2238
|
-
...app.web ? { web: app.web } : {},
|
|
2239
|
-
...app.bottomSheet ? { bottom_sheet: app.bottomSheet } : {},
|
|
2240
|
-
...app.profileStorageVersion ? { profile_storage_version: app.profileStorageVersion } : {},
|
|
2241
|
-
customizations: {
|
|
2242
|
-
primary_color: branding.primaryColor ?? DEFAULT_PRIMARY_COLOR,
|
|
2243
|
-
...branding.logo ? { logo: branding.logo } : {},
|
|
2244
|
-
...branding.logoDarkMode ? { logo_dark_mode: branding.logoDarkMode } : {},
|
|
2245
|
-
...branding.animations ? { animations: branding.animations } : {}
|
|
2246
|
-
},
|
|
2247
|
-
hub: {
|
|
2248
|
-
...app.allowedWebOrigins ? { allowed_web_origins: app.allowedWebOrigins } : {},
|
|
2249
|
-
customizations: {
|
|
2250
|
-
rounded_corners: branding.roundedCorners ?? true,
|
|
2251
|
-
...branding.containerBorderRadius !== void 0 ? { container_border_radius: branding.containerBorderRadius } : {},
|
|
2252
|
-
...branding.placement !== void 0 ? { placement: branding.placement } : {},
|
|
2253
|
-
...branding.hubPrimaryColor !== void 0 ? { primary_color: branding.hubPrimaryColor } : {},
|
|
2254
|
-
...branding.primaryColorDarkMode !== void 0 ? { primary_color_dark_mode: branding.primaryColorDarkMode } : {},
|
|
2255
|
-
...branding.backgroundColor !== void 0 ? { background_color: branding.backgroundColor } : {},
|
|
2256
|
-
...branding.fontFamily !== void 0 ? { font_family: branding.fontFamily } : {},
|
|
2257
|
-
...branding.hideVerificationIcons !== void 0 ? { hide_verification_icons: branding.hideVerificationIcons } : {},
|
|
2258
|
-
visual_swoops: branding.visualSwoops ?? true,
|
|
2259
|
-
blur_background: branding.blurBackground ?? true,
|
|
2260
|
-
...branding.blurBackgroundOpacity !== void 0 ? { blur_background_opacity: branding.blurBackgroundOpacity } : {},
|
|
2261
|
-
...branding.offsetX !== void 0 ? { offset_x: branding.offsetX } : {},
|
|
2262
|
-
...branding.offsetY !== void 0 ? { offset_y: branding.offsetY } : {},
|
|
2263
|
-
...branding.propertyOverrides ? { property_overrides: branding.propertyOverrides } : {},
|
|
2264
|
-
dark_mode: branding.darkMode ?? "auto"
|
|
2265
|
-
},
|
|
2266
|
-
...branding.customScripts ? { custom_scripts: branding.customScripts } : {},
|
|
2267
|
-
...branding.customStyles ? { custom_styles: branding.customStyles } : {},
|
|
2268
|
-
auth: {
|
|
2269
|
-
email: buildAuthEmailConfig(auth.email),
|
|
2270
|
-
...auth.mobile ? { mobile: buildAuthMobileConfig(auth.mobile) } : {},
|
|
2271
|
-
sign_in_methods: signInMethods,
|
|
2272
|
-
additional_fields: auth.additionalFields ?? [],
|
|
2273
|
-
...auth.rememberSignInMethod !== void 0 ? { remember_sign_in_method: auth.rememberSignInMethod } : {},
|
|
2274
|
-
...auth.useExplicitSignUpFlow !== void 0 ? { use_explicit_sign_up_flow: auth.useExplicitSignUpFlow } : {},
|
|
2275
|
-
...auth.allowUnverifiedUsers !== void 0 ? { allow_unverified_users: auth.allowUnverifiedUsers } : {},
|
|
2276
|
-
...auth.primarySignUpMethod ? { primary_sign_up_method: auth.primarySignUpMethod } : {},
|
|
2277
|
-
...auth.preferredMethod ? { preferred_method: auth.preferredMethod } : {},
|
|
2278
|
-
...auth.order ? { order: auth.order } : {},
|
|
2279
|
-
...isInstantAnonymousMethod(app.signInMethods) ? { instant_user: { enabled: true } } : {},
|
|
2280
|
-
show_app_icon: branding.showAppIcon ?? false
|
|
2281
|
-
},
|
|
2282
|
-
legal: {
|
|
2283
|
-
...app.legal?.companyName ? { company_name: app.legal.companyName } : {},
|
|
2284
|
-
...app.legal?.privacyPolicyUrl ? { privacy_policy_url: app.legal.privacyPolicyUrl } : {},
|
|
2285
|
-
...app.legal?.termsConditionsUrl ? { terms_conditions_url: app.legal.termsConditionsUrl } : {},
|
|
2286
|
-
...app.legal?.supportEmail ? { support_email: app.legal.supportEmail } : {}
|
|
2287
|
-
},
|
|
2288
|
-
custom_content: {
|
|
2289
|
-
...app.customContent?.signInModal ? {
|
|
2290
|
-
sign_in_modal: {
|
|
2291
|
-
...app.customContent.signInModal.title ? { title: app.customContent.signInModal.title } : {},
|
|
2292
|
-
...app.customContent.signInModal.subtitle ? { subtitle: app.customContent.signInModal.subtitle } : {},
|
|
2293
|
-
...app.customContent.signInModal.signInTitle ? {
|
|
2294
|
-
sign_in_title: app.customContent.signInModal.signInTitle
|
|
2295
|
-
} : {},
|
|
2296
|
-
...app.customContent.signInModal.signUpTitle ? {
|
|
2297
|
-
sign_up_title: app.customContent.signInModal.signUpTitle
|
|
2298
|
-
} : {},
|
|
2299
|
-
...app.customContent.signInModal.signInSubtitle ? {
|
|
2300
|
-
sign_in_subtitle: app.customContent.signInModal.signInSubtitle
|
|
2301
|
-
} : {},
|
|
2302
|
-
...app.customContent.signInModal.signUpSubtitle ? {
|
|
2303
|
-
sign_up_subtitle: app.customContent.signInModal.signUpSubtitle
|
|
2304
|
-
} : {},
|
|
2305
|
-
...app.customContent.signInModal.signInButton ? {
|
|
2306
|
-
sign_in_button: app.customContent.signInModal.signInButton
|
|
2307
|
-
} : {},
|
|
2308
|
-
...app.customContent.signInModal.signUpButton ? {
|
|
2309
|
-
sign_up_button: app.customContent.signInModal.signUpButton
|
|
2310
|
-
} : {}
|
|
2311
|
-
}
|
|
2312
|
-
} : {},
|
|
2313
|
-
...app.customContent?.profileModal ? { profile_modal: app.customContent.profileModal } : {},
|
|
2314
|
-
...app.customContent?.verificationModal ? {
|
|
2315
|
-
verification_modal: {
|
|
2316
|
-
...app.customContent.verificationModal.title ? { title: app.customContent.verificationModal.title } : {},
|
|
2317
|
-
...app.customContent.verificationModal.subtitle ? { subtitle: app.customContent.verificationModal.subtitle } : {}
|
|
2318
|
-
}
|
|
2319
|
-
} : {},
|
|
2320
|
-
...app.customContent?.signInFailureModal ? {
|
|
2321
|
-
sign_in_failure_modal: {
|
|
2322
|
-
failure_message: app.customContent.signInFailureModal.failureMessage
|
|
2323
|
-
}
|
|
2324
|
-
} : {},
|
|
2325
|
-
...app.customContent?.noAccountMessage ? { no_account_message: app.customContent.noAccountMessage } : {},
|
|
2326
|
-
...app.customContent?.mobile ? { mobile: app.customContent.mobile } : {}
|
|
2327
|
-
},
|
|
2328
|
-
profile: {
|
|
2329
|
-
...app.profile?.accountInformation ? { account_information: app.profile.accountInformation } : {},
|
|
2330
|
-
...app.profile?.personalInformation ? { personal_information: app.profile.personalInformation } : {},
|
|
2331
|
-
...app.profile?.preferences ? { preferences: app.profile.preferences } : {},
|
|
2332
|
-
...app.profile?.signOutButton ? { sign_out_button: app.profile.signOutButton } : {},
|
|
2333
|
-
...app.profile?.deleteAccountButton ? { delete_account_button: app.profile.deleteAccountButton } : {},
|
|
2334
|
-
...app.profile?.addSignInMethodsButton ? { add_sign_in_methods_button: app.profile.addSignInMethodsButton } : {}
|
|
2175
|
+
function handleMigrate(deps) {
|
|
2176
|
+
return async (req, res, _session, userContext) => {
|
|
2177
|
+
const startedAt = Date.now();
|
|
2178
|
+
let tenantId = PUBLIC_TENANT_ID;
|
|
2179
|
+
let rowndUserId;
|
|
2180
|
+
let superTokensUserId;
|
|
2181
|
+
let user;
|
|
2182
|
+
let recipeUserId;
|
|
2183
|
+
try {
|
|
2184
|
+
if (!deps.stConfig.supertokens) {
|
|
2185
|
+
throw new Error("Supertokens config not found");
|
|
2186
|
+
}
|
|
2187
|
+
const parsed = await parseRequest(req);
|
|
2188
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2189
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2190
|
+
rowndUserId = await validateRowndToken(parsed.token);
|
|
2191
|
+
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2192
|
+
if (!user) {
|
|
2193
|
+
const rowndUser = await fetchRowndUserInfo(rowndUserId);
|
|
2194
|
+
const stUserImport = mapRowndUserToSuperTokens(rowndUser);
|
|
2195
|
+
try {
|
|
2196
|
+
const importedUser = await importUser(
|
|
2197
|
+
stUserImport,
|
|
2198
|
+
deps.stConfig.supertokens
|
|
2199
|
+
);
|
|
2200
|
+
superTokensUserId = importedUser.id;
|
|
2201
|
+
if (importedUser.loginMethods[0]?.recipeUserId) {
|
|
2202
|
+
recipeUserId = import_supertokens_node3.default.convertToRecipeUserId(
|
|
2203
|
+
importedUser.loginMethods[0].recipeUserId
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
user = await import_supertokens_node3.default.getUser(rowndUserId, userContext);
|
|
2208
|
+
if (!user) {
|
|
2209
|
+
throw err;
|
|
2335
2210
|
}
|
|
2211
|
+
superTokensUserId = user.id;
|
|
2212
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2213
|
+
logDebugMessage(
|
|
2214
|
+
`User already migrated (race condition). tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2215
|
+
);
|
|
2336
2216
|
}
|
|
2217
|
+
logDebugMessage(
|
|
2218
|
+
`User migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2219
|
+
);
|
|
2220
|
+
} else {
|
|
2221
|
+
superTokensUserId = user.id;
|
|
2222
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
2223
|
+
logDebugMessage(
|
|
2224
|
+
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
if (superTokensUserId) {
|
|
2228
|
+
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
2229
|
+
}
|
|
2230
|
+
if (!recipeUserId) {
|
|
2231
|
+
throw new Error("User not found or has no login methods");
|
|
2337
2232
|
}
|
|
2233
|
+
await import_session2.default.createNewSession(
|
|
2234
|
+
req,
|
|
2235
|
+
res,
|
|
2236
|
+
PUBLIC_TENANT_ID,
|
|
2237
|
+
recipeUserId,
|
|
2238
|
+
{
|
|
2239
|
+
...buildRowndAudience({}, appVariantId)
|
|
2240
|
+
},
|
|
2241
|
+
{},
|
|
2242
|
+
userContext
|
|
2243
|
+
);
|
|
2244
|
+
logDebugMessage(
|
|
2245
|
+
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
2246
|
+
);
|
|
2247
|
+
deps.telemetryClient.recordSuccess({
|
|
2248
|
+
outcome: "success",
|
|
2249
|
+
durationMs: Date.now() - startedAt,
|
|
2250
|
+
tenantId,
|
|
2251
|
+
rowndUserId,
|
|
2252
|
+
superTokensUserId
|
|
2253
|
+
});
|
|
2254
|
+
return { status: "OK" };
|
|
2255
|
+
} catch (error) {
|
|
2256
|
+
logDebugMessage(`Migration failed. Error: ${getErrorMessage(error)}`);
|
|
2257
|
+
deps.telemetryClient.recordError({
|
|
2258
|
+
error,
|
|
2259
|
+
startedAt,
|
|
2260
|
+
tenantId,
|
|
2261
|
+
rowndUserId,
|
|
2262
|
+
superTokensUserId
|
|
2263
|
+
});
|
|
2264
|
+
return {
|
|
2265
|
+
status: "ERROR",
|
|
2266
|
+
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
2267
|
+
};
|
|
2338
2268
|
}
|
|
2339
2269
|
};
|
|
2340
2270
|
}
|
|
2341
|
-
function
|
|
2342
|
-
return {
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
...authEmail?.customContent ? { custom_content: authEmail.customContent } : {},
|
|
2349
|
-
...authEmail?.customClosingContent ? { custom_closing_content: authEmail.customClosingContent } : {}
|
|
2271
|
+
function handleGetUser() {
|
|
2272
|
+
return async (_req, _res, session) => {
|
|
2273
|
+
const user = await getUserById(session.getUserId());
|
|
2274
|
+
return {
|
|
2275
|
+
status: "OK",
|
|
2276
|
+
...user
|
|
2277
|
+
};
|
|
2350
2278
|
};
|
|
2351
2279
|
}
|
|
2352
|
-
function
|
|
2280
|
+
function handleUpdateUser() {
|
|
2281
|
+
return async (req, _res, session, userContext) => {
|
|
2282
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2283
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2284
|
+
const payload = parseUpdateUserBody(await getJsonBody(req));
|
|
2285
|
+
const inputData = payload.data ?? {};
|
|
2286
|
+
const requestUserContext = {
|
|
2287
|
+
...userContext,
|
|
2288
|
+
...payload.context
|
|
2289
|
+
};
|
|
2290
|
+
const { email, ...dataWithoutEmail } = inputData;
|
|
2291
|
+
const hasEmailUpdate = hasOwn(inputData, "email") && typeof email === "string";
|
|
2292
|
+
const permissionError = validateWritableFields(
|
|
2293
|
+
Object.keys(dataWithoutEmail)
|
|
2294
|
+
);
|
|
2295
|
+
if (permissionError) {
|
|
2296
|
+
return permissionError;
|
|
2297
|
+
}
|
|
2298
|
+
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
2299
|
+
await updateUserData(session.getUserId(), dataWithoutEmail);
|
|
2300
|
+
}
|
|
2301
|
+
if (hasEmailUpdate) {
|
|
2302
|
+
const pendingVerificationResult = await startPendingEmailVerification({
|
|
2303
|
+
userId: session.getUserId(),
|
|
2304
|
+
recipeUserId: session.getRecipeUserId(),
|
|
2305
|
+
tenantId: session.getTenantId(),
|
|
2306
|
+
email,
|
|
2307
|
+
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
2308
|
+
userContext: appVariantId ? { ...requestUserContext, rowndAppVariantId: appVariantId } : requestUserContext
|
|
2309
|
+
});
|
|
2310
|
+
return {
|
|
2311
|
+
status: "OK",
|
|
2312
|
+
...pendingVerificationResult
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
const user = await getUserById(session.getUserId());
|
|
2316
|
+
return {
|
|
2317
|
+
status: "OK",
|
|
2318
|
+
...user
|
|
2319
|
+
};
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
function handleDeleteUser() {
|
|
2323
|
+
return async (_req, _res, session) => {
|
|
2324
|
+
await import_supertokens_node3.default.deleteUser(session.getUserId(), true);
|
|
2325
|
+
return { status: "OK" };
|
|
2326
|
+
};
|
|
2327
|
+
}
|
|
2328
|
+
function handleSignOut() {
|
|
2329
|
+
return async (_req, _res, session, userContext) => {
|
|
2330
|
+
await import_session2.default.revokeAllSessionsForUser(
|
|
2331
|
+
session.getUserId(),
|
|
2332
|
+
true,
|
|
2333
|
+
session.getTenantId(),
|
|
2334
|
+
userContext
|
|
2335
|
+
);
|
|
2336
|
+
return { status: "OK" };
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
function handleGetUserMeta() {
|
|
2340
|
+
return async (_req, _res, session) => {
|
|
2341
|
+
const metadata = await getUserMetadata(session.getUserId());
|
|
2342
|
+
return {
|
|
2343
|
+
status: "OK",
|
|
2344
|
+
id: session.getUserId(),
|
|
2345
|
+
meta: Object.fromEntries(
|
|
2346
|
+
Object.entries(metadata).filter(
|
|
2347
|
+
([key]) => !isInternalMetadataField(key)
|
|
2348
|
+
)
|
|
2349
|
+
)
|
|
2350
|
+
};
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
function handleUpdateUserMeta() {
|
|
2354
|
+
return async (req, _res, session) => {
|
|
2355
|
+
const payload = parseUpdateMetaBody(await getJsonBody(req));
|
|
2356
|
+
const internalField = Object.keys(payload.meta ?? {}).find(
|
|
2357
|
+
isInternalMetadataField
|
|
2358
|
+
);
|
|
2359
|
+
if (internalField) {
|
|
2360
|
+
return {
|
|
2361
|
+
status: "ERROR",
|
|
2362
|
+
code: 403,
|
|
2363
|
+
message: `field is not writable: ${internalField}`
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
const updateMetadataResult = await updateUserMetadata(
|
|
2367
|
+
session.getUserId(),
|
|
2368
|
+
payload.meta ?? {}
|
|
2369
|
+
);
|
|
2370
|
+
return {
|
|
2371
|
+
status: "OK",
|
|
2372
|
+
...updateMetadataResult
|
|
2373
|
+
};
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
function handleGetUserField() {
|
|
2377
|
+
return async (req, _res, session) => {
|
|
2378
|
+
const field = req.getKeyValueFromQuery("field");
|
|
2379
|
+
if (!field) {
|
|
2380
|
+
return missingFieldResponse();
|
|
2381
|
+
}
|
|
2382
|
+
const user = await getUserById(session.getUserId());
|
|
2383
|
+
return {
|
|
2384
|
+
status: "OK",
|
|
2385
|
+
value: user.data[field]
|
|
2386
|
+
};
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
function handleUpdateUserField() {
|
|
2390
|
+
return async (req, _res, session, userContext) => {
|
|
2391
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
2392
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2393
|
+
const field = req.getKeyValueFromQuery("field");
|
|
2394
|
+
if (!field) {
|
|
2395
|
+
return missingFieldResponse();
|
|
2396
|
+
}
|
|
2397
|
+
const payload = parseUpdateFieldBody(await getJsonBody(req));
|
|
2398
|
+
if (field === "email" && typeof payload.value === "string") {
|
|
2399
|
+
const pendingVerificationResult = await startPendingEmailVerification({
|
|
2400
|
+
userId: session.getUserId(),
|
|
2401
|
+
recipeUserId: session.getRecipeUserId(),
|
|
2402
|
+
tenantId: session.getTenantId(),
|
|
2403
|
+
email: payload.value,
|
|
2404
|
+
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
2405
|
+
userContext: appVariantId ? { ...userContext, rowndAppVariantId: appVariantId } : userContext
|
|
2406
|
+
});
|
|
2407
|
+
return {
|
|
2408
|
+
status: "OK",
|
|
2409
|
+
...pendingVerificationResult
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
const permissionError = validateWritableFields([field]);
|
|
2413
|
+
if (permissionError) {
|
|
2414
|
+
return permissionError;
|
|
2415
|
+
}
|
|
2416
|
+
const updateUserDataResult = await updateUserData(session.getUserId(), {
|
|
2417
|
+
[field]: payload.value
|
|
2418
|
+
});
|
|
2419
|
+
return {
|
|
2420
|
+
status: "OK",
|
|
2421
|
+
...updateUserDataResult
|
|
2422
|
+
};
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2425
|
+
function validateWritableFields(fields) {
|
|
2426
|
+
const readOnlyField = fields.find((field) => !canUpdateUserDataField(field));
|
|
2427
|
+
if (!readOnlyField) {
|
|
2428
|
+
return void 0;
|
|
2429
|
+
}
|
|
2353
2430
|
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 } : {}
|
|
2431
|
+
status: "ERROR",
|
|
2432
|
+
code: 403,
|
|
2433
|
+
message: `field is not writable: ${readOnlyField}`
|
|
2360
2434
|
};
|
|
2361
2435
|
}
|
|
2362
2436
|
|
|
@@ -2373,6 +2447,9 @@ var init = createPluginInitFunction(
|
|
|
2373
2447
|
const appVariantId = input?.userContext?.rowndAppVariantId;
|
|
2374
2448
|
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2375
2449
|
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2450
|
+
const clientDomain = input?.userContext?.rowndClientDomain;
|
|
2451
|
+
const clientDomainKey = clientDomain ?? (displayContext === "mobile_app" ? "mobile" : "browser");
|
|
2452
|
+
const clientBaseUrl = pluginConfig2.clientDomains?.[clientDomainKey];
|
|
2376
2453
|
const bootstrapParams = {
|
|
2377
2454
|
appKey: pluginConfig2.rowndAppKey,
|
|
2378
2455
|
...hubBootstrapParams ?? {},
|
|
@@ -2380,10 +2457,10 @@ var init = createPluginInitFunction(
|
|
|
2380
2457
|
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2381
2458
|
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2382
2459
|
};
|
|
2383
|
-
const rewrittenLink = input[linkKey] ?
|
|
2460
|
+
const rewrittenLink = input[linkKey] ? clientBaseUrl ? rewriteLinkToBaseUrl(
|
|
2384
2461
|
input[linkKey],
|
|
2385
2462
|
targetPath,
|
|
2386
|
-
|
|
2463
|
+
clientBaseUrl,
|
|
2387
2464
|
bootstrapParams
|
|
2388
2465
|
) : rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey];
|
|
2389
2466
|
return {
|
|
@@ -2401,17 +2478,17 @@ var init = createPluginInitFunction(
|
|
|
2401
2478
|
id: PLUGIN_ID,
|
|
2402
2479
|
compatibleSDKVersions: PLUGIN_SDK_VERSION,
|
|
2403
2480
|
init: async () => {
|
|
2404
|
-
if (!
|
|
2481
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("session")) {
|
|
2405
2482
|
console.warn(
|
|
2406
2483
|
"RowndMigrationPlugin: Session recipe is not initialized. Session migration will fail."
|
|
2407
2484
|
);
|
|
2408
2485
|
}
|
|
2409
|
-
if (!
|
|
2486
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("thirdparty")) {
|
|
2410
2487
|
console.warn(
|
|
2411
2488
|
"RowndMigrationPlugin: ThirdParty recipe is not initialized. Guest login will fail."
|
|
2412
2489
|
);
|
|
2413
2490
|
}
|
|
2414
|
-
if (!
|
|
2491
|
+
if (!import_supertokens_node4.default.isRecipeInitialized("emailverification")) {
|
|
2415
2492
|
console.warn(
|
|
2416
2493
|
"RowndMigrationPlugin: EmailVerification recipe is not initialized. Verified email profile updates will fail."
|
|
2417
2494
|
);
|
|
@@ -2524,13 +2601,13 @@ var init = createPluginInitFunction(
|
|
|
2524
2601
|
return {
|
|
2525
2602
|
...implementation,
|
|
2526
2603
|
sendEmail: async function(input) {
|
|
2527
|
-
return implementation.sendEmail(
|
|
2528
|
-
|
|
2604
|
+
return implementation.sendEmail(
|
|
2605
|
+
addHubBootstrapParams(
|
|
2529
2606
|
input,
|
|
2530
2607
|
"urlWithLinkCode",
|
|
2531
|
-
|
|
2608
|
+
HUB_LOGIN_PAGE_PATH
|
|
2532
2609
|
)
|
|
2533
|
-
|
|
2610
|
+
);
|
|
2534
2611
|
}
|
|
2535
2612
|
};
|
|
2536
2613
|
}
|
|
@@ -2545,13 +2622,13 @@ var init = createPluginInitFunction(
|
|
|
2545
2622
|
return {
|
|
2546
2623
|
...implementation,
|
|
2547
2624
|
sendSms: async function(input) {
|
|
2548
|
-
return implementation.sendSms(
|
|
2549
|
-
|
|
2625
|
+
return implementation.sendSms(
|
|
2626
|
+
addHubBootstrapParams(
|
|
2550
2627
|
input,
|
|
2551
2628
|
"urlWithLinkCode",
|
|
2552
|
-
|
|
2629
|
+
HUB_LOGIN_PAGE_PATH
|
|
2553
2630
|
)
|
|
2554
|
-
|
|
2631
|
+
);
|
|
2555
2632
|
}
|
|
2556
2633
|
};
|
|
2557
2634
|
}
|
|
@@ -2567,22 +2644,29 @@ var init = createPluginInitFunction(
|
|
|
2567
2644
|
const displayContext = getRequestedDisplayContextFromRequest(
|
|
2568
2645
|
input.options.req
|
|
2569
2646
|
);
|
|
2647
|
+
if (displayContext) {
|
|
2648
|
+
input.userContext.rowndDisplayContext = displayContext;
|
|
2649
|
+
}
|
|
2570
2650
|
const redirectToPath = getRequestedRedirectToPathFromRequest(
|
|
2571
2651
|
input.options.req
|
|
2572
2652
|
);
|
|
2653
|
+
if (redirectToPath) {
|
|
2654
|
+
input.userContext.rowndRedirectToPath = redirectToPath;
|
|
2655
|
+
}
|
|
2656
|
+
const clientDomain = getRequestedClientDomainFromRequest(
|
|
2657
|
+
input.options.req
|
|
2658
|
+
);
|
|
2659
|
+
if (clientDomain) {
|
|
2660
|
+
input.userContext.rowndClientDomain = clientDomain;
|
|
2661
|
+
}
|
|
2573
2662
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2574
2663
|
input.options.req
|
|
2575
2664
|
);
|
|
2665
|
+
if (appVariantId) {
|
|
2666
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2667
|
+
}
|
|
2576
2668
|
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
|
-
});
|
|
2669
|
+
return originalImplementation.createCodePOST(input);
|
|
2586
2670
|
},
|
|
2587
2671
|
consumeCodePOST: async (input) => {
|
|
2588
2672
|
if (originalImplementation.consumeCodePOST === void 0) {
|
|
@@ -2593,11 +2677,11 @@ var init = createPluginInitFunction(
|
|
|
2593
2677
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2594
2678
|
input.options.req
|
|
2595
2679
|
);
|
|
2680
|
+
if (appVariantId) {
|
|
2681
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2682
|
+
}
|
|
2596
2683
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2597
|
-
const response = await originalImplementation.consumeCodePOST(
|
|
2598
|
-
...input,
|
|
2599
|
-
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2600
|
-
});
|
|
2684
|
+
const response = await originalImplementation.consumeCodePOST(input);
|
|
2601
2685
|
if (response.status === "OK") {
|
|
2602
2686
|
await recordRowndAppVariantForUser(
|
|
2603
2687
|
response.user.id,
|
|
@@ -2618,11 +2702,11 @@ var init = createPluginInitFunction(
|
|
|
2618
2702
|
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2619
2703
|
input.options.req
|
|
2620
2704
|
);
|
|
2705
|
+
if (appVariantId) {
|
|
2706
|
+
input.userContext.rowndAppVariantId = appVariantId;
|
|
2707
|
+
}
|
|
2621
2708
|
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2622
|
-
const response = await originalImplementation.signInUpPOST(
|
|
2623
|
-
...input,
|
|
2624
|
-
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2625
|
-
});
|
|
2709
|
+
const response = await originalImplementation.signInUpPOST(input);
|
|
2626
2710
|
if (response.status === "OK") {
|
|
2627
2711
|
await recordRowndAppVariantForUser(
|
|
2628
2712
|
response.user.id,
|
|
@@ -2660,19 +2744,23 @@ var init = createPluginInitFunction(
|
|
|
2660
2744
|
functions: (originalImplementation) => ({
|
|
2661
2745
|
...originalImplementation,
|
|
2662
2746
|
createNewSession: async (input) => {
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
...await buildRowndSessionClaims(
|
|
2747
|
+
const [rowndSessionClaims, rowndIsAnonymousClaim] = await Promise.all([
|
|
2748
|
+
buildRowndSessionClaims(
|
|
2666
2749
|
input.userId,
|
|
2667
2750
|
input.accessTokenPayload
|
|
2668
2751
|
),
|
|
2669
|
-
|
|
2752
|
+
RowndIsAnonymousClaim.build(
|
|
2670
2753
|
input.userId,
|
|
2671
2754
|
input.recipeUserId,
|
|
2672
2755
|
input.tenantId,
|
|
2673
2756
|
input.accessTokenPayload,
|
|
2674
2757
|
input.userContext
|
|
2675
2758
|
)
|
|
2759
|
+
]);
|
|
2760
|
+
input.accessTokenPayload = {
|
|
2761
|
+
...input.accessTokenPayload,
|
|
2762
|
+
...rowndSessionClaims,
|
|
2763
|
+
...rowndIsAnonymousClaim
|
|
2676
2764
|
};
|
|
2677
2765
|
return originalImplementation.createNewSession(input);
|
|
2678
2766
|
}
|
|
@@ -2698,7 +2786,7 @@ var init = createPluginInitFunction(
|
|
|
2698
2786
|
...addHubBootstrapParams(
|
|
2699
2787
|
input,
|
|
2700
2788
|
"emailVerifyLink",
|
|
2701
|
-
|
|
2789
|
+
HUB_VERIFY_EMAIL_PAGE_PATH
|
|
2702
2790
|
)
|
|
2703
2791
|
});
|
|
2704
2792
|
}
|
|
@@ -2751,21 +2839,14 @@ var init = createPluginInitFunction(
|
|
|
2751
2839
|
);
|
|
2752
2840
|
}
|
|
2753
2841
|
}
|
|
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
|
-
}
|
|
2842
|
+
for (const [key, value] of Object.entries(config2.clientDomains ?? {})) {
|
|
2843
|
+
validateClientDomainUrl(key, value);
|
|
2763
2844
|
}
|
|
2764
2845
|
return {
|
|
2765
2846
|
rowndAppKey: config2.rowndAppKey,
|
|
2766
2847
|
rowndAppSecret: config2.rowndAppSecret,
|
|
2767
2848
|
enableDebugLogs: config2.enableDebugLogs,
|
|
2768
|
-
|
|
2849
|
+
clientDomains: config2.clientDomains,
|
|
2769
2850
|
telemetry: config2.telemetry,
|
|
2770
2851
|
schema: config2.schema,
|
|
2771
2852
|
appConfig: config2.appConfig,
|
|
@@ -2773,110 +2854,15 @@ var init = createPluginInitFunction(
|
|
|
2773
2854
|
};
|
|
2774
2855
|
}
|
|
2775
2856
|
);
|
|
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;
|
|
2857
|
+
function validateClientDomainUrl(key, value) {
|
|
2858
|
+
try {
|
|
2859
|
+
if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(value)) {
|
|
2860
|
+
throw new Error();
|
|
2798
2861
|
}
|
|
2862
|
+
new URL(value);
|
|
2863
|
+
} catch {
|
|
2864
|
+
throw new Error(`Invalid clientDomains.${key} in plugin config`);
|
|
2799
2865
|
}
|
|
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
2866
|
}
|
|
2881
2867
|
|
|
2882
2868
|
// src/index.ts
|
|
@@ -2887,15 +2873,16 @@ var index_default = { init };
|
|
|
2887
2873
|
DEFAULT_ROWND_SCHEMA,
|
|
2888
2874
|
GUEST_AUTH_METHOD_ID,
|
|
2889
2875
|
HANDLE_BASE_PATH,
|
|
2876
|
+
HUB_LOGIN_PAGE_PATH,
|
|
2877
|
+
HUB_VERIFY_EMAIL_PAGE_PATH,
|
|
2890
2878
|
PLUGIN_ID,
|
|
2891
2879
|
PLUGIN_SDK_VERSION,
|
|
2880
|
+
PLUGIN_VERSION,
|
|
2892
2881
|
PUBLIC_TENANT_ID,
|
|
2893
2882
|
ROWND_JWT_CLAIMS,
|
|
2894
2883
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
2895
2884
|
RowndPluginError,
|
|
2896
2885
|
getRowndClient,
|
|
2897
|
-
getRowndEmailVerificationContent,
|
|
2898
|
-
getRowndPasswordlessEmailContent,
|
|
2899
2886
|
init,
|
|
2900
2887
|
setRowndClient
|
|
2901
2888
|
});
|