@supertokens-plugins/rownd-nodejs 0.2.1 → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/bulkMigrate.js +1096 -0
- package/dist/cli.js +54 -0
- package/dist/generateAppConfig.js +731 -0
- package/dist/index.d.mts +733 -26
- package/dist/index.d.ts +733 -26
- package/dist/index.js +1905 -113
- package/dist/index.mjs +1894 -110
- package/dist/initConfig.js +136 -0
- package/dist/setupCoreInstance.js +295 -0
- package/package.json +13 -10
- package/scripts/bulkMigrate.ts +0 -450
- package/scripts/config.yaml +0 -27
- package/src/constants.ts +0 -5
- package/src/errors.ts +0 -13
- package/src/index.ts +0 -7
- package/src/logger.ts +0 -7
- package/src/plugin.test.ts +0 -790
- package/src/plugin.ts +0 -189
- package/src/pluginImplementation.ts +0 -187
- package/src/telemetry/axiomTelemetryClient.ts +0 -34
- package/src/telemetry/createTelemetryClient.ts +0 -87
- package/src/telemetry/openTelemetryClient.ts +0 -32
- package/src/types.ts +0 -124
package/dist/index.js
CHANGED
|
@@ -267,14 +267,22 @@ var require_supports_color = __commonJS({
|
|
|
267
267
|
// src/index.ts
|
|
268
268
|
var index_exports = {};
|
|
269
269
|
__export(index_exports, {
|
|
270
|
+
ANONYMOUS_AUTH_METHOD_ID: () => ANONYMOUS_AUTH_METHOD_ID,
|
|
271
|
+
DEFAULT_ROWND_SCHEMA: () => DEFAULT_ROWND_SCHEMA,
|
|
272
|
+
GUEST_AUTH_METHOD_ID: () => GUEST_AUTH_METHOD_ID,
|
|
270
273
|
HANDLE_BASE_PATH: () => HANDLE_BASE_PATH,
|
|
271
274
|
PLUGIN_ID: () => PLUGIN_ID,
|
|
272
275
|
PLUGIN_SDK_VERSION: () => PLUGIN_SDK_VERSION,
|
|
273
276
|
PUBLIC_TENANT_ID: () => PUBLIC_TENANT_ID,
|
|
277
|
+
ROWND_JWT_CLAIMS: () => ROWND_JWT_CLAIMS,
|
|
274
278
|
ROWND_PLUGIN_ERROR_MESSAGES: () => ROWND_PLUGIN_ERROR_MESSAGES,
|
|
275
279
|
RowndPluginError: () => RowndPluginError,
|
|
276
280
|
default: () => index_default,
|
|
277
|
-
|
|
281
|
+
getRowndClient: () => getRowndClient,
|
|
282
|
+
getRowndEmailVerificationContent: () => getRowndEmailVerificationContent,
|
|
283
|
+
getRowndPasswordlessEmailContent: () => getRowndPasswordlessEmailContent,
|
|
284
|
+
init: () => init,
|
|
285
|
+
setRowndClient: () => setRowndClient
|
|
278
286
|
});
|
|
279
287
|
module.exports = __toCommonJS(index_exports);
|
|
280
288
|
|
|
@@ -672,13 +680,46 @@ var buildLogger = (pluginId, version, { fileLocation = true } = {}) => {
|
|
|
672
680
|
|
|
673
681
|
// src/plugin.ts
|
|
674
682
|
var import_node = require("@rownd/node");
|
|
675
|
-
var
|
|
683
|
+
var import_supertokens_node2 = __toESM(require("supertokens-node"));
|
|
676
684
|
|
|
677
685
|
// src/constants.ts
|
|
678
686
|
var PLUGIN_ID = "supertokens-plugin-rownd";
|
|
679
687
|
var PLUGIN_SDK_VERSION = ["23.0.0", "23.0.1", ">=23.0.1"];
|
|
680
688
|
var HANDLE_BASE_PATH = "/plugin/rownd";
|
|
681
689
|
var PUBLIC_TENANT_ID = "public";
|
|
690
|
+
var GUEST_AUTH_METHOD_ID = "guest";
|
|
691
|
+
var ANONYMOUS_AUTH_METHOD_ID = "anonymous";
|
|
692
|
+
var ROWND_JWT_CLAIMS = {
|
|
693
|
+
AppUserId: "https://auth.rownd.io/app_user_id",
|
|
694
|
+
IsVerifiedUser: "https://auth.rownd.io/is_verified_user",
|
|
695
|
+
IsAnonymous: "https://auth.rownd.io/is_anonymous",
|
|
696
|
+
IssuedOffline: "https://auth.rownd.io/issued_offline",
|
|
697
|
+
JwtType: "https://auth.rownd.io/jwt_type",
|
|
698
|
+
PlatformJwt: "https://auth.rownd.io/platform_jwt",
|
|
699
|
+
AuthLevel: "https://auth.rownd.io/auth_level"
|
|
700
|
+
};
|
|
701
|
+
var DEFAULT_ROWND_SCHEMA = {
|
|
702
|
+
zip_code: {
|
|
703
|
+
display_name: "Zip code",
|
|
704
|
+
type: "string",
|
|
705
|
+
user_visible: true
|
|
706
|
+
},
|
|
707
|
+
last_name: {
|
|
708
|
+
display_name: "Last name",
|
|
709
|
+
type: "string",
|
|
710
|
+
user_visible: true
|
|
711
|
+
},
|
|
712
|
+
nick_name: {
|
|
713
|
+
display_name: "Nick name",
|
|
714
|
+
type: "string",
|
|
715
|
+
user_visible: true
|
|
716
|
+
},
|
|
717
|
+
first_name: {
|
|
718
|
+
display_name: "First name",
|
|
719
|
+
type: "string",
|
|
720
|
+
user_visible: true
|
|
721
|
+
}
|
|
722
|
+
};
|
|
682
723
|
|
|
683
724
|
// src/logger.ts
|
|
684
725
|
var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
@@ -686,9 +727,6 @@ var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
|
686
727
|
PLUGIN_SDK_VERSION
|
|
687
728
|
);
|
|
688
729
|
|
|
689
|
-
// src/plugin.ts
|
|
690
|
-
var import_session = __toESM(require("supertokens-node/recipe/session"));
|
|
691
|
-
|
|
692
730
|
// src/telemetry/axiomTelemetryClient.ts
|
|
693
731
|
var DEFAULT_AXIOM_URL = "https://api.axiom.co/v1/datasets";
|
|
694
732
|
var AxiomTelemetryClient = class {
|
|
@@ -803,6 +841,17 @@ function safeRecordEvent(client, event, outcome) {
|
|
|
803
841
|
}
|
|
804
842
|
}
|
|
805
843
|
|
|
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
|
+
|
|
806
855
|
// src/errors.ts
|
|
807
856
|
var ROWND_PLUGIN_ERROR_MESSAGES = {
|
|
808
857
|
MISSING_AUTHORIZATION_HEADER: "Missing authorization header",
|
|
@@ -816,7 +865,46 @@ var RowndPluginError = class extends Error {
|
|
|
816
865
|
};
|
|
817
866
|
|
|
818
867
|
// src/pluginImplementation.ts
|
|
868
|
+
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
869
|
+
try {
|
|
870
|
+
const url = new URL(inputUrl);
|
|
871
|
+
url.pathname = `/${targetPath.replace(/^\//, "")}`;
|
|
872
|
+
for (const [key, value] of Object.entries(searchParams ?? {})) {
|
|
873
|
+
url.searchParams.set(key, value);
|
|
874
|
+
}
|
|
875
|
+
return url.toString();
|
|
876
|
+
} catch {
|
|
877
|
+
const [path = inputUrl, query = ""] = inputUrl.replace(/auth\/verify[^?]*/, targetPath).split("?");
|
|
878
|
+
const params = new URLSearchParams(query);
|
|
879
|
+
for (const [key, value] of Object.entries(searchParams ?? {})) {
|
|
880
|
+
params.set(key, value);
|
|
881
|
+
}
|
|
882
|
+
const queryString = params.toString();
|
|
883
|
+
return queryString ? `${path}?${queryString}` : path;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function rewriteLinkToMobileDeepLink(inputUrl, targetPath, baseUrl, searchParams) {
|
|
887
|
+
const normalizedBaseUrl = baseUrl.endsWith("://") ? baseUrl : baseUrl.replace(/\/+$/, "") + "/";
|
|
888
|
+
const targetUrl = new URL(
|
|
889
|
+
`${normalizedBaseUrl}${targetPath.replace(/^\//, "")}`
|
|
890
|
+
);
|
|
891
|
+
try {
|
|
892
|
+
const sourceUrl = new URL(inputUrl);
|
|
893
|
+
targetUrl.search = sourceUrl.search;
|
|
894
|
+
targetUrl.hash = sourceUrl.hash;
|
|
895
|
+
} catch {
|
|
896
|
+
const [, query = ""] = inputUrl.split("?");
|
|
897
|
+
targetUrl.search = query ? `?${query.split("#")[0]}` : "";
|
|
898
|
+
const hash = inputUrl.split("#")[1];
|
|
899
|
+
targetUrl.hash = hash ? `#${hash}` : "";
|
|
900
|
+
}
|
|
901
|
+
for (const [key, value] of Object.entries(searchParams ?? {})) {
|
|
902
|
+
targetUrl.searchParams.set(key, value);
|
|
903
|
+
}
|
|
904
|
+
return targetUrl.toString();
|
|
905
|
+
}
|
|
819
906
|
var rowndClient;
|
|
907
|
+
var pluginConfig;
|
|
820
908
|
function setRowndClient(client) {
|
|
821
909
|
rowndClient = client;
|
|
822
910
|
}
|
|
@@ -826,6 +914,12 @@ function getRowndClient() {
|
|
|
826
914
|
}
|
|
827
915
|
return rowndClient;
|
|
828
916
|
}
|
|
917
|
+
function setPluginConfig(config2) {
|
|
918
|
+
pluginConfig = config2;
|
|
919
|
+
}
|
|
920
|
+
function getPluginConfig() {
|
|
921
|
+
return pluginConfig;
|
|
922
|
+
}
|
|
829
923
|
async function parseRequest(req) {
|
|
830
924
|
const authHeader = req.getHeaderValue("authorization");
|
|
831
925
|
if (!authHeader) {
|
|
@@ -839,6 +933,403 @@ async function parseRequest(req) {
|
|
|
839
933
|
token
|
|
840
934
|
};
|
|
841
935
|
}
|
|
936
|
+
function handleGetAppConfig(deps) {
|
|
937
|
+
return async (req) => {
|
|
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
|
+
};
|
|
955
|
+
}
|
|
956
|
+
function handleGuestLogin(deps) {
|
|
957
|
+
return async (req, res, _session, userContext) => {
|
|
958
|
+
const startedAt = Date.now();
|
|
959
|
+
const guestId = `guest_${(0, import_crypto.randomUUID)()}`;
|
|
960
|
+
try {
|
|
961
|
+
const body = parseGuestBody(await getJsonBody(req));
|
|
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
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
function handleMigrate(deps) {
|
|
1020
|
+
return async (req, res, _session, userContext) => {
|
|
1021
|
+
const startedAt = Date.now();
|
|
1022
|
+
let tenantId = PUBLIC_TENANT_ID;
|
|
1023
|
+
let rowndUserId;
|
|
1024
|
+
let superTokensUserId;
|
|
1025
|
+
let user;
|
|
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
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
function handleGetUser() {
|
|
1116
|
+
return async (_req, _res, maybeSession) => {
|
|
1117
|
+
const session = requireSession(maybeSession);
|
|
1118
|
+
return {
|
|
1119
|
+
status: "OK",
|
|
1120
|
+
...await getUserById(session.getUserId())
|
|
1121
|
+
};
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
function handleUpdateUser() {
|
|
1125
|
+
return async (req, _res, maybeSession, userContext) => {
|
|
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
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
function handleDeleteUser() {
|
|
1166
|
+
return async (_req, _res, maybeSession) => {
|
|
1167
|
+
const session = requireSession(maybeSession);
|
|
1168
|
+
await import_supertokens_node.default.deleteUser(session.getUserId(), true);
|
|
1169
|
+
return { status: "OK" };
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function handleSignOut() {
|
|
1173
|
+
return async (_req, _res, maybeSession, userContext) => {
|
|
1174
|
+
const session = requireSession(maybeSession);
|
|
1175
|
+
await import_session.default.revokeAllSessionsForUser(
|
|
1176
|
+
session.getUserId(),
|
|
1177
|
+
true,
|
|
1178
|
+
session.getTenantId(),
|
|
1179
|
+
userContext
|
|
1180
|
+
);
|
|
1181
|
+
return { status: "OK" };
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function handleGetUserMeta() {
|
|
1185
|
+
return async (_req, _res, maybeSession) => {
|
|
1186
|
+
const session = requireSession(maybeSession);
|
|
1187
|
+
const metadata = await getUserMetadata(session.getUserId());
|
|
1188
|
+
return {
|
|
1189
|
+
status: "OK",
|
|
1190
|
+
id: session.getUserId(),
|
|
1191
|
+
meta: Object.fromEntries(
|
|
1192
|
+
Object.entries(metadata).filter(
|
|
1193
|
+
([key]) => !isInternalMetadataField(key)
|
|
1194
|
+
)
|
|
1195
|
+
)
|
|
1196
|
+
};
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
function handleUpdateUserMeta() {
|
|
1200
|
+
return async (req, _res, maybeSession) => {
|
|
1201
|
+
const session = requireSession(maybeSession);
|
|
1202
|
+
const payload = parseUpdateMetaBody(await getJsonBody(req));
|
|
1203
|
+
const internalField = Object.keys(payload.meta ?? {}).find(
|
|
1204
|
+
isInternalMetadataField
|
|
1205
|
+
);
|
|
1206
|
+
if (internalField) {
|
|
1207
|
+
return {
|
|
1208
|
+
status: "ERROR",
|
|
1209
|
+
code: 403,
|
|
1210
|
+
message: `field is not writable: ${internalField}`
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
status: "OK",
|
|
1215
|
+
...await updateUserMetadata(session.getUserId(), payload.meta ?? {})
|
|
1216
|
+
};
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
function handleGetUserField() {
|
|
1220
|
+
return async (req, _res, maybeSession) => {
|
|
1221
|
+
const session = requireSession(maybeSession);
|
|
1222
|
+
const field = req.getKeyValueFromQuery("field");
|
|
1223
|
+
if (!field) {
|
|
1224
|
+
return missingFieldResponse();
|
|
1225
|
+
}
|
|
1226
|
+
const user = await getUserById(session.getUserId());
|
|
1227
|
+
return {
|
|
1228
|
+
status: "OK",
|
|
1229
|
+
value: user.data[field]
|
|
1230
|
+
};
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function handleUpdateUserField() {
|
|
1234
|
+
return async (req, _res, maybeSession, userContext) => {
|
|
1235
|
+
const session = requireSession(maybeSession);
|
|
1236
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1237
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1238
|
+
const field = req.getKeyValueFromQuery("field");
|
|
1239
|
+
if (!field) {
|
|
1240
|
+
return missingFieldResponse();
|
|
1241
|
+
}
|
|
1242
|
+
const payload = parseUpdateFieldBody(await getJsonBody(req));
|
|
1243
|
+
if (field === "email" && typeof payload.value === "string") {
|
|
1244
|
+
return {
|
|
1245
|
+
status: "OK",
|
|
1246
|
+
...await startPendingEmailVerification({
|
|
1247
|
+
userId: session.getUserId(),
|
|
1248
|
+
recipeUserId: session.getRecipeUserId(),
|
|
1249
|
+
tenantId: session.getTenantId(),
|
|
1250
|
+
email: payload.value,
|
|
1251
|
+
pendingVerificationId: (0, import_crypto.randomUUID)(),
|
|
1252
|
+
userContext: appVariantId ? { ...userContext, rowndAppVariantId: appVariantId } : userContext
|
|
1253
|
+
})
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
const permissionError = validateWritableFields([field]);
|
|
1257
|
+
if (permissionError) {
|
|
1258
|
+
return permissionError;
|
|
1259
|
+
}
|
|
1260
|
+
return {
|
|
1261
|
+
status: "OK",
|
|
1262
|
+
...await updateUserData(session.getUserId(), {
|
|
1263
|
+
[field]: payload.value
|
|
1264
|
+
})
|
|
1265
|
+
};
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
function requireSession(session) {
|
|
1269
|
+
if (!session) {
|
|
1270
|
+
throw new Error("Session not found");
|
|
1271
|
+
}
|
|
1272
|
+
return session;
|
|
1273
|
+
}
|
|
1274
|
+
async function getJsonBody(req) {
|
|
1275
|
+
return req.getJSONBody();
|
|
1276
|
+
}
|
|
1277
|
+
function parseGuestBody(value) {
|
|
1278
|
+
if (!isJsonRecord(value)) {
|
|
1279
|
+
return {};
|
|
1280
|
+
}
|
|
1281
|
+
return {
|
|
1282
|
+
authLevel: typeof value.auth_level === "string" ? value.auth_level : void 0
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
function parseUpdateUserBody(value) {
|
|
1286
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.data)) {
|
|
1287
|
+
return {};
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
data: value.data,
|
|
1291
|
+
...isJsonRecord(value.context) ? { context: value.context } : {}
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function parseUpdateMetaBody(value) {
|
|
1295
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.meta)) {
|
|
1296
|
+
return {};
|
|
1297
|
+
}
|
|
1298
|
+
return { meta: value.meta };
|
|
1299
|
+
}
|
|
1300
|
+
function parseUpdateFieldBody(value) {
|
|
1301
|
+
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
1302
|
+
return {};
|
|
1303
|
+
}
|
|
1304
|
+
return { value: value.value };
|
|
1305
|
+
}
|
|
1306
|
+
function validateWritableFields(fields) {
|
|
1307
|
+
const readOnlyField = fields.find((field) => !canUpdateUserDataField(field));
|
|
1308
|
+
if (!readOnlyField) {
|
|
1309
|
+
return void 0;
|
|
1310
|
+
}
|
|
1311
|
+
return {
|
|
1312
|
+
status: "ERROR",
|
|
1313
|
+
code: 403,
|
|
1314
|
+
message: `field is not writable: ${readOnlyField}`
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
function missingFieldResponse() {
|
|
1318
|
+
return {
|
|
1319
|
+
status: "ERROR",
|
|
1320
|
+
code: 400,
|
|
1321
|
+
message: "field is required"
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
function isJsonRecord(value) {
|
|
1325
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1326
|
+
}
|
|
1327
|
+
function hasOwn(value, key) {
|
|
1328
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
1329
|
+
}
|
|
1330
|
+
function getErrorMessage(error) {
|
|
1331
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
1332
|
+
}
|
|
842
1333
|
function mapRowndUserToSuperTokens(rowndUser) {
|
|
843
1334
|
const loginMethods = [];
|
|
844
1335
|
const rowndUserData = rowndUser.data || {};
|
|
@@ -877,34 +1368,26 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
877
1368
|
isVerified: !!rowndUserVerifiedData.phone_number
|
|
878
1369
|
});
|
|
879
1370
|
}
|
|
880
|
-
if (rowndUserData.email &&
|
|
1371
|
+
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
881
1372
|
loginMethods.push({
|
|
882
1373
|
recipeId: "passwordless",
|
|
883
1374
|
email: rowndUserData.email,
|
|
884
1375
|
isVerified: !!rowndUserVerifiedData.email
|
|
885
1376
|
});
|
|
886
1377
|
}
|
|
1378
|
+
let authLevel = rowndUser.auth_level;
|
|
887
1379
|
if (loginMethods.length === 0) {
|
|
888
|
-
|
|
1380
|
+
const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : ANONYMOUS_AUTH_METHOD_ID;
|
|
1381
|
+
if (!authLevel) authLevel = thirdPartyId;
|
|
1382
|
+
loginMethods.push({
|
|
1383
|
+
recipeId: "thirdparty",
|
|
1384
|
+
thirdPartyId,
|
|
1385
|
+
thirdPartyUserId: rowndUserData.user_id,
|
|
1386
|
+
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1387
|
+
isVerified: false
|
|
1388
|
+
});
|
|
889
1389
|
}
|
|
890
|
-
const
|
|
891
|
-
const rowndUserAttributes = rowndUser.attributes || {};
|
|
892
|
-
const userMetadata = {
|
|
893
|
-
data: {
|
|
894
|
-
...rowndUserData
|
|
895
|
-
},
|
|
896
|
-
meta: {
|
|
897
|
-
...rowndUserMeta
|
|
898
|
-
},
|
|
899
|
-
verified_data: {
|
|
900
|
-
...rowndUserVerifiedData
|
|
901
|
-
},
|
|
902
|
-
attributes: {
|
|
903
|
-
...rowndUserAttributes
|
|
904
|
-
},
|
|
905
|
-
rownd_migrated: true,
|
|
906
|
-
rownd_user_id: rowndUserData.user_id
|
|
907
|
-
};
|
|
1390
|
+
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
908
1391
|
return {
|
|
909
1392
|
externalUserId: rowndUserData.user_id,
|
|
910
1393
|
loginMethods,
|
|
@@ -950,17 +1433,967 @@ async function fetchRowndUserInfo(userId) {
|
|
|
950
1433
|
}
|
|
951
1434
|
return rowndUser;
|
|
952
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
|
+
function buildRowndUserMetadata(rowndUser) {
|
|
1504
|
+
const metadata = {
|
|
1505
|
+
...rowndUser.meta || {},
|
|
1506
|
+
original_rownd_user: rowndUser
|
|
1507
|
+
};
|
|
1508
|
+
for (const [key, value] of Object.entries(rowndUser.data || {})) {
|
|
1509
|
+
if (!isIdentityField(key) && value !== void 0) {
|
|
1510
|
+
metadata[key] = value;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return metadata;
|
|
1514
|
+
}
|
|
1515
|
+
var RowndIsAnonymousClaim = new import_claims.BooleanClaim({
|
|
1516
|
+
key: "is_anonymous",
|
|
1517
|
+
fetchValue: async (userId) => {
|
|
1518
|
+
const user = await import_supertokens_node.default.getUser(userId);
|
|
1519
|
+
const effectiveAuthLevel = getEffectiveAuthLevel(user);
|
|
1520
|
+
return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
|
|
1524
|
+
const user = await import_supertokens_node.default.getUser(userId);
|
|
1525
|
+
const metadata = user ? await getUserMetadata(user.id) : void 0;
|
|
1526
|
+
const originalRowndUser = metadata?.original_rownd_user;
|
|
1527
|
+
const verifiedData = originalRowndUser?.verified_data;
|
|
1528
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1529
|
+
user,
|
|
1530
|
+
originalRowndUser?.auth_level,
|
|
1531
|
+
verifiedData
|
|
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
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
function buildConfiguredSessionClaims(metadata) {
|
|
1553
|
+
if (!metadata) {
|
|
1554
|
+
return {};
|
|
1555
|
+
}
|
|
1556
|
+
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1557
|
+
const claims = {};
|
|
1558
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1559
|
+
if (field.include_in_session_claims !== true) {
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
const value = metadata.original_rownd_user?.data?.[key] ?? metadata[key];
|
|
1563
|
+
if (value !== void 0) {
|
|
1564
|
+
claims[field.session_claim_name || key] = value;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
return claims;
|
|
1568
|
+
}
|
|
1569
|
+
function getRowndAppUserId(userId, user, currentPayload, metadata) {
|
|
1570
|
+
const originalUserId = metadata?.original_rownd_user?.data?.user_id;
|
|
1571
|
+
if (typeof originalUserId === "string") {
|
|
1572
|
+
return originalUserId;
|
|
1573
|
+
}
|
|
1574
|
+
if (typeof currentPayload.app_user_id === "string") {
|
|
1575
|
+
return currentPayload.app_user_id;
|
|
1576
|
+
}
|
|
1577
|
+
return user?.id || userId;
|
|
1578
|
+
}
|
|
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
|
+
function getAnonymousId(userId, user, metadata) {
|
|
1591
|
+
const originalAnonymousId = metadata?.original_rownd_user?.data?.anonymous_id;
|
|
1592
|
+
if (typeof originalAnonymousId === "string") {
|
|
1593
|
+
return originalAnonymousId;
|
|
1594
|
+
}
|
|
1595
|
+
const anonymousMethod = user?.loginMethods.find((loginMethod) => {
|
|
1596
|
+
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1597
|
+
});
|
|
1598
|
+
const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
|
|
1599
|
+
return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
|
|
1600
|
+
}
|
|
1601
|
+
async function shouldLinkRowndAccounts(input) {
|
|
1602
|
+
const [newAccountInfo, , session] = input;
|
|
1603
|
+
if (!session) {
|
|
1604
|
+
return void 0;
|
|
1605
|
+
}
|
|
1606
|
+
const currentUser = await import_supertokens_node.default.getUser(session.getUserId());
|
|
1607
|
+
if (hasOnlyGuestLoginMethods(currentUser)) {
|
|
1608
|
+
return {
|
|
1609
|
+
shouldAutomaticallyLink: true,
|
|
1610
|
+
shouldRequireVerification: false
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
if (!currentUser || isGuestAccountInfo(newAccountInfo)) {
|
|
1614
|
+
return void 0;
|
|
1615
|
+
}
|
|
1616
|
+
if (doesAccountInfoMatchAuthMethod(currentUser, newAccountInfo)) {
|
|
1617
|
+
return {
|
|
1618
|
+
shouldAutomaticallyLink: true,
|
|
1619
|
+
shouldRequireVerification: true
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
return void 0;
|
|
1623
|
+
}
|
|
1624
|
+
async function getUserMetadata(userId) {
|
|
1625
|
+
const metadata = await import_usermetadata2.default.getUserMetadata(userId);
|
|
1626
|
+
return metadata.metadata || {};
|
|
1627
|
+
}
|
|
1628
|
+
function getPendingVerifications(metadata) {
|
|
1629
|
+
const pendingVerification = metadata.rownd_pending_verification;
|
|
1630
|
+
if (Array.isArray(pendingVerification)) {
|
|
1631
|
+
return pendingVerification.filter(isPendingVerification);
|
|
1632
|
+
}
|
|
1633
|
+
return [];
|
|
1634
|
+
}
|
|
1635
|
+
function isPendingVerification(value) {
|
|
1636
|
+
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
1637
|
+
}
|
|
1638
|
+
async function getUserById(userId) {
|
|
1639
|
+
const metadata = await getUserMetadata(userId);
|
|
1640
|
+
const stUser = await import_supertokens_node.default.getUser(userId);
|
|
1641
|
+
if (!stUser) {
|
|
1642
|
+
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
1643
|
+
}
|
|
1644
|
+
const originalRowndUser = metadata.original_rownd_user;
|
|
1645
|
+
const rowndUser = originalRowndUser?.data?.user_id || userId;
|
|
1646
|
+
const state = originalRowndUser?.state || "enabled";
|
|
1647
|
+
const dataFieldKeys = /* @__PURE__ */ new Set();
|
|
1648
|
+
const data = {
|
|
1649
|
+
user_id: userId
|
|
1650
|
+
};
|
|
1651
|
+
for (const [key, value] of Object.entries(originalRowndUser?.data || {})) {
|
|
1652
|
+
if (!isIdentityField(key)) {
|
|
1653
|
+
data[key] = value;
|
|
1654
|
+
dataFieldKeys.add(key);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1658
|
+
for (const key of Object.keys(schema)) {
|
|
1659
|
+
dataFieldKeys.add(key);
|
|
1660
|
+
if (!isInternalMetadataField(key) && !isIdentityField(key) && metadata[key] !== void 0) {
|
|
1661
|
+
data[key] = metadata[key];
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
const verifiedData = {
|
|
1665
|
+
...originalRowndUser?.verified_data || {}
|
|
1666
|
+
};
|
|
1667
|
+
let lastUsedAt = stUser.timeJoined;
|
|
1668
|
+
for (const method of stUser.loginMethods) {
|
|
1669
|
+
if (typeof method.lastUsed === "number" && method.lastUsed > lastUsedAt) {
|
|
1670
|
+
lastUsedAt = method.lastUsed;
|
|
1671
|
+
}
|
|
1672
|
+
if (method.recipeId === "passwordless") {
|
|
1673
|
+
if (method.email) {
|
|
1674
|
+
verifiedData.email = method.email;
|
|
1675
|
+
if (data.email === void 0) data.email = method.email;
|
|
1676
|
+
}
|
|
1677
|
+
if (method.phoneNumber) {
|
|
1678
|
+
verifiedData.phone_number = method.phoneNumber;
|
|
1679
|
+
if (data.phone_number === void 0)
|
|
1680
|
+
data.phone_number = method.phoneNumber;
|
|
1681
|
+
}
|
|
1682
|
+
} else if (method.recipeId === "thirdparty") {
|
|
1683
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1684
|
+
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
1685
|
+
if (method.verified && method.email) {
|
|
1686
|
+
verifiedData.email = method.email;
|
|
1687
|
+
}
|
|
1688
|
+
if (method.email && data.email === void 0) {
|
|
1689
|
+
data.email = method.email;
|
|
1690
|
+
}
|
|
1691
|
+
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
1692
|
+
data.google_id = thirdPartyUserId;
|
|
1693
|
+
verifiedData.google_id = thirdPartyUserId;
|
|
1694
|
+
}
|
|
1695
|
+
if (thirdPartyId === "apple" && thirdPartyUserId) {
|
|
1696
|
+
data.apple_id = thirdPartyUserId;
|
|
1697
|
+
verifiedData.apple_id = thirdPartyUserId;
|
|
1698
|
+
}
|
|
1699
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1700
|
+
if (method.email && data.email === void 0) {
|
|
1701
|
+
data.email = method.email;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
if (verifiedData.email === true && typeof data.email === "string") {
|
|
1706
|
+
verifiedData.email = data.email;
|
|
1707
|
+
}
|
|
1708
|
+
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
1709
|
+
verifiedData.phone_number = data.phone_number;
|
|
1710
|
+
}
|
|
1711
|
+
const anonymousId = getAnonymousId(stUser.id, stUser, metadata);
|
|
1712
|
+
if (anonymousId && data.anonymous_id === void 0) {
|
|
1713
|
+
data.anonymous_id = anonymousId;
|
|
1714
|
+
}
|
|
1715
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1716
|
+
stUser,
|
|
1717
|
+
originalRowndUser?.auth_level,
|
|
1718
|
+
verifiedData
|
|
1719
|
+
);
|
|
1720
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1721
|
+
if (data[key] === void 0 && field.type === "string") {
|
|
1722
|
+
data[key] = "";
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
const mapMethod = (method) => {
|
|
1726
|
+
if (method.recipeId === "thirdparty") {
|
|
1727
|
+
if (getThirdPartyId(method) === "google") return "google";
|
|
1728
|
+
if (getThirdPartyId(method) === "apple") return "apple";
|
|
1729
|
+
} else if (method.recipeId === "passwordless") {
|
|
1730
|
+
if (method.email) return "email";
|
|
1731
|
+
if (method.phoneNumber) return "phone";
|
|
1732
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1733
|
+
return "email";
|
|
1734
|
+
}
|
|
1735
|
+
return "email";
|
|
1736
|
+
};
|
|
1737
|
+
const sortedByJoined = [...stUser.loginMethods].sort(
|
|
1738
|
+
(a, b) => a.timeJoined - b.timeJoined
|
|
1739
|
+
);
|
|
1740
|
+
const sortedByLastUsed = [
|
|
1741
|
+
...stUser.loginMethods
|
|
1742
|
+
].sort((a, b) => (b.lastUsed || b.timeJoined) - (a.lastUsed || a.timeJoined));
|
|
1743
|
+
const firstMethod = sortedByJoined[0];
|
|
1744
|
+
const lastMethod = sortedByLastUsed[0];
|
|
1745
|
+
const metadataMeta = Object.fromEntries(
|
|
1746
|
+
Object.entries(metadata).filter(
|
|
1747
|
+
([key]) => !isInternalMetadataField(key) && !dataFieldKeys.has(key)
|
|
1748
|
+
)
|
|
1749
|
+
);
|
|
1750
|
+
const meta = {
|
|
1751
|
+
...metadataMeta,
|
|
1752
|
+
created: new Date(stUser.timeJoined).toISOString(),
|
|
1753
|
+
first_sign_in: new Date(stUser.timeJoined).toISOString(),
|
|
1754
|
+
last_sign_in: new Date(lastUsedAt).toISOString(),
|
|
1755
|
+
last_active: new Date(lastUsedAt).toISOString(),
|
|
1756
|
+
first_sign_in_method: firstMethod ? mapMethod(firstMethod) : "email",
|
|
1757
|
+
last_sign_in_method: lastMethod ? mapMethod(lastMethod) : "email"
|
|
1758
|
+
};
|
|
1759
|
+
return {
|
|
1760
|
+
rownd_user: rowndUser,
|
|
1761
|
+
data,
|
|
1762
|
+
meta,
|
|
1763
|
+
verified_data: verifiedData,
|
|
1764
|
+
state,
|
|
1765
|
+
auth_level: authLevel,
|
|
1766
|
+
redacted: [],
|
|
1767
|
+
groups: originalRowndUser?.groups || [],
|
|
1768
|
+
attributes: originalRowndUser?.attributes || {}
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
async function updateUserData(userId, inputData) {
|
|
1772
|
+
const metadata = await getUserMetadata(userId);
|
|
1773
|
+
const updatedMetadata = {
|
|
1774
|
+
...metadata,
|
|
1775
|
+
...inputData
|
|
1776
|
+
};
|
|
1777
|
+
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
1778
|
+
return getUserById(userId);
|
|
1779
|
+
}
|
|
1780
|
+
async function startPendingEmailVerification(input) {
|
|
1781
|
+
const metadata = await getUserMetadata(input.userId);
|
|
1782
|
+
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
1783
|
+
if (currentEmail === input.email) {
|
|
1784
|
+
return getUserById(input.userId);
|
|
1785
|
+
}
|
|
1786
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1787
|
+
const pendingEmailVerifications = pendingVerifications.filter(
|
|
1788
|
+
(pendingVerification2) => pendingVerification2.field === "email"
|
|
1789
|
+
);
|
|
1790
|
+
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1791
|
+
await import_emailverification.default.revokeEmailVerificationTokens(
|
|
1792
|
+
input.tenantId,
|
|
1793
|
+
input.recipeUserId,
|
|
1794
|
+
pendingVerification2.value,
|
|
1795
|
+
input.userContext
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
const pendingVerification = {
|
|
1799
|
+
id: input.pendingVerificationId,
|
|
1800
|
+
field: "email",
|
|
1801
|
+
value: input.email,
|
|
1802
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1803
|
+
};
|
|
1804
|
+
await import_usermetadata2.default.updateUserMetadata(input.userId, {
|
|
1805
|
+
...metadata,
|
|
1806
|
+
rownd_pending_verification: [
|
|
1807
|
+
...pendingVerifications.filter(
|
|
1808
|
+
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
1809
|
+
),
|
|
1810
|
+
pendingVerification
|
|
1811
|
+
]
|
|
1812
|
+
});
|
|
1813
|
+
const response = await import_emailverification.default.sendEmailVerificationEmail(
|
|
1814
|
+
input.tenantId,
|
|
1815
|
+
input.userId,
|
|
1816
|
+
input.recipeUserId,
|
|
1817
|
+
input.email,
|
|
1818
|
+
{
|
|
1819
|
+
...input.userContext,
|
|
1820
|
+
rowndPendingVerificationId: pendingVerification.id
|
|
1821
|
+
}
|
|
1822
|
+
);
|
|
1823
|
+
if (response.status === "EMAIL_ALREADY_VERIFIED_ERROR") {
|
|
1824
|
+
await completePendingEmailVerification({
|
|
1825
|
+
recipeUserId: input.recipeUserId,
|
|
1826
|
+
email: input.email,
|
|
1827
|
+
userContext: input.userContext
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
return getUserById(input.userId);
|
|
1831
|
+
}
|
|
1832
|
+
async function completePendingEmailVerification(input) {
|
|
1833
|
+
const user = await import_supertokens_node.default.getUser(
|
|
1834
|
+
input.recipeUserId.getAsString(),
|
|
1835
|
+
input.userContext
|
|
1836
|
+
);
|
|
1837
|
+
const userId = user?.id ?? input.recipeUserId.getAsString();
|
|
1838
|
+
const metadata = await getUserMetadata(userId);
|
|
1839
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1840
|
+
const pendingVerification = pendingVerifications.find(
|
|
1841
|
+
(pendingVerification2) => pendingVerification2.field === "email" && pendingVerification2.value === input.email
|
|
1842
|
+
);
|
|
1843
|
+
if (!pendingVerification) {
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
let metadataUserId = userId;
|
|
1847
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
|
|
1848
|
+
if (passwordlessEmailMethod) {
|
|
1849
|
+
const updateResult = await import_passwordless.default.updateUser({
|
|
1850
|
+
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
1851
|
+
email: input.email,
|
|
1852
|
+
userContext: input.userContext
|
|
1853
|
+
});
|
|
1854
|
+
if (updateResult.status !== "OK") {
|
|
1855
|
+
throw new Error(
|
|
1856
|
+
`Failed to update verified email method: ${updateResult.status}`
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
1860
|
+
const isPasswordlessSignUpAllowed = await import_accountlinking.default.isSignUpAllowed(
|
|
1861
|
+
PUBLIC_TENANT_ID,
|
|
1862
|
+
{
|
|
1863
|
+
recipeId: "passwordless",
|
|
1864
|
+
email: input.email
|
|
1865
|
+
},
|
|
1866
|
+
true,
|
|
1867
|
+
void 0,
|
|
1868
|
+
input.userContext
|
|
1869
|
+
);
|
|
1870
|
+
if (!isPasswordlessSignUpAllowed) {
|
|
1871
|
+
throw new Error("Passwordless sign up is not allowed for this email");
|
|
1872
|
+
}
|
|
1873
|
+
const passwordlessUser = await import_passwordless.default.signInUp({
|
|
1874
|
+
email: input.email,
|
|
1875
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
1876
|
+
userContext: input.userContext
|
|
1877
|
+
});
|
|
1878
|
+
const primaryUserResult = await import_accountlinking.default.createPrimaryUser(
|
|
1879
|
+
passwordlessUser.recipeUserId,
|
|
1880
|
+
input.userContext
|
|
1881
|
+
);
|
|
1882
|
+
const primaryUserId = primaryUserResult.status === "OK" ? primaryUserResult.user.id : primaryUserResult.status === "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" ? primaryUserResult.primaryUserId : passwordlessUser.user.id;
|
|
1883
|
+
if (userId !== primaryUserId) {
|
|
1884
|
+
const linkResult = await import_accountlinking.default.linkAccounts(
|
|
1885
|
+
input.recipeUserId,
|
|
1886
|
+
primaryUserId,
|
|
1887
|
+
input.userContext
|
|
1888
|
+
);
|
|
1889
|
+
if (linkResult.status !== "OK") {
|
|
1890
|
+
throw new Error(
|
|
1891
|
+
`Failed to link verified email method: ${linkResult.status}`
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
metadataUserId = primaryUserId;
|
|
1896
|
+
}
|
|
1897
|
+
const updatedMetadata = {
|
|
1898
|
+
...metadata,
|
|
1899
|
+
...metadata.original_rownd_user ? {
|
|
1900
|
+
original_rownd_user: {
|
|
1901
|
+
...metadata.original_rownd_user,
|
|
1902
|
+
data: {
|
|
1903
|
+
...metadata.original_rownd_user.data,
|
|
1904
|
+
email: input.email
|
|
1905
|
+
},
|
|
1906
|
+
verified_data: {
|
|
1907
|
+
...metadata.original_rownd_user.verified_data,
|
|
1908
|
+
email: input.email
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
} : {},
|
|
1912
|
+
rownd_pending_verification: pendingVerifications.filter(
|
|
1913
|
+
(verification) => verification !== pendingVerification
|
|
1914
|
+
)
|
|
1915
|
+
};
|
|
1916
|
+
await import_usermetadata2.default.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
1917
|
+
}
|
|
1918
|
+
async function updateUserMetadata(userId, inputMeta) {
|
|
1919
|
+
const metadata = await getUserMetadata(userId);
|
|
1920
|
+
const updatedMetadata = {
|
|
1921
|
+
...metadata,
|
|
1922
|
+
...inputMeta
|
|
1923
|
+
};
|
|
1924
|
+
await import_usermetadata2.default.updateUserMetadata(userId, updatedMetadata);
|
|
1925
|
+
return {
|
|
1926
|
+
id: userId,
|
|
1927
|
+
meta: Object.fromEntries(
|
|
1928
|
+
Object.entries(updatedMetadata).filter(
|
|
1929
|
+
([key]) => !isInternalMetadataField(key)
|
|
1930
|
+
)
|
|
1931
|
+
)
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
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
|
+
function getPasswordlessEmailLoginMethod(user) {
|
|
1950
|
+
return user?.loginMethods.find((method) => {
|
|
1951
|
+
return method.recipeId === "passwordless" && !!method.email;
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
function hasOnlyGuestLoginMethods(user) {
|
|
1955
|
+
return !!user?.loginMethods.length && user.loginMethods.every(isGuestLoginMethod);
|
|
1956
|
+
}
|
|
1957
|
+
function isGuestLoginMethod(method) {
|
|
1958
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1959
|
+
return method.recipeId === "thirdparty" && (thirdPartyId === GUEST_AUTH_METHOD_ID || thirdPartyId === ANONYMOUS_AUTH_METHOD_ID);
|
|
1960
|
+
}
|
|
1961
|
+
function isGuestAccountInfo(input) {
|
|
1962
|
+
return input?.recipeId === "thirdparty" && (input.thirdParty?.id === GUEST_AUTH_METHOD_ID || input.thirdParty?.id === ANONYMOUS_AUTH_METHOD_ID);
|
|
1963
|
+
}
|
|
1964
|
+
function doesAccountInfoMatchAuthMethod(user, accountInfo) {
|
|
1965
|
+
if (!user) {
|
|
1966
|
+
return false;
|
|
1967
|
+
}
|
|
1968
|
+
const normalizedEmail = accountInfo.email?.toLowerCase();
|
|
1969
|
+
if (normalizedEmail) {
|
|
1970
|
+
return user.loginMethods.some((method) => {
|
|
1971
|
+
if (isGuestLoginMethod(method) || !method.email) {
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
return method.email.toLowerCase() === normalizedEmail;
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
if (accountInfo.phoneNumber) {
|
|
1978
|
+
return user.loginMethods.some((method) => {
|
|
1979
|
+
return !isGuestLoginMethod(method) && method.phoneNumber === accountInfo.phoneNumber;
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
function hasVerifiedRealLoginMethod(user) {
|
|
1985
|
+
return !!user?.loginMethods.some((method) => {
|
|
1986
|
+
if (isGuestLoginMethod(method)) {
|
|
1987
|
+
return false;
|
|
1988
|
+
}
|
|
1989
|
+
if (method.recipeId === "passwordless") {
|
|
1990
|
+
return !!(method.email || method.phoneNumber);
|
|
1991
|
+
}
|
|
1992
|
+
if (method.recipeId === "thirdparty") {
|
|
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,
|
|
2052
|
+
{
|
|
2053
|
+
enabled: true,
|
|
2054
|
+
display_name: getStringMethodProperty(val, "displayName") ?? key,
|
|
2055
|
+
icon_light_url: getStringMethodProperty(val, "iconLightUrl"),
|
|
2056
|
+
icon_dark_url: getStringMethodProperty(val, "iconDarkUrl")
|
|
2057
|
+
}
|
|
2058
|
+
] : [key, void 0];
|
|
2059
|
+
}).filter(([, v]) => v !== void 0)
|
|
2060
|
+
);
|
|
2061
|
+
const googleMethod = methods.google;
|
|
2062
|
+
const appleMethod = methods.apple;
|
|
2063
|
+
const anonymousMethod = methods.anonymous;
|
|
2064
|
+
const anonymousType = getAnonymousType(anonymousMethod);
|
|
2065
|
+
const googleOneTap = getOneTapConfig(googleMethod);
|
|
2066
|
+
return {
|
|
2067
|
+
email: { enabled: !!methods.email },
|
|
2068
|
+
phone: { enabled: !!methods.phone },
|
|
2069
|
+
google: {
|
|
2070
|
+
enabled: !!googleMethod,
|
|
2071
|
+
client_id: getStringMethodProperty(googleMethod, "clientId") ?? "",
|
|
2072
|
+
ios_client_id: getStringMethodProperty(googleMethod, "iosClientId") ?? "",
|
|
2073
|
+
scopes: getStringArrayMethodProperty(googleMethod, "scopes") ?? [],
|
|
2074
|
+
...getSignInFasterWithGoogle(googleMethod) ? { sign_in_faster_with_google: getSignInFasterWithGoogle(googleMethod) } : {},
|
|
2075
|
+
one_tap: {
|
|
2076
|
+
browser: {
|
|
2077
|
+
auto_prompt: googleOneTap?.browser?.autoPrompt ?? false,
|
|
2078
|
+
delay: googleOneTap?.browser?.delay ?? 7e3
|
|
2079
|
+
},
|
|
2080
|
+
mobile_app: {
|
|
2081
|
+
auto_prompt: googleOneTap?.mobileApp?.autoPrompt ?? false,
|
|
2082
|
+
delay: googleOneTap?.mobileApp?.delay ?? 7e3
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
},
|
|
2086
|
+
apple: {
|
|
2087
|
+
enabled: !!appleMethod,
|
|
2088
|
+
client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
|
|
2089
|
+
},
|
|
2090
|
+
anonymous: {
|
|
2091
|
+
enabled: !!anonymousMethod && anonymousType !== "instant",
|
|
2092
|
+
...anonymousMethod && anonymousType !== "instant" ? { type: anonymousType } : {},
|
|
2093
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "displayName") !== void 0 ? {
|
|
2094
|
+
display_name: getStringMethodProperty(
|
|
2095
|
+
anonymousMethod,
|
|
2096
|
+
"displayName"
|
|
2097
|
+
)
|
|
2098
|
+
} : {},
|
|
2099
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconLightUrl") !== void 0 ? {
|
|
2100
|
+
icon_light_url: getStringMethodProperty(
|
|
2101
|
+
anonymousMethod,
|
|
2102
|
+
"iconLightUrl"
|
|
2103
|
+
)
|
|
2104
|
+
} : {},
|
|
2105
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconDarkUrl") !== void 0 ? {
|
|
2106
|
+
icon_dark_url: getStringMethodProperty(
|
|
2107
|
+
anonymousMethod,
|
|
2108
|
+
"iconDarkUrl"
|
|
2109
|
+
)
|
|
2110
|
+
} : {}
|
|
2111
|
+
},
|
|
2112
|
+
...customProviders
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
function isInstantAnonymousMethod(methods) {
|
|
2116
|
+
return (methods ?? []).some(
|
|
2117
|
+
(method) => method.method === "anonymous" && getAnonymousType(method) === "instant"
|
|
2118
|
+
);
|
|
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;
|
|
2175
|
+
}
|
|
2176
|
+
const existing = result[key];
|
|
2177
|
+
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
2178
|
+
}
|
|
2179
|
+
return result;
|
|
2180
|
+
}
|
|
2181
|
+
function buildAppConfig(config2, stConfig, appVariantId) {
|
|
2182
|
+
const userSchema = config2.schema ?? DEFAULT_ROWND_SCHEMA;
|
|
2183
|
+
const baseApp = config2.appConfig ?? {};
|
|
2184
|
+
const subBrand = appVariantId ? config2.subBrands?.[appVariantId] : void 0;
|
|
2185
|
+
const app = appVariantId ? subBrand && mergeConfigInput(baseApp, subBrand) : baseApp;
|
|
2186
|
+
if (!app) {
|
|
2187
|
+
return void 0;
|
|
2188
|
+
}
|
|
2189
|
+
const branding = app.branding ?? {};
|
|
2190
|
+
const auth = app.auth ?? {};
|
|
2191
|
+
const signInMethods = buildSignInMethodsConfig(app.signInMethods);
|
|
2192
|
+
const variant = getSubBrandVariant(app);
|
|
2193
|
+
const finalSchema = { ...userSchema };
|
|
2194
|
+
if (signInMethods.email.enabled && !finalSchema.email) {
|
|
2195
|
+
finalSchema.email = {
|
|
2196
|
+
display_name: "Email",
|
|
2197
|
+
type: "string",
|
|
2198
|
+
user_visible: true
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
if (signInMethods.phone.enabled && !finalSchema.phone_number) {
|
|
2202
|
+
finalSchema.phone_number = {
|
|
2203
|
+
display_name: "Phone number",
|
|
2204
|
+
type: "string",
|
|
2205
|
+
user_visible: true
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if (signInMethods.google.enabled && !finalSchema.google_id) {
|
|
2209
|
+
finalSchema.google_id = {
|
|
2210
|
+
display_name: "Google ID",
|
|
2211
|
+
type: "string",
|
|
2212
|
+
user_visible: false
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
if (signInMethods.apple.enabled && !finalSchema.apple_id) {
|
|
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 } : {}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
function buildAuthEmailConfig(authEmail) {
|
|
2342
|
+
return {
|
|
2343
|
+
from_address: authEmail?.fromAddress ?? "no-reply@rownd.io",
|
|
2344
|
+
image: authEmail?.image ?? "",
|
|
2345
|
+
...authEmail?.subject ? { subject: authEmail.subject } : {},
|
|
2346
|
+
...authEmail?.callToActionText ? { call_to_action_text: authEmail.callToActionText } : {},
|
|
2347
|
+
...authEmail?.verifyTemplate ? { verify_template: authEmail.verifyTemplate } : {},
|
|
2348
|
+
...authEmail?.customContent ? { custom_content: authEmail.customContent } : {},
|
|
2349
|
+
...authEmail?.customClosingContent ? { custom_closing_content: authEmail.customClosingContent } : {}
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
function buildAuthMobileConfig(authMobile) {
|
|
2353
|
+
return {
|
|
2354
|
+
...authMobile?.title ? { title: authMobile.title } : {},
|
|
2355
|
+
...authMobile?.image ? { image: authMobile.image } : {},
|
|
2356
|
+
...authMobile?.callToActionText ? { call_to_action_text: authMobile.callToActionText } : {},
|
|
2357
|
+
...authMobile?.hyperlinkText ? { hyperlink_text: authMobile.hyperlinkText } : {},
|
|
2358
|
+
...authMobile?.hyperlinkRedirectUrl ? { hyperlink_redirect_url: authMobile.hyperlinkRedirectUrl } : {},
|
|
2359
|
+
...authMobile?.customContent ? { custom_content: authMobile.customContent } : {}
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
953
2362
|
|
|
954
2363
|
// src/plugin.ts
|
|
955
2364
|
var init = createPluginInitFunction(
|
|
956
|
-
(
|
|
2365
|
+
(pluginConfig2) => {
|
|
957
2366
|
const rowndClient2 = (0, import_node.createInstance)({
|
|
958
|
-
app_key:
|
|
959
|
-
app_secret:
|
|
2367
|
+
app_key: pluginConfig2.rowndAppKey,
|
|
2368
|
+
app_secret: pluginConfig2.rowndAppSecret
|
|
960
2369
|
});
|
|
961
|
-
const telemetryClient = createClient(
|
|
2370
|
+
const telemetryClient = createClient(pluginConfig2.telemetry);
|
|
2371
|
+
let hubBootstrapParams;
|
|
2372
|
+
const addHubBootstrapParams = (input, linkKey, targetPath) => {
|
|
2373
|
+
const appVariantId = input?.userContext?.rowndAppVariantId;
|
|
2374
|
+
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2375
|
+
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2376
|
+
const bootstrapParams = {
|
|
2377
|
+
appKey: pluginConfig2.rowndAppKey,
|
|
2378
|
+
...hubBootstrapParams ?? {},
|
|
2379
|
+
...typeof appVariantId === "string" ? { appVariantId } : {},
|
|
2380
|
+
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2381
|
+
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2382
|
+
};
|
|
2383
|
+
const rewrittenLink = input[linkKey] ? displayContext === "mobile_app" && pluginConfig2.mobileDeepLinkBaseUrl ? rewriteLinkToMobileDeepLink(
|
|
2384
|
+
input[linkKey],
|
|
2385
|
+
targetPath,
|
|
2386
|
+
pluginConfig2.mobileDeepLinkBaseUrl,
|
|
2387
|
+
bootstrapParams
|
|
2388
|
+
) : rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey];
|
|
2389
|
+
return {
|
|
2390
|
+
...input,
|
|
2391
|
+
[linkKey]: rewrittenLink
|
|
2392
|
+
};
|
|
2393
|
+
};
|
|
962
2394
|
setRowndClient(rowndClient2);
|
|
963
|
-
|
|
2395
|
+
setPluginConfig(pluginConfig2);
|
|
2396
|
+
if (pluginConfig2.enableDebugLogs) {
|
|
964
2397
|
enableDebugLogs();
|
|
965
2398
|
}
|
|
966
2399
|
logDebugMessage("Rownd plugin init complete");
|
|
@@ -968,109 +2401,341 @@ var init = createPluginInitFunction(
|
|
|
968
2401
|
id: PLUGIN_ID,
|
|
969
2402
|
compatibleSDKVersions: PLUGIN_SDK_VERSION,
|
|
970
2403
|
init: async () => {
|
|
971
|
-
if (!
|
|
2404
|
+
if (!import_supertokens_node2.default.isRecipeInitialized("session")) {
|
|
972
2405
|
console.warn(
|
|
973
2406
|
"RowndMigrationPlugin: Session recipe is not initialized. Session migration will fail."
|
|
974
2407
|
);
|
|
975
2408
|
}
|
|
2409
|
+
if (!import_supertokens_node2.default.isRecipeInitialized("thirdparty")) {
|
|
2410
|
+
console.warn(
|
|
2411
|
+
"RowndMigrationPlugin: ThirdParty recipe is not initialized. Guest login will fail."
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
if (!import_supertokens_node2.default.isRecipeInitialized("emailverification")) {
|
|
2415
|
+
console.warn(
|
|
2416
|
+
"RowndMigrationPlugin: EmailVerification recipe is not initialized. Verified email profile updates will fail."
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
976
2419
|
},
|
|
977
|
-
routeHandlers(
|
|
978
|
-
const apiBasePath =
|
|
2420
|
+
routeHandlers(stConfig) {
|
|
2421
|
+
const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
|
|
2422
|
+
hubBootstrapParams = {
|
|
2423
|
+
apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
|
|
2424
|
+
apiBasePath
|
|
2425
|
+
};
|
|
2426
|
+
const routeHandlerDeps = {
|
|
2427
|
+
pluginConfig: pluginConfig2,
|
|
2428
|
+
stConfig,
|
|
2429
|
+
telemetryClient
|
|
2430
|
+
};
|
|
979
2431
|
return {
|
|
980
2432
|
status: "OK",
|
|
981
2433
|
routeHandlers: [
|
|
2434
|
+
{
|
|
2435
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/app-config`,
|
|
2436
|
+
method: "get",
|
|
2437
|
+
handler: withRequestHandler(
|
|
2438
|
+
handleGetAppConfig(routeHandlerDeps)
|
|
2439
|
+
)
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/guest`,
|
|
2443
|
+
method: "post",
|
|
2444
|
+
handler: withRequestHandler(handleGuestLogin(routeHandlerDeps))
|
|
2445
|
+
},
|
|
982
2446
|
{
|
|
983
2447
|
path: `${apiBasePath}${HANDLE_BASE_PATH}/migrate`,
|
|
984
2448
|
method: "post",
|
|
985
|
-
handler: withRequestHandler(
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
2449
|
+
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
path: `${apiBasePath}/plugin/migrate-session`,
|
|
2453
|
+
method: "post",
|
|
2454
|
+
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2455
|
+
},
|
|
2456
|
+
{
|
|
2457
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/signout`,
|
|
2458
|
+
method: "post",
|
|
2459
|
+
verifySessionOptions: {
|
|
2460
|
+
sessionRequired: true,
|
|
2461
|
+
checkDatabase: true
|
|
2462
|
+
},
|
|
2463
|
+
handler: withRequestHandler(handleSignOut())
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2467
|
+
method: "get",
|
|
2468
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2469
|
+
handler: withRequestHandler(handleGetUser())
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2473
|
+
method: "put",
|
|
2474
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2475
|
+
handler: withRequestHandler(handleUpdateUser())
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2479
|
+
method: "delete",
|
|
2480
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2481
|
+
handler: withRequestHandler(handleDeleteUser())
|
|
2482
|
+
},
|
|
2483
|
+
{
|
|
2484
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/meta`,
|
|
2485
|
+
method: "get",
|
|
2486
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2487
|
+
handler: withRequestHandler(handleGetUserMeta())
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/meta`,
|
|
2491
|
+
method: "put",
|
|
2492
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2493
|
+
handler: withRequestHandler(handleUpdateUserMeta())
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
|
|
2497
|
+
method: "get",
|
|
2498
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2499
|
+
handler: withRequestHandler(handleGetUserField())
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
|
|
2503
|
+
method: "put",
|
|
2504
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2505
|
+
handler: withRequestHandler(handleUpdateUserField())
|
|
2506
|
+
}
|
|
2507
|
+
]
|
|
2508
|
+
};
|
|
2509
|
+
},
|
|
2510
|
+
overrideMap: {
|
|
2511
|
+
passwordless: {
|
|
2512
|
+
config: (config2) => {
|
|
2513
|
+
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
2514
|
+
const originalSmsDeliveryOverride = config2.smsDelivery?.override;
|
|
2515
|
+
return {
|
|
2516
|
+
...config2,
|
|
2517
|
+
emailDelivery: {
|
|
2518
|
+
...config2.emailDelivery,
|
|
2519
|
+
override: (originalImplementation, builder) => {
|
|
2520
|
+
const implementation = originalEmailDeliveryOverride ? originalEmailDeliveryOverride(
|
|
2521
|
+
originalImplementation,
|
|
2522
|
+
builder
|
|
2523
|
+
) : originalImplementation;
|
|
2524
|
+
return {
|
|
2525
|
+
...implementation,
|
|
2526
|
+
sendEmail: async function(input) {
|
|
2527
|
+
return implementation.sendEmail({
|
|
2528
|
+
...addHubBootstrapParams(
|
|
2529
|
+
input,
|
|
2530
|
+
"urlWithLinkCode",
|
|
2531
|
+
"account/login"
|
|
2532
|
+
)
|
|
2533
|
+
});
|
|
996
2534
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
superTokensUserId = user.id;
|
|
1018
|
-
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1019
|
-
logDebugMessage(
|
|
1020
|
-
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1021
|
-
);
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
},
|
|
2538
|
+
smsDelivery: {
|
|
2539
|
+
...config2.smsDelivery,
|
|
2540
|
+
override: (originalImplementation, builder) => {
|
|
2541
|
+
const implementation = originalSmsDeliveryOverride ? originalSmsDeliveryOverride(
|
|
2542
|
+
originalImplementation,
|
|
2543
|
+
builder
|
|
2544
|
+
) : originalImplementation;
|
|
2545
|
+
return {
|
|
2546
|
+
...implementation,
|
|
2547
|
+
sendSms: async function(input) {
|
|
2548
|
+
return implementation.sendSms({
|
|
2549
|
+
...addHubBootstrapParams(
|
|
2550
|
+
input,
|
|
2551
|
+
"urlWithLinkCode",
|
|
2552
|
+
"account/login"
|
|
2553
|
+
)
|
|
2554
|
+
});
|
|
1022
2555
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
};
|
|
2560
|
+
},
|
|
2561
|
+
apis: (originalImplementation) => ({
|
|
2562
|
+
...originalImplementation,
|
|
2563
|
+
createCodePOST: async (input) => {
|
|
2564
|
+
if (originalImplementation.createCodePOST === void 0) {
|
|
2565
|
+
throw new Error("Passwordless createCodePOST is unavailable");
|
|
2566
|
+
}
|
|
2567
|
+
const displayContext = getRequestedDisplayContextFromRequest(
|
|
2568
|
+
input.options.req
|
|
2569
|
+
);
|
|
2570
|
+
const redirectToPath = getRequestedRedirectToPathFromRequest(
|
|
2571
|
+
input.options.req
|
|
2572
|
+
);
|
|
2573
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2574
|
+
input.options.req
|
|
2575
|
+
);
|
|
2576
|
+
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
|
+
});
|
|
2586
|
+
},
|
|
2587
|
+
consumeCodePOST: async (input) => {
|
|
2588
|
+
if (originalImplementation.consumeCodePOST === void 0) {
|
|
2589
|
+
throw new Error(
|
|
2590
|
+
"Passwordless consumeCodePOST is unavailable"
|
|
2591
|
+
);
|
|
2592
|
+
}
|
|
2593
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2594
|
+
input.options.req
|
|
2595
|
+
);
|
|
2596
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2597
|
+
const response = await originalImplementation.consumeCodePOST({
|
|
2598
|
+
...input,
|
|
2599
|
+
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2600
|
+
});
|
|
2601
|
+
if (response.status === "OK") {
|
|
2602
|
+
await recordRowndAppVariantForUser(
|
|
2603
|
+
response.user.id,
|
|
2604
|
+
appVariantId
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
return response;
|
|
2608
|
+
}
|
|
2609
|
+
})
|
|
2610
|
+
},
|
|
2611
|
+
thirdparty: {
|
|
2612
|
+
apis: (originalImplementation) => ({
|
|
2613
|
+
...originalImplementation,
|
|
2614
|
+
signInUpPOST: async (input) => {
|
|
2615
|
+
if (originalImplementation.signInUpPOST === void 0) {
|
|
2616
|
+
throw new Error("ThirdParty signInUpPOST is unavailable");
|
|
2617
|
+
}
|
|
2618
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2619
|
+
input.options.req
|
|
2620
|
+
);
|
|
2621
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2622
|
+
const response = await originalImplementation.signInUpPOST({
|
|
2623
|
+
...input,
|
|
2624
|
+
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2625
|
+
});
|
|
2626
|
+
if (response.status === "OK") {
|
|
2627
|
+
await recordRowndAppVariantForUser(
|
|
2628
|
+
response.user.id,
|
|
2629
|
+
appVariantId
|
|
2630
|
+
);
|
|
2631
|
+
}
|
|
2632
|
+
return response;
|
|
2633
|
+
}
|
|
2634
|
+
})
|
|
2635
|
+
},
|
|
2636
|
+
accountlinking: {
|
|
2637
|
+
recipeInitRequired: true,
|
|
2638
|
+
config: (config2) => {
|
|
2639
|
+
const originalShouldDoAutomaticAccountLinking = config2.shouldDoAutomaticAccountLinking;
|
|
2640
|
+
return {
|
|
2641
|
+
...config2,
|
|
2642
|
+
shouldDoAutomaticAccountLinking: async (...input) => {
|
|
2643
|
+
const rowndLinkingDecision = await shouldLinkRowndAccounts(input);
|
|
2644
|
+
if (rowndLinkingDecision) {
|
|
2645
|
+
return rowndLinkingDecision;
|
|
2646
|
+
}
|
|
2647
|
+
if (originalShouldDoAutomaticAccountLinking) {
|
|
2648
|
+
return originalShouldDoAutomaticAccountLinking(...input);
|
|
2649
|
+
}
|
|
2650
|
+
return {
|
|
2651
|
+
shouldAutomaticallyLink: false,
|
|
2652
|
+
shouldRequireVerification: false
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
},
|
|
2658
|
+
session: {
|
|
2659
|
+
recipeInitRequired: true,
|
|
2660
|
+
functions: (originalImplementation) => ({
|
|
2661
|
+
...originalImplementation,
|
|
2662
|
+
createNewSession: async (input) => {
|
|
2663
|
+
input.accessTokenPayload = {
|
|
2664
|
+
...input.accessTokenPayload,
|
|
2665
|
+
...await buildRowndSessionClaims(
|
|
2666
|
+
input.userId,
|
|
2667
|
+
input.accessTokenPayload
|
|
2668
|
+
),
|
|
2669
|
+
...await RowndIsAnonymousClaim.build(
|
|
2670
|
+
input.userId,
|
|
2671
|
+
input.recipeUserId,
|
|
2672
|
+
input.tenantId,
|
|
2673
|
+
input.accessTokenPayload,
|
|
2674
|
+
input.userContext
|
|
2675
|
+
)
|
|
2676
|
+
};
|
|
2677
|
+
return originalImplementation.createNewSession(input);
|
|
2678
|
+
}
|
|
2679
|
+
})
|
|
2680
|
+
},
|
|
2681
|
+
emailverification: {
|
|
2682
|
+
recipeInitRequired: true,
|
|
2683
|
+
config: (config2) => {
|
|
2684
|
+
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
2685
|
+
return {
|
|
2686
|
+
...config2,
|
|
2687
|
+
emailDelivery: {
|
|
2688
|
+
...config2.emailDelivery,
|
|
2689
|
+
override: (originalImplementation, builder) => {
|
|
2690
|
+
const implementation = originalEmailDeliveryOverride ? originalEmailDeliveryOverride(
|
|
2691
|
+
originalImplementation,
|
|
2692
|
+
builder
|
|
2693
|
+
) : originalImplementation;
|
|
2694
|
+
return {
|
|
2695
|
+
...implementation,
|
|
2696
|
+
sendEmail: async (input) => {
|
|
2697
|
+
return implementation.sendEmail({
|
|
2698
|
+
...addHubBootstrapParams(
|
|
2699
|
+
input,
|
|
2700
|
+
"emailVerifyLink",
|
|
2701
|
+
"account/verify-email"
|
|
2702
|
+
)
|
|
2703
|
+
});
|
|
1025
2704
|
}
|
|
1026
|
-
|
|
1027
|
-
req,
|
|
1028
|
-
res,
|
|
1029
|
-
PUBLIC_TENANT_ID,
|
|
1030
|
-
recipeUserId,
|
|
1031
|
-
{},
|
|
1032
|
-
{},
|
|
1033
|
-
userContext
|
|
1034
|
-
);
|
|
1035
|
-
logDebugMessage(
|
|
1036
|
-
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
1037
|
-
);
|
|
1038
|
-
telemetryClient.recordSuccess({
|
|
1039
|
-
outcome: "success",
|
|
1040
|
-
durationMs: Date.now() - startedAt,
|
|
1041
|
-
tenantId,
|
|
1042
|
-
rowndUserId,
|
|
1043
|
-
superTokensUserId
|
|
1044
|
-
});
|
|
1045
|
-
return { status: "OK" };
|
|
1046
|
-
} catch (error) {
|
|
1047
|
-
logDebugMessage(
|
|
1048
|
-
`Migration failed. Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1049
|
-
);
|
|
1050
|
-
telemetryClient.recordError({
|
|
1051
|
-
error,
|
|
1052
|
-
startedAt,
|
|
1053
|
-
tenantId,
|
|
1054
|
-
rowndUserId,
|
|
1055
|
-
superTokensUserId
|
|
1056
|
-
});
|
|
1057
|
-
return {
|
|
1058
|
-
status: "ERROR",
|
|
1059
|
-
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
2705
|
+
};
|
|
1062
2706
|
}
|
|
1063
|
-
|
|
2707
|
+
}
|
|
2708
|
+
};
|
|
2709
|
+
},
|
|
2710
|
+
apis: (originalImplementation) => ({
|
|
2711
|
+
...originalImplementation,
|
|
2712
|
+
verifyEmailPOST: async (input) => {
|
|
2713
|
+
if (originalImplementation.verifyEmailPOST === void 0) {
|
|
2714
|
+
throw new Error(
|
|
2715
|
+
"EmailVerification verifyEmailPOST is unavailable"
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2718
|
+
const response = await originalImplementation.verifyEmailPOST(input);
|
|
2719
|
+
if (response.status === "OK") {
|
|
2720
|
+
await completePendingEmailVerification({
|
|
2721
|
+
recipeUserId: response.user.recipeUserId,
|
|
2722
|
+
email: response.user.email,
|
|
2723
|
+
userContext: input.userContext
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
return response;
|
|
1064
2727
|
}
|
|
1065
|
-
|
|
1066
|
-
}
|
|
2728
|
+
})
|
|
2729
|
+
}
|
|
1067
2730
|
}
|
|
1068
2731
|
};
|
|
1069
2732
|
},
|
|
1070
2733
|
() => ({}),
|
|
1071
2734
|
(config2) => {
|
|
1072
2735
|
if (!config2?.rowndAppKey || !config2?.rowndAppSecret) {
|
|
1073
|
-
throw new Error(
|
|
2736
|
+
throw new Error(
|
|
2737
|
+
"Missing rowndAppKey or rowndAppSecret in plugin config"
|
|
2738
|
+
);
|
|
1074
2739
|
}
|
|
1075
2740
|
if (config2.telemetry?.provider === "axiom") {
|
|
1076
2741
|
if (!config2.telemetry.token || !config2.telemetry.dataset) {
|
|
@@ -1086,24 +2751,151 @@ var init = createPluginInitFunction(
|
|
|
1086
2751
|
);
|
|
1087
2752
|
}
|
|
1088
2753
|
}
|
|
2754
|
+
if (config2.mobileDeepLinkBaseUrl !== void 0) {
|
|
2755
|
+
try {
|
|
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
|
+
}
|
|
2763
|
+
}
|
|
1089
2764
|
return {
|
|
1090
2765
|
rowndAppKey: config2.rowndAppKey,
|
|
1091
2766
|
rowndAppSecret: config2.rowndAppSecret,
|
|
1092
2767
|
enableDebugLogs: config2.enableDebugLogs,
|
|
1093
|
-
|
|
2768
|
+
mobileDeepLinkBaseUrl: config2.mobileDeepLinkBaseUrl,
|
|
2769
|
+
telemetry: config2.telemetry,
|
|
2770
|
+
schema: config2.schema,
|
|
2771
|
+
appConfig: config2.appConfig,
|
|
2772
|
+
subBrands: config2.subBrands
|
|
1094
2773
|
};
|
|
1095
2774
|
}
|
|
1096
2775
|
);
|
|
1097
2776
|
|
|
2777
|
+
// src/emailContent.ts
|
|
2778
|
+
function getActiveAppConfig(userContext) {
|
|
2779
|
+
const config2 = getPluginConfig();
|
|
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;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
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
|
+
}
|
|
2881
|
+
|
|
1098
2882
|
// src/index.ts
|
|
1099
2883
|
var index_default = { init };
|
|
1100
2884
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1101
2885
|
0 && (module.exports = {
|
|
2886
|
+
ANONYMOUS_AUTH_METHOD_ID,
|
|
2887
|
+
DEFAULT_ROWND_SCHEMA,
|
|
2888
|
+
GUEST_AUTH_METHOD_ID,
|
|
1102
2889
|
HANDLE_BASE_PATH,
|
|
1103
2890
|
PLUGIN_ID,
|
|
1104
2891
|
PLUGIN_SDK_VERSION,
|
|
1105
2892
|
PUBLIC_TENANT_ID,
|
|
2893
|
+
ROWND_JWT_CLAIMS,
|
|
1106
2894
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
1107
2895
|
RowndPluginError,
|
|
1108
|
-
|
|
2896
|
+
getRowndClient,
|
|
2897
|
+
getRowndEmailVerificationContent,
|
|
2898
|
+
getRowndPasswordlessEmailContent,
|
|
2899
|
+
init,
|
|
2900
|
+
setRowndClient
|
|
1109
2901
|
});
|