@supertokens-plugins/rownd-nodejs 0.2.1 → 0.3.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/bulkMigrate.js +1096 -0
- package/dist/cli.js +54 -0
- package/dist/generateAppConfig.js +731 -0
- package/dist/index.d.mts +716 -26
- package/dist/index.d.ts +716 -26
- package/dist/index.js +1756 -113
- package/dist/index.mjs +1747 -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.mjs
CHANGED
|
@@ -665,6 +665,39 @@ var PLUGIN_ID = "supertokens-plugin-rownd";
|
|
|
665
665
|
var PLUGIN_SDK_VERSION = ["23.0.0", "23.0.1", ">=23.0.1"];
|
|
666
666
|
var HANDLE_BASE_PATH = "/plugin/rownd";
|
|
667
667
|
var PUBLIC_TENANT_ID = "public";
|
|
668
|
+
var GUEST_AUTH_METHOD_ID = "guest";
|
|
669
|
+
var ANONYMOUS_AUTH_METHOD_ID = "anonymous";
|
|
670
|
+
var ROWND_JWT_CLAIMS = {
|
|
671
|
+
AppUserId: "https://auth.rownd.io/app_user_id",
|
|
672
|
+
IsVerifiedUser: "https://auth.rownd.io/is_verified_user",
|
|
673
|
+
IsAnonymous: "https://auth.rownd.io/is_anonymous",
|
|
674
|
+
IssuedOffline: "https://auth.rownd.io/issued_offline",
|
|
675
|
+
JwtType: "https://auth.rownd.io/jwt_type",
|
|
676
|
+
PlatformJwt: "https://auth.rownd.io/platform_jwt",
|
|
677
|
+
AuthLevel: "https://auth.rownd.io/auth_level"
|
|
678
|
+
};
|
|
679
|
+
var DEFAULT_ROWND_SCHEMA = {
|
|
680
|
+
zip_code: {
|
|
681
|
+
display_name: "Zip code",
|
|
682
|
+
type: "string",
|
|
683
|
+
user_visible: true
|
|
684
|
+
},
|
|
685
|
+
last_name: {
|
|
686
|
+
display_name: "Last name",
|
|
687
|
+
type: "string",
|
|
688
|
+
user_visible: true
|
|
689
|
+
},
|
|
690
|
+
nick_name: {
|
|
691
|
+
display_name: "Nick name",
|
|
692
|
+
type: "string",
|
|
693
|
+
user_visible: true
|
|
694
|
+
},
|
|
695
|
+
first_name: {
|
|
696
|
+
display_name: "First name",
|
|
697
|
+
type: "string",
|
|
698
|
+
user_visible: true
|
|
699
|
+
}
|
|
700
|
+
};
|
|
668
701
|
|
|
669
702
|
// src/logger.ts
|
|
670
703
|
var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
@@ -672,9 +705,6 @@ var { logDebugMessage, enableDebugLogs } = buildLogger(
|
|
|
672
705
|
PLUGIN_SDK_VERSION
|
|
673
706
|
);
|
|
674
707
|
|
|
675
|
-
// src/plugin.ts
|
|
676
|
-
import Session from "supertokens-node/recipe/session";
|
|
677
|
-
|
|
678
708
|
// src/telemetry/axiomTelemetryClient.ts
|
|
679
709
|
var DEFAULT_AXIOM_URL = "https://api.axiom.co/v1/datasets";
|
|
680
710
|
var AxiomTelemetryClient = class {
|
|
@@ -789,6 +819,17 @@ function safeRecordEvent(client, event, outcome) {
|
|
|
789
819
|
}
|
|
790
820
|
}
|
|
791
821
|
|
|
822
|
+
// src/pluginImplementation.ts
|
|
823
|
+
import { randomUUID } from "crypto";
|
|
824
|
+
import SuperTokens from "supertokens-node";
|
|
825
|
+
import AccountLinking from "supertokens-node/recipe/accountlinking";
|
|
826
|
+
import EmailVerification from "supertokens-node/recipe/emailverification";
|
|
827
|
+
import Passwordless from "supertokens-node/recipe/passwordless";
|
|
828
|
+
import Session from "supertokens-node/recipe/session";
|
|
829
|
+
import { BooleanClaim } from "supertokens-node/recipe/session/claims";
|
|
830
|
+
import ThirdParty from "supertokens-node/recipe/thirdparty";
|
|
831
|
+
import UserMetadata2 from "supertokens-node/recipe/usermetadata";
|
|
832
|
+
|
|
792
833
|
// src/errors.ts
|
|
793
834
|
var ROWND_PLUGIN_ERROR_MESSAGES = {
|
|
794
835
|
MISSING_AUTHORIZATION_HEADER: "Missing authorization header",
|
|
@@ -802,7 +843,26 @@ var RowndPluginError = class extends Error {
|
|
|
802
843
|
};
|
|
803
844
|
|
|
804
845
|
// src/pluginImplementation.ts
|
|
846
|
+
function rewriteLinkPath(inputUrl, targetPath, searchParams) {
|
|
847
|
+
try {
|
|
848
|
+
const url = new URL(inputUrl);
|
|
849
|
+
url.pathname = `/${targetPath.replace(/^\//, "")}`;
|
|
850
|
+
for (const [key, value] of Object.entries(searchParams ?? {})) {
|
|
851
|
+
url.searchParams.set(key, value);
|
|
852
|
+
}
|
|
853
|
+
return url.toString();
|
|
854
|
+
} catch {
|
|
855
|
+
const [path = inputUrl, query = ""] = inputUrl.replace(/auth\/verify[^?]*/, targetPath).split("?");
|
|
856
|
+
const params = new URLSearchParams(query);
|
|
857
|
+
for (const [key, value] of Object.entries(searchParams ?? {})) {
|
|
858
|
+
params.set(key, value);
|
|
859
|
+
}
|
|
860
|
+
const queryString = params.toString();
|
|
861
|
+
return queryString ? `${path}?${queryString}` : path;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
805
864
|
var rowndClient;
|
|
865
|
+
var pluginConfig;
|
|
806
866
|
function setRowndClient(client) {
|
|
807
867
|
rowndClient = client;
|
|
808
868
|
}
|
|
@@ -812,6 +872,9 @@ function getRowndClient() {
|
|
|
812
872
|
}
|
|
813
873
|
return rowndClient;
|
|
814
874
|
}
|
|
875
|
+
function setPluginConfig(config2) {
|
|
876
|
+
pluginConfig = config2;
|
|
877
|
+
}
|
|
815
878
|
async function parseRequest(req) {
|
|
816
879
|
const authHeader = req.getHeaderValue("authorization");
|
|
817
880
|
if (!authHeader) {
|
|
@@ -825,6 +888,403 @@ async function parseRequest(req) {
|
|
|
825
888
|
token
|
|
826
889
|
};
|
|
827
890
|
}
|
|
891
|
+
function handleGetAppConfig(deps) {
|
|
892
|
+
return async (req) => {
|
|
893
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
894
|
+
const appConfig = buildAppConfig(
|
|
895
|
+
deps.pluginConfig,
|
|
896
|
+
deps.stConfig,
|
|
897
|
+
appVariantId
|
|
898
|
+
);
|
|
899
|
+
if (!appConfig) {
|
|
900
|
+
return {
|
|
901
|
+
status: "ERROR",
|
|
902
|
+
message: `Unknown Rownd app variant: ${appVariantId}`
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
status: "OK",
|
|
907
|
+
...appConfig
|
|
908
|
+
};
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function handleGuestLogin(deps) {
|
|
912
|
+
return async (req, res, _session, userContext) => {
|
|
913
|
+
const startedAt = Date.now();
|
|
914
|
+
const guestId = `guest_${randomUUID()}`;
|
|
915
|
+
try {
|
|
916
|
+
const body = parseGuestBody(await getJsonBody(req));
|
|
917
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
918
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
919
|
+
const thirdPartyId = body.authLevel === ANONYMOUS_AUTH_METHOD_ID ? ANONYMOUS_AUTH_METHOD_ID : GUEST_AUTH_METHOD_ID;
|
|
920
|
+
const thirdPartyUserId = thirdPartyId === ANONYMOUS_AUTH_METHOD_ID ? `anon_${randomUUID()}` : guestId;
|
|
921
|
+
const response = await ThirdParty.manuallyCreateOrUpdateUser(
|
|
922
|
+
PUBLIC_TENANT_ID,
|
|
923
|
+
thirdPartyId,
|
|
924
|
+
thirdPartyUserId,
|
|
925
|
+
`${thirdPartyUserId}@anonymous.local`,
|
|
926
|
+
false,
|
|
927
|
+
void 0,
|
|
928
|
+
userContext
|
|
929
|
+
);
|
|
930
|
+
if (response.status !== "OK") {
|
|
931
|
+
throw new Error(
|
|
932
|
+
`Guest user creation failed with status: ${response.status}`
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
await Session.createNewSession(
|
|
936
|
+
req,
|
|
937
|
+
res,
|
|
938
|
+
PUBLIC_TENANT_ID,
|
|
939
|
+
response.recipeUserId,
|
|
940
|
+
{
|
|
941
|
+
...buildRowndAudience({}, appVariantId),
|
|
942
|
+
auth_level: GUEST_AUTH_METHOD_ID,
|
|
943
|
+
is_anonymous: true,
|
|
944
|
+
app_user_id: response.user.id
|
|
945
|
+
},
|
|
946
|
+
{},
|
|
947
|
+
userContext
|
|
948
|
+
);
|
|
949
|
+
logDebugMessage(`Guest session created for user: ${response.user.id}`);
|
|
950
|
+
deps.telemetryClient.recordSuccess({
|
|
951
|
+
outcome: "success",
|
|
952
|
+
durationMs: Date.now() - startedAt,
|
|
953
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
954
|
+
superTokensUserId: response.user.id
|
|
955
|
+
});
|
|
956
|
+
return {
|
|
957
|
+
status: "OK",
|
|
958
|
+
createdNewRecipeUser: response.createdNewRecipeUser
|
|
959
|
+
};
|
|
960
|
+
} catch (error) {
|
|
961
|
+
logDebugMessage(`Guest login failed. Error: ${getErrorMessage(error)}`);
|
|
962
|
+
deps.telemetryClient.recordError({
|
|
963
|
+
error,
|
|
964
|
+
startedAt,
|
|
965
|
+
tenantId: PUBLIC_TENANT_ID
|
|
966
|
+
});
|
|
967
|
+
return {
|
|
968
|
+
status: "ERROR",
|
|
969
|
+
message: "Guest login failed"
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
function handleMigrate(deps) {
|
|
975
|
+
return async (req, res, _session, userContext) => {
|
|
976
|
+
const startedAt = Date.now();
|
|
977
|
+
let tenantId = PUBLIC_TENANT_ID;
|
|
978
|
+
let rowndUserId;
|
|
979
|
+
let superTokensUserId;
|
|
980
|
+
let user;
|
|
981
|
+
let recipeUserId;
|
|
982
|
+
try {
|
|
983
|
+
if (!deps.stConfig.supertokens) {
|
|
984
|
+
throw new Error("Supertokens config not found");
|
|
985
|
+
}
|
|
986
|
+
const parsed = await parseRequest(req);
|
|
987
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
988
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
989
|
+
rowndUserId = await validateRowndToken(parsed.token);
|
|
990
|
+
user = await SuperTokens.getUser(rowndUserId, userContext);
|
|
991
|
+
if (!user) {
|
|
992
|
+
const rowndUser = await fetchRowndUserInfo(rowndUserId);
|
|
993
|
+
const stUserImport = mapRowndUserToSuperTokens(rowndUser);
|
|
994
|
+
try {
|
|
995
|
+
const importedUser = await importUser(
|
|
996
|
+
stUserImport,
|
|
997
|
+
deps.stConfig.supertokens
|
|
998
|
+
);
|
|
999
|
+
superTokensUserId = importedUser.id;
|
|
1000
|
+
if (importedUser.loginMethods[0]?.recipeUserId) {
|
|
1001
|
+
recipeUserId = SuperTokens.convertToRecipeUserId(
|
|
1002
|
+
importedUser.loginMethods[0].recipeUserId
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
user = await SuperTokens.getUser(rowndUserId, userContext);
|
|
1007
|
+
if (!user) {
|
|
1008
|
+
throw err;
|
|
1009
|
+
}
|
|
1010
|
+
superTokensUserId = user.id;
|
|
1011
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1012
|
+
logDebugMessage(
|
|
1013
|
+
`User already migrated (race condition). tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
logDebugMessage(
|
|
1017
|
+
`User migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1018
|
+
);
|
|
1019
|
+
} else {
|
|
1020
|
+
superTokensUserId = user.id;
|
|
1021
|
+
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1022
|
+
logDebugMessage(
|
|
1023
|
+
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
if (superTokensUserId) {
|
|
1027
|
+
await recordRowndAppVariantForUser(superTokensUserId, appVariantId);
|
|
1028
|
+
}
|
|
1029
|
+
if (!recipeUserId) {
|
|
1030
|
+
throw new Error("User not found or has no login methods");
|
|
1031
|
+
}
|
|
1032
|
+
await Session.createNewSession(
|
|
1033
|
+
req,
|
|
1034
|
+
res,
|
|
1035
|
+
PUBLIC_TENANT_ID,
|
|
1036
|
+
recipeUserId,
|
|
1037
|
+
{
|
|
1038
|
+
...buildRowndAudience({}, appVariantId)
|
|
1039
|
+
},
|
|
1040
|
+
{},
|
|
1041
|
+
userContext
|
|
1042
|
+
);
|
|
1043
|
+
logDebugMessage(
|
|
1044
|
+
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
1045
|
+
);
|
|
1046
|
+
deps.telemetryClient.recordSuccess({
|
|
1047
|
+
outcome: "success",
|
|
1048
|
+
durationMs: Date.now() - startedAt,
|
|
1049
|
+
tenantId,
|
|
1050
|
+
rowndUserId,
|
|
1051
|
+
superTokensUserId
|
|
1052
|
+
});
|
|
1053
|
+
return { status: "OK" };
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
logDebugMessage(`Migration failed. Error: ${getErrorMessage(error)}`);
|
|
1056
|
+
deps.telemetryClient.recordError({
|
|
1057
|
+
error,
|
|
1058
|
+
startedAt,
|
|
1059
|
+
tenantId,
|
|
1060
|
+
rowndUserId,
|
|
1061
|
+
superTokensUserId
|
|
1062
|
+
});
|
|
1063
|
+
return {
|
|
1064
|
+
status: "ERROR",
|
|
1065
|
+
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function handleGetUser() {
|
|
1071
|
+
return async (_req, _res, maybeSession) => {
|
|
1072
|
+
const session = requireSession(maybeSession);
|
|
1073
|
+
return {
|
|
1074
|
+
status: "OK",
|
|
1075
|
+
...await getUserById(session.getUserId())
|
|
1076
|
+
};
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function handleUpdateUser() {
|
|
1080
|
+
return async (req, _res, maybeSession, userContext) => {
|
|
1081
|
+
const session = requireSession(maybeSession);
|
|
1082
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1083
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1084
|
+
const payload = parseUpdateUserBody(await getJsonBody(req));
|
|
1085
|
+
const inputData = payload.data ?? {};
|
|
1086
|
+
const requestUserContext = {
|
|
1087
|
+
...userContext,
|
|
1088
|
+
...payload.context
|
|
1089
|
+
};
|
|
1090
|
+
const { email, ...dataWithoutEmail } = inputData;
|
|
1091
|
+
const hasEmailUpdate = hasOwn(inputData, "email") && typeof email === "string";
|
|
1092
|
+
const permissionError = validateWritableFields(
|
|
1093
|
+
Object.keys(dataWithoutEmail)
|
|
1094
|
+
);
|
|
1095
|
+
if (permissionError) {
|
|
1096
|
+
return permissionError;
|
|
1097
|
+
}
|
|
1098
|
+
if (Object.keys(dataWithoutEmail).length > 0) {
|
|
1099
|
+
await updateUserData(session.getUserId(), dataWithoutEmail);
|
|
1100
|
+
}
|
|
1101
|
+
if (hasEmailUpdate) {
|
|
1102
|
+
return {
|
|
1103
|
+
status: "OK",
|
|
1104
|
+
...await startPendingEmailVerification({
|
|
1105
|
+
userId: session.getUserId(),
|
|
1106
|
+
recipeUserId: session.getRecipeUserId(),
|
|
1107
|
+
tenantId: session.getTenantId(),
|
|
1108
|
+
email,
|
|
1109
|
+
pendingVerificationId: randomUUID(),
|
|
1110
|
+
userContext: appVariantId ? { ...requestUserContext, rowndAppVariantId: appVariantId } : requestUserContext
|
|
1111
|
+
})
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
return {
|
|
1115
|
+
status: "OK",
|
|
1116
|
+
...await getUserById(session.getUserId())
|
|
1117
|
+
};
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
function handleDeleteUser() {
|
|
1121
|
+
return async (_req, _res, maybeSession) => {
|
|
1122
|
+
const session = requireSession(maybeSession);
|
|
1123
|
+
await SuperTokens.deleteUser(session.getUserId(), true);
|
|
1124
|
+
return { status: "OK" };
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
function handleSignOut() {
|
|
1128
|
+
return async (_req, _res, maybeSession, userContext) => {
|
|
1129
|
+
const session = requireSession(maybeSession);
|
|
1130
|
+
await Session.revokeAllSessionsForUser(
|
|
1131
|
+
session.getUserId(),
|
|
1132
|
+
true,
|
|
1133
|
+
session.getTenantId(),
|
|
1134
|
+
userContext
|
|
1135
|
+
);
|
|
1136
|
+
return { status: "OK" };
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
function handleGetUserMeta() {
|
|
1140
|
+
return async (_req, _res, maybeSession) => {
|
|
1141
|
+
const session = requireSession(maybeSession);
|
|
1142
|
+
const metadata = await getUserMetadata(session.getUserId());
|
|
1143
|
+
return {
|
|
1144
|
+
status: "OK",
|
|
1145
|
+
id: session.getUserId(),
|
|
1146
|
+
meta: Object.fromEntries(
|
|
1147
|
+
Object.entries(metadata).filter(
|
|
1148
|
+
([key]) => !isInternalMetadataField(key)
|
|
1149
|
+
)
|
|
1150
|
+
)
|
|
1151
|
+
};
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function handleUpdateUserMeta() {
|
|
1155
|
+
return async (req, _res, maybeSession) => {
|
|
1156
|
+
const session = requireSession(maybeSession);
|
|
1157
|
+
const payload = parseUpdateMetaBody(await getJsonBody(req));
|
|
1158
|
+
const internalField = Object.keys(payload.meta ?? {}).find(
|
|
1159
|
+
isInternalMetadataField
|
|
1160
|
+
);
|
|
1161
|
+
if (internalField) {
|
|
1162
|
+
return {
|
|
1163
|
+
status: "ERROR",
|
|
1164
|
+
code: 403,
|
|
1165
|
+
message: `field is not writable: ${internalField}`
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
return {
|
|
1169
|
+
status: "OK",
|
|
1170
|
+
...await updateUserMetadata(session.getUserId(), payload.meta ?? {})
|
|
1171
|
+
};
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function handleGetUserField() {
|
|
1175
|
+
return async (req, _res, maybeSession) => {
|
|
1176
|
+
const session = requireSession(maybeSession);
|
|
1177
|
+
const field = req.getKeyValueFromQuery("field");
|
|
1178
|
+
if (!field) {
|
|
1179
|
+
return missingFieldResponse();
|
|
1180
|
+
}
|
|
1181
|
+
const user = await getUserById(session.getUserId());
|
|
1182
|
+
return {
|
|
1183
|
+
status: "OK",
|
|
1184
|
+
value: user.data[field]
|
|
1185
|
+
};
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
function handleUpdateUserField() {
|
|
1189
|
+
return async (req, _res, maybeSession, userContext) => {
|
|
1190
|
+
const session = requireSession(maybeSession);
|
|
1191
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(req);
|
|
1192
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1193
|
+
const field = req.getKeyValueFromQuery("field");
|
|
1194
|
+
if (!field) {
|
|
1195
|
+
return missingFieldResponse();
|
|
1196
|
+
}
|
|
1197
|
+
const payload = parseUpdateFieldBody(await getJsonBody(req));
|
|
1198
|
+
if (field === "email" && typeof payload.value === "string") {
|
|
1199
|
+
return {
|
|
1200
|
+
status: "OK",
|
|
1201
|
+
...await startPendingEmailVerification({
|
|
1202
|
+
userId: session.getUserId(),
|
|
1203
|
+
recipeUserId: session.getRecipeUserId(),
|
|
1204
|
+
tenantId: session.getTenantId(),
|
|
1205
|
+
email: payload.value,
|
|
1206
|
+
pendingVerificationId: randomUUID(),
|
|
1207
|
+
userContext: appVariantId ? { ...userContext, rowndAppVariantId: appVariantId } : userContext
|
|
1208
|
+
})
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
const permissionError = validateWritableFields([field]);
|
|
1212
|
+
if (permissionError) {
|
|
1213
|
+
return permissionError;
|
|
1214
|
+
}
|
|
1215
|
+
return {
|
|
1216
|
+
status: "OK",
|
|
1217
|
+
...await updateUserData(session.getUserId(), {
|
|
1218
|
+
[field]: payload.value
|
|
1219
|
+
})
|
|
1220
|
+
};
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
function requireSession(session) {
|
|
1224
|
+
if (!session) {
|
|
1225
|
+
throw new Error("Session not found");
|
|
1226
|
+
}
|
|
1227
|
+
return session;
|
|
1228
|
+
}
|
|
1229
|
+
async function getJsonBody(req) {
|
|
1230
|
+
return req.getJSONBody();
|
|
1231
|
+
}
|
|
1232
|
+
function parseGuestBody(value) {
|
|
1233
|
+
if (!isJsonRecord(value)) {
|
|
1234
|
+
return {};
|
|
1235
|
+
}
|
|
1236
|
+
return {
|
|
1237
|
+
authLevel: typeof value.auth_level === "string" ? value.auth_level : void 0
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
function parseUpdateUserBody(value) {
|
|
1241
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.data)) {
|
|
1242
|
+
return {};
|
|
1243
|
+
}
|
|
1244
|
+
return {
|
|
1245
|
+
data: value.data,
|
|
1246
|
+
...isJsonRecord(value.context) ? { context: value.context } : {}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function parseUpdateMetaBody(value) {
|
|
1250
|
+
if (!isJsonRecord(value) || !isJsonRecord(value.meta)) {
|
|
1251
|
+
return {};
|
|
1252
|
+
}
|
|
1253
|
+
return { meta: value.meta };
|
|
1254
|
+
}
|
|
1255
|
+
function parseUpdateFieldBody(value) {
|
|
1256
|
+
if (!isJsonRecord(value) || !hasOwn(value, "value")) {
|
|
1257
|
+
return {};
|
|
1258
|
+
}
|
|
1259
|
+
return { value: value.value };
|
|
1260
|
+
}
|
|
1261
|
+
function validateWritableFields(fields) {
|
|
1262
|
+
const readOnlyField = fields.find((field) => !canUpdateUserDataField(field));
|
|
1263
|
+
if (!readOnlyField) {
|
|
1264
|
+
return void 0;
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
status: "ERROR",
|
|
1268
|
+
code: 403,
|
|
1269
|
+
message: `field is not writable: ${readOnlyField}`
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
function missingFieldResponse() {
|
|
1273
|
+
return {
|
|
1274
|
+
status: "ERROR",
|
|
1275
|
+
code: 400,
|
|
1276
|
+
message: "field is required"
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
function isJsonRecord(value) {
|
|
1280
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1281
|
+
}
|
|
1282
|
+
function hasOwn(value, key) {
|
|
1283
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
1284
|
+
}
|
|
1285
|
+
function getErrorMessage(error) {
|
|
1286
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
1287
|
+
}
|
|
828
1288
|
function mapRowndUserToSuperTokens(rowndUser) {
|
|
829
1289
|
const loginMethods = [];
|
|
830
1290
|
const rowndUserData = rowndUser.data || {};
|
|
@@ -863,34 +1323,26 @@ function mapRowndUserToSuperTokens(rowndUser) {
|
|
|
863
1323
|
isVerified: !!rowndUserVerifiedData.phone_number
|
|
864
1324
|
});
|
|
865
1325
|
}
|
|
866
|
-
if (rowndUserData.email &&
|
|
1326
|
+
if (rowndUserData.email && !rowndUserData.google_id && !rowndUserData.apple_id) {
|
|
867
1327
|
loginMethods.push({
|
|
868
1328
|
recipeId: "passwordless",
|
|
869
1329
|
email: rowndUserData.email,
|
|
870
1330
|
isVerified: !!rowndUserVerifiedData.email
|
|
871
1331
|
});
|
|
872
1332
|
}
|
|
1333
|
+
let authLevel = rowndUser.auth_level;
|
|
873
1334
|
if (loginMethods.length === 0) {
|
|
874
|
-
|
|
1335
|
+
const thirdPartyId = authLevel === GUEST_AUTH_METHOD_ID ? GUEST_AUTH_METHOD_ID : ANONYMOUS_AUTH_METHOD_ID;
|
|
1336
|
+
if (!authLevel) authLevel = thirdPartyId;
|
|
1337
|
+
loginMethods.push({
|
|
1338
|
+
recipeId: "thirdparty",
|
|
1339
|
+
thirdPartyId,
|
|
1340
|
+
thirdPartyUserId: rowndUserData.user_id,
|
|
1341
|
+
email: `${rowndUserData.user_id}@anonymous.local`,
|
|
1342
|
+
isVerified: false
|
|
1343
|
+
});
|
|
875
1344
|
}
|
|
876
|
-
const
|
|
877
|
-
const rowndUserAttributes = rowndUser.attributes || {};
|
|
878
|
-
const userMetadata = {
|
|
879
|
-
data: {
|
|
880
|
-
...rowndUserData
|
|
881
|
-
},
|
|
882
|
-
meta: {
|
|
883
|
-
...rowndUserMeta
|
|
884
|
-
},
|
|
885
|
-
verified_data: {
|
|
886
|
-
...rowndUserVerifiedData
|
|
887
|
-
},
|
|
888
|
-
attributes: {
|
|
889
|
-
...rowndUserAttributes
|
|
890
|
-
},
|
|
891
|
-
rownd_migrated: true,
|
|
892
|
-
rownd_user_id: rowndUserData.user_id
|
|
893
|
-
};
|
|
1345
|
+
const userMetadata = buildRowndUserMetadata(rowndUser);
|
|
894
1346
|
return {
|
|
895
1347
|
externalUserId: rowndUserData.user_id,
|
|
896
1348
|
loginMethods,
|
|
@@ -936,17 +1388,961 @@ async function fetchRowndUserInfo(userId) {
|
|
|
936
1388
|
}
|
|
937
1389
|
return rowndUser;
|
|
938
1390
|
}
|
|
1391
|
+
var IDENTITY_USER_DATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1392
|
+
"user_id",
|
|
1393
|
+
"email",
|
|
1394
|
+
"phone_number",
|
|
1395
|
+
"google_id",
|
|
1396
|
+
"apple_id"
|
|
1397
|
+
]);
|
|
1398
|
+
var INTERNAL_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
1399
|
+
"original_rownd_user",
|
|
1400
|
+
"rownd_pending_verification"
|
|
1401
|
+
]);
|
|
1402
|
+
function isIdentityField(field) {
|
|
1403
|
+
return IDENTITY_USER_DATA_FIELDS.has(field);
|
|
1404
|
+
}
|
|
1405
|
+
function isInternalMetadataField(field) {
|
|
1406
|
+
return INTERNAL_METADATA_FIELDS.has(field);
|
|
1407
|
+
}
|
|
1408
|
+
function getStringList(value) {
|
|
1409
|
+
if (Array.isArray(value)) {
|
|
1410
|
+
return value.filter((entry) => typeof entry === "string");
|
|
1411
|
+
}
|
|
1412
|
+
return typeof value === "string" ? [value] : [];
|
|
1413
|
+
}
|
|
1414
|
+
function getRequestedAppVariantIdFromRequest(req) {
|
|
1415
|
+
return req.getKeyValueFromQuery("app_variant_id");
|
|
1416
|
+
}
|
|
1417
|
+
function getRequestedDisplayContextFromRequest(req) {
|
|
1418
|
+
const displayContext = req.getKeyValueFromQuery("rownd_display_context");
|
|
1419
|
+
return displayContext === "browser" || displayContext === "mobile_app" || displayContext === "customer_web_view" ? displayContext : void 0;
|
|
1420
|
+
}
|
|
1421
|
+
function getRequestedRedirectToPathFromRequest(req) {
|
|
1422
|
+
const redirectToPath = req.getKeyValueFromQuery("rownd_redirect_to_path");
|
|
1423
|
+
return typeof redirectToPath === "string" && redirectToPath.length > 0 ? redirectToPath : void 0;
|
|
1424
|
+
}
|
|
1425
|
+
function assertRowndAppVariantIsConfigured(appVariantId) {
|
|
1426
|
+
if (!appVariantId) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (pluginConfig?.subBrands && !pluginConfig.subBrands[appVariantId]) {
|
|
1430
|
+
throw new Error(`Unknown Rownd app variant: ${appVariantId}`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
async function recordRowndAppVariantForUser(userId, appVariantId) {
|
|
1434
|
+
if (!appVariantId) {
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
1438
|
+
const metadata = await getUserMetadata(userId);
|
|
1439
|
+
const originalRowndUser = isJsonRecord(metadata.original_rownd_user) ? metadata.original_rownd_user : {};
|
|
1440
|
+
const attributes = isJsonRecord(originalRowndUser.attributes) ? originalRowndUser.attributes : {};
|
|
1441
|
+
const appVariants = getStringList(attributes["rownd:app_variants"]);
|
|
1442
|
+
if (appVariants.includes(appVariantId)) {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
await UserMetadata2.updateUserMetadata(userId, {
|
|
1446
|
+
...metadata,
|
|
1447
|
+
original_rownd_user: {
|
|
1448
|
+
...originalRowndUser,
|
|
1449
|
+
data: isJsonRecord(originalRowndUser.data) ? originalRowndUser.data : { user_id: userId },
|
|
1450
|
+
verified_data: isJsonRecord(originalRowndUser.verified_data) ? originalRowndUser.verified_data : {},
|
|
1451
|
+
attributes: {
|
|
1452
|
+
...attributes,
|
|
1453
|
+
"rownd:app_variants": [...appVariants, appVariantId]
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
function buildRowndUserMetadata(rowndUser) {
|
|
1459
|
+
const metadata = {
|
|
1460
|
+
...rowndUser.meta || {},
|
|
1461
|
+
original_rownd_user: rowndUser
|
|
1462
|
+
};
|
|
1463
|
+
for (const [key, value] of Object.entries(rowndUser.data || {})) {
|
|
1464
|
+
if (!isIdentityField(key) && value !== void 0) {
|
|
1465
|
+
metadata[key] = value;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return metadata;
|
|
1469
|
+
}
|
|
1470
|
+
var RowndIsAnonymousClaim = new BooleanClaim({
|
|
1471
|
+
key: "is_anonymous",
|
|
1472
|
+
fetchValue: async (userId) => {
|
|
1473
|
+
const user = await SuperTokens.getUser(userId);
|
|
1474
|
+
const effectiveAuthLevel = getEffectiveAuthLevel(user);
|
|
1475
|
+
return effectiveAuthLevel === GUEST_AUTH_METHOD_ID;
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
async function buildRowndSessionClaims(userId, currentPayload = {}, appVariantId) {
|
|
1479
|
+
const user = await SuperTokens.getUser(userId);
|
|
1480
|
+
const metadata = user ? await getUserMetadata(user.id) : void 0;
|
|
1481
|
+
const originalRowndUser = metadata?.original_rownd_user;
|
|
1482
|
+
const verifiedData = originalRowndUser?.verified_data;
|
|
1483
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1484
|
+
user,
|
|
1485
|
+
originalRowndUser?.auth_level,
|
|
1486
|
+
verifiedData
|
|
1487
|
+
);
|
|
1488
|
+
const appUserId = getRowndAppUserId(userId, user, currentPayload, metadata);
|
|
1489
|
+
const isAnonymous = authLevel === GUEST_AUTH_METHOD_ID;
|
|
1490
|
+
const anonymousId = getAnonymousId(userId, user, metadata);
|
|
1491
|
+
const isVerifiedUser = authLevel !== "unverified";
|
|
1492
|
+
const audience = buildRowndAudience(currentPayload, appVariantId);
|
|
1493
|
+
const configuredClaims = buildConfiguredSessionClaims(metadata);
|
|
1494
|
+
return {
|
|
1495
|
+
...audience,
|
|
1496
|
+
...configuredClaims,
|
|
1497
|
+
app_user_id: appUserId,
|
|
1498
|
+
auth_level: authLevel,
|
|
1499
|
+
is_verified_user: isVerifiedUser,
|
|
1500
|
+
[ROWND_JWT_CLAIMS.AppUserId]: appUserId,
|
|
1501
|
+
[ROWND_JWT_CLAIMS.AuthLevel]: authLevel,
|
|
1502
|
+
[ROWND_JWT_CLAIMS.IsVerifiedUser]: isVerifiedUser,
|
|
1503
|
+
[ROWND_JWT_CLAIMS.IsAnonymous]: isAnonymous,
|
|
1504
|
+
...anonymousId ? { anonymous_id: anonymousId } : {}
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
function buildConfiguredSessionClaims(metadata) {
|
|
1508
|
+
if (!metadata) {
|
|
1509
|
+
return {};
|
|
1510
|
+
}
|
|
1511
|
+
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1512
|
+
const claims = {};
|
|
1513
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1514
|
+
if (field.include_in_session_claims !== true) {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
const value = metadata.original_rownd_user?.data?.[key] ?? metadata[key];
|
|
1518
|
+
if (value !== void 0) {
|
|
1519
|
+
claims[field.session_claim_name || key] = value;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return claims;
|
|
1523
|
+
}
|
|
1524
|
+
function getRowndAppUserId(userId, user, currentPayload, metadata) {
|
|
1525
|
+
const originalUserId = metadata?.original_rownd_user?.data?.user_id;
|
|
1526
|
+
if (typeof originalUserId === "string") {
|
|
1527
|
+
return originalUserId;
|
|
1528
|
+
}
|
|
1529
|
+
if (typeof currentPayload.app_user_id === "string") {
|
|
1530
|
+
return currentPayload.app_user_id;
|
|
1531
|
+
}
|
|
1532
|
+
return user?.id || userId;
|
|
1533
|
+
}
|
|
1534
|
+
function buildRowndAudience(currentPayload, appVariantId) {
|
|
1535
|
+
const audience = getStringList(currentPayload.aud);
|
|
1536
|
+
const appId = pluginConfig?.appConfig?.id;
|
|
1537
|
+
if (appId) {
|
|
1538
|
+
audience.push(`app:${appId}`);
|
|
1539
|
+
}
|
|
1540
|
+
if (appVariantId) {
|
|
1541
|
+
audience.push(`app_variant:${appVariantId}`);
|
|
1542
|
+
}
|
|
1543
|
+
return audience.length > 0 ? { aud: [...new Set(audience)] } : {};
|
|
1544
|
+
}
|
|
1545
|
+
function getAnonymousId(userId, user, metadata) {
|
|
1546
|
+
const originalAnonymousId = metadata?.original_rownd_user?.data?.anonymous_id;
|
|
1547
|
+
if (typeof originalAnonymousId === "string") {
|
|
1548
|
+
return originalAnonymousId;
|
|
1549
|
+
}
|
|
1550
|
+
const anonymousMethod = user?.loginMethods.find((loginMethod) => {
|
|
1551
|
+
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1552
|
+
});
|
|
1553
|
+
const thirdPartyUserId = anonymousMethod ? getThirdPartyUserId(anonymousMethod) : void 0;
|
|
1554
|
+
return thirdPartyUserId || (hasAnonymousLoginMethod(user) ? `anon_${userId}` : void 0);
|
|
1555
|
+
}
|
|
1556
|
+
async function shouldLinkRowndAccounts(input) {
|
|
1557
|
+
const [newAccountInfo, , session] = input;
|
|
1558
|
+
if (!session) {
|
|
1559
|
+
return void 0;
|
|
1560
|
+
}
|
|
1561
|
+
const currentUser = await SuperTokens.getUser(session.getUserId());
|
|
1562
|
+
if (hasOnlyGuestLoginMethods(currentUser)) {
|
|
1563
|
+
return {
|
|
1564
|
+
shouldAutomaticallyLink: true,
|
|
1565
|
+
shouldRequireVerification: false
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
if (!currentUser || isGuestAccountInfo(newAccountInfo)) {
|
|
1569
|
+
return void 0;
|
|
1570
|
+
}
|
|
1571
|
+
if (doesAccountInfoMatchAuthMethod(currentUser, newAccountInfo)) {
|
|
1572
|
+
return {
|
|
1573
|
+
shouldAutomaticallyLink: true,
|
|
1574
|
+
shouldRequireVerification: true
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
return void 0;
|
|
1578
|
+
}
|
|
1579
|
+
async function getUserMetadata(userId) {
|
|
1580
|
+
const metadata = await UserMetadata2.getUserMetadata(userId);
|
|
1581
|
+
return metadata.metadata || {};
|
|
1582
|
+
}
|
|
1583
|
+
function getPendingVerifications(metadata) {
|
|
1584
|
+
const pendingVerification = metadata.rownd_pending_verification;
|
|
1585
|
+
if (Array.isArray(pendingVerification)) {
|
|
1586
|
+
return pendingVerification.filter(isPendingVerification);
|
|
1587
|
+
}
|
|
1588
|
+
return [];
|
|
1589
|
+
}
|
|
1590
|
+
function isPendingVerification(value) {
|
|
1591
|
+
return isRecord(value) && typeof value.id === "string" && typeof value.field === "string" && typeof value.value === "string" && typeof value.created_at === "string";
|
|
1592
|
+
}
|
|
1593
|
+
async function getUserById(userId) {
|
|
1594
|
+
const metadata = await getUserMetadata(userId);
|
|
1595
|
+
const stUser = await SuperTokens.getUser(userId);
|
|
1596
|
+
if (!stUser) {
|
|
1597
|
+
throw new RowndPluginError("ROWND_USER_NOT_FOUND");
|
|
1598
|
+
}
|
|
1599
|
+
const originalRowndUser = metadata.original_rownd_user;
|
|
1600
|
+
const rowndUser = originalRowndUser?.data?.user_id || userId;
|
|
1601
|
+
const state = originalRowndUser?.state || "enabled";
|
|
1602
|
+
const dataFieldKeys = /* @__PURE__ */ new Set();
|
|
1603
|
+
const data = {
|
|
1604
|
+
user_id: userId
|
|
1605
|
+
};
|
|
1606
|
+
for (const [key, value] of Object.entries(originalRowndUser?.data || {})) {
|
|
1607
|
+
if (!isIdentityField(key)) {
|
|
1608
|
+
data[key] = value;
|
|
1609
|
+
dataFieldKeys.add(key);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1613
|
+
for (const key of Object.keys(schema)) {
|
|
1614
|
+
dataFieldKeys.add(key);
|
|
1615
|
+
if (!isInternalMetadataField(key) && !isIdentityField(key) && metadata[key] !== void 0) {
|
|
1616
|
+
data[key] = metadata[key];
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
const verifiedData = {
|
|
1620
|
+
...originalRowndUser?.verified_data || {}
|
|
1621
|
+
};
|
|
1622
|
+
let lastUsedAt = stUser.timeJoined;
|
|
1623
|
+
for (const method of stUser.loginMethods) {
|
|
1624
|
+
if (typeof method.lastUsed === "number" && method.lastUsed > lastUsedAt) {
|
|
1625
|
+
lastUsedAt = method.lastUsed;
|
|
1626
|
+
}
|
|
1627
|
+
if (method.recipeId === "passwordless") {
|
|
1628
|
+
if (method.email) {
|
|
1629
|
+
verifiedData.email = method.email;
|
|
1630
|
+
if (data.email === void 0) data.email = method.email;
|
|
1631
|
+
}
|
|
1632
|
+
if (method.phoneNumber) {
|
|
1633
|
+
verifiedData.phone_number = method.phoneNumber;
|
|
1634
|
+
if (data.phone_number === void 0)
|
|
1635
|
+
data.phone_number = method.phoneNumber;
|
|
1636
|
+
}
|
|
1637
|
+
} else if (method.recipeId === "thirdparty") {
|
|
1638
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1639
|
+
const thirdPartyUserId = getThirdPartyUserId(method);
|
|
1640
|
+
if (method.verified && method.email) {
|
|
1641
|
+
verifiedData.email = method.email;
|
|
1642
|
+
}
|
|
1643
|
+
if (method.email && data.email === void 0) {
|
|
1644
|
+
data.email = method.email;
|
|
1645
|
+
}
|
|
1646
|
+
if (thirdPartyId === "google" && thirdPartyUserId) {
|
|
1647
|
+
data.google_id = thirdPartyUserId;
|
|
1648
|
+
verifiedData.google_id = thirdPartyUserId;
|
|
1649
|
+
}
|
|
1650
|
+
if (thirdPartyId === "apple" && thirdPartyUserId) {
|
|
1651
|
+
data.apple_id = thirdPartyUserId;
|
|
1652
|
+
verifiedData.apple_id = thirdPartyUserId;
|
|
1653
|
+
}
|
|
1654
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1655
|
+
if (method.email && data.email === void 0) {
|
|
1656
|
+
data.email = method.email;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (verifiedData.email === true && typeof data.email === "string") {
|
|
1661
|
+
verifiedData.email = data.email;
|
|
1662
|
+
}
|
|
1663
|
+
if (verifiedData.phone_number === true && typeof data.phone_number === "string") {
|
|
1664
|
+
verifiedData.phone_number = data.phone_number;
|
|
1665
|
+
}
|
|
1666
|
+
const anonymousId = getAnonymousId(stUser.id, stUser, metadata);
|
|
1667
|
+
if (anonymousId && data.anonymous_id === void 0) {
|
|
1668
|
+
data.anonymous_id = anonymousId;
|
|
1669
|
+
}
|
|
1670
|
+
const authLevel = getEffectiveAuthLevel(
|
|
1671
|
+
stUser,
|
|
1672
|
+
originalRowndUser?.auth_level,
|
|
1673
|
+
verifiedData
|
|
1674
|
+
);
|
|
1675
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
1676
|
+
if (data[key] === void 0 && field.type === "string") {
|
|
1677
|
+
data[key] = "";
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
const mapMethod = (method) => {
|
|
1681
|
+
if (method.recipeId === "thirdparty") {
|
|
1682
|
+
if (getThirdPartyId(method) === "google") return "google";
|
|
1683
|
+
if (getThirdPartyId(method) === "apple") return "apple";
|
|
1684
|
+
} else if (method.recipeId === "passwordless") {
|
|
1685
|
+
if (method.email) return "email";
|
|
1686
|
+
if (method.phoneNumber) return "phone";
|
|
1687
|
+
} else if (method.recipeId === "emailpassword") {
|
|
1688
|
+
return "email";
|
|
1689
|
+
}
|
|
1690
|
+
return "email";
|
|
1691
|
+
};
|
|
1692
|
+
const sortedByJoined = [...stUser.loginMethods].sort(
|
|
1693
|
+
(a, b) => a.timeJoined - b.timeJoined
|
|
1694
|
+
);
|
|
1695
|
+
const sortedByLastUsed = [
|
|
1696
|
+
...stUser.loginMethods
|
|
1697
|
+
].sort((a, b) => (b.lastUsed || b.timeJoined) - (a.lastUsed || a.timeJoined));
|
|
1698
|
+
const firstMethod = sortedByJoined[0];
|
|
1699
|
+
const lastMethod = sortedByLastUsed[0];
|
|
1700
|
+
const metadataMeta = Object.fromEntries(
|
|
1701
|
+
Object.entries(metadata).filter(
|
|
1702
|
+
([key]) => !isInternalMetadataField(key) && !dataFieldKeys.has(key)
|
|
1703
|
+
)
|
|
1704
|
+
);
|
|
1705
|
+
const meta = {
|
|
1706
|
+
...metadataMeta,
|
|
1707
|
+
created: new Date(stUser.timeJoined).toISOString(),
|
|
1708
|
+
first_sign_in: new Date(stUser.timeJoined).toISOString(),
|
|
1709
|
+
last_sign_in: new Date(lastUsedAt).toISOString(),
|
|
1710
|
+
last_active: new Date(lastUsedAt).toISOString(),
|
|
1711
|
+
first_sign_in_method: firstMethod ? mapMethod(firstMethod) : "email",
|
|
1712
|
+
last_sign_in_method: lastMethod ? mapMethod(lastMethod) : "email"
|
|
1713
|
+
};
|
|
1714
|
+
return {
|
|
1715
|
+
rownd_user: rowndUser,
|
|
1716
|
+
data,
|
|
1717
|
+
meta,
|
|
1718
|
+
verified_data: verifiedData,
|
|
1719
|
+
state,
|
|
1720
|
+
auth_level: authLevel,
|
|
1721
|
+
redacted: [],
|
|
1722
|
+
groups: originalRowndUser?.groups || [],
|
|
1723
|
+
attributes: originalRowndUser?.attributes || {}
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
async function updateUserData(userId, inputData) {
|
|
1727
|
+
const metadata = await getUserMetadata(userId);
|
|
1728
|
+
const updatedMetadata = {
|
|
1729
|
+
...metadata,
|
|
1730
|
+
...inputData
|
|
1731
|
+
};
|
|
1732
|
+
await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
|
|
1733
|
+
return getUserById(userId);
|
|
1734
|
+
}
|
|
1735
|
+
async function startPendingEmailVerification(input) {
|
|
1736
|
+
const metadata = await getUserMetadata(input.userId);
|
|
1737
|
+
const currentEmail = (await getUserById(input.userId)).data.email;
|
|
1738
|
+
if (currentEmail === input.email) {
|
|
1739
|
+
return getUserById(input.userId);
|
|
1740
|
+
}
|
|
1741
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1742
|
+
const pendingEmailVerifications = pendingVerifications.filter(
|
|
1743
|
+
(pendingVerification2) => pendingVerification2.field === "email"
|
|
1744
|
+
);
|
|
1745
|
+
for (const pendingVerification2 of pendingEmailVerifications) {
|
|
1746
|
+
await EmailVerification.revokeEmailVerificationTokens(
|
|
1747
|
+
input.tenantId,
|
|
1748
|
+
input.recipeUserId,
|
|
1749
|
+
pendingVerification2.value,
|
|
1750
|
+
input.userContext
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
const pendingVerification = {
|
|
1754
|
+
id: input.pendingVerificationId,
|
|
1755
|
+
field: "email",
|
|
1756
|
+
value: input.email,
|
|
1757
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1758
|
+
};
|
|
1759
|
+
await UserMetadata2.updateUserMetadata(input.userId, {
|
|
1760
|
+
...metadata,
|
|
1761
|
+
rownd_pending_verification: [
|
|
1762
|
+
...pendingVerifications.filter(
|
|
1763
|
+
(pendingVerification2) => pendingVerification2.field !== "email"
|
|
1764
|
+
),
|
|
1765
|
+
pendingVerification
|
|
1766
|
+
]
|
|
1767
|
+
});
|
|
1768
|
+
const response = await EmailVerification.sendEmailVerificationEmail(
|
|
1769
|
+
input.tenantId,
|
|
1770
|
+
input.userId,
|
|
1771
|
+
input.recipeUserId,
|
|
1772
|
+
input.email,
|
|
1773
|
+
{
|
|
1774
|
+
...input.userContext,
|
|
1775
|
+
rowndPendingVerificationId: pendingVerification.id
|
|
1776
|
+
}
|
|
1777
|
+
);
|
|
1778
|
+
if (response.status === "EMAIL_ALREADY_VERIFIED_ERROR") {
|
|
1779
|
+
await completePendingEmailVerification({
|
|
1780
|
+
recipeUserId: input.recipeUserId,
|
|
1781
|
+
email: input.email,
|
|
1782
|
+
userContext: input.userContext
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
return getUserById(input.userId);
|
|
1786
|
+
}
|
|
1787
|
+
async function completePendingEmailVerification(input) {
|
|
1788
|
+
const user = await SuperTokens.getUser(
|
|
1789
|
+
input.recipeUserId.getAsString(),
|
|
1790
|
+
input.userContext
|
|
1791
|
+
);
|
|
1792
|
+
const userId = user?.id ?? input.recipeUserId.getAsString();
|
|
1793
|
+
const metadata = await getUserMetadata(userId);
|
|
1794
|
+
const pendingVerifications = getPendingVerifications(metadata);
|
|
1795
|
+
const pendingVerification = pendingVerifications.find(
|
|
1796
|
+
(pendingVerification2) => pendingVerification2.field === "email" && pendingVerification2.value === input.email
|
|
1797
|
+
);
|
|
1798
|
+
if (!pendingVerification) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
let metadataUserId = userId;
|
|
1802
|
+
const passwordlessEmailMethod = getPasswordlessEmailLoginMethod(user);
|
|
1803
|
+
if (passwordlessEmailMethod) {
|
|
1804
|
+
const updateResult = await Passwordless.updateUser({
|
|
1805
|
+
recipeUserId: passwordlessEmailMethod.recipeUserId,
|
|
1806
|
+
email: input.email,
|
|
1807
|
+
userContext: input.userContext
|
|
1808
|
+
});
|
|
1809
|
+
if (updateResult.status !== "OK") {
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
`Failed to update verified email method: ${updateResult.status}`
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
} else if (hasOnlyGuestLoginMethods(user)) {
|
|
1815
|
+
const isPasswordlessSignUpAllowed = await AccountLinking.isSignUpAllowed(
|
|
1816
|
+
PUBLIC_TENANT_ID,
|
|
1817
|
+
{
|
|
1818
|
+
recipeId: "passwordless",
|
|
1819
|
+
email: input.email
|
|
1820
|
+
},
|
|
1821
|
+
true,
|
|
1822
|
+
void 0,
|
|
1823
|
+
input.userContext
|
|
1824
|
+
);
|
|
1825
|
+
if (!isPasswordlessSignUpAllowed) {
|
|
1826
|
+
throw new Error("Passwordless sign up is not allowed for this email");
|
|
1827
|
+
}
|
|
1828
|
+
const passwordlessUser = await Passwordless.signInUp({
|
|
1829
|
+
email: input.email,
|
|
1830
|
+
tenantId: PUBLIC_TENANT_ID,
|
|
1831
|
+
userContext: input.userContext
|
|
1832
|
+
});
|
|
1833
|
+
const primaryUserResult = await AccountLinking.createPrimaryUser(
|
|
1834
|
+
passwordlessUser.recipeUserId,
|
|
1835
|
+
input.userContext
|
|
1836
|
+
);
|
|
1837
|
+
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;
|
|
1838
|
+
if (userId !== primaryUserId) {
|
|
1839
|
+
const linkResult = await AccountLinking.linkAccounts(
|
|
1840
|
+
input.recipeUserId,
|
|
1841
|
+
primaryUserId,
|
|
1842
|
+
input.userContext
|
|
1843
|
+
);
|
|
1844
|
+
if (linkResult.status !== "OK") {
|
|
1845
|
+
throw new Error(
|
|
1846
|
+
`Failed to link verified email method: ${linkResult.status}`
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
metadataUserId = primaryUserId;
|
|
1851
|
+
}
|
|
1852
|
+
const updatedMetadata = {
|
|
1853
|
+
...metadata,
|
|
1854
|
+
...metadata.original_rownd_user ? {
|
|
1855
|
+
original_rownd_user: {
|
|
1856
|
+
...metadata.original_rownd_user,
|
|
1857
|
+
data: {
|
|
1858
|
+
...metadata.original_rownd_user.data,
|
|
1859
|
+
email: input.email
|
|
1860
|
+
},
|
|
1861
|
+
verified_data: {
|
|
1862
|
+
...metadata.original_rownd_user.verified_data,
|
|
1863
|
+
email: input.email
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
} : {},
|
|
1867
|
+
rownd_pending_verification: pendingVerifications.filter(
|
|
1868
|
+
(verification) => verification !== pendingVerification
|
|
1869
|
+
)
|
|
1870
|
+
};
|
|
1871
|
+
await UserMetadata2.updateUserMetadata(metadataUserId, updatedMetadata);
|
|
1872
|
+
}
|
|
1873
|
+
async function updateUserMetadata(userId, inputMeta) {
|
|
1874
|
+
const metadata = await getUserMetadata(userId);
|
|
1875
|
+
const updatedMetadata = {
|
|
1876
|
+
...metadata,
|
|
1877
|
+
...inputMeta
|
|
1878
|
+
};
|
|
1879
|
+
await UserMetadata2.updateUserMetadata(userId, updatedMetadata);
|
|
1880
|
+
return {
|
|
1881
|
+
id: userId,
|
|
1882
|
+
meta: Object.fromEntries(
|
|
1883
|
+
Object.entries(updatedMetadata).filter(
|
|
1884
|
+
([key]) => !isInternalMetadataField(key)
|
|
1885
|
+
)
|
|
1886
|
+
)
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
function getThirdPartyId(method) {
|
|
1890
|
+
return method.thirdPartyId || method.thirdParty?.id;
|
|
1891
|
+
}
|
|
1892
|
+
function getThirdPartyUserId(method) {
|
|
1893
|
+
return method.thirdPartyUserId || method.thirdParty?.userId;
|
|
1894
|
+
}
|
|
1895
|
+
function getGuestAuthLevel(user) {
|
|
1896
|
+
const guestMethod = user?.loginMethods.find(isGuestLoginMethod);
|
|
1897
|
+
return guestMethod ? GUEST_AUTH_METHOD_ID : void 0;
|
|
1898
|
+
}
|
|
1899
|
+
function hasAnonymousLoginMethod(user) {
|
|
1900
|
+
return !!user?.loginMethods.some((loginMethod) => {
|
|
1901
|
+
return loginMethod.recipeId === "thirdparty" && getThirdPartyId(loginMethod) === ANONYMOUS_AUTH_METHOD_ID;
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
function getPasswordlessEmailLoginMethod(user) {
|
|
1905
|
+
return user?.loginMethods.find((method) => {
|
|
1906
|
+
return method.recipeId === "passwordless" && !!method.email;
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
function hasOnlyGuestLoginMethods(user) {
|
|
1910
|
+
return !!user?.loginMethods.length && user.loginMethods.every(isGuestLoginMethod);
|
|
1911
|
+
}
|
|
1912
|
+
function isGuestLoginMethod(method) {
|
|
1913
|
+
const thirdPartyId = getThirdPartyId(method);
|
|
1914
|
+
return method.recipeId === "thirdparty" && (thirdPartyId === GUEST_AUTH_METHOD_ID || thirdPartyId === ANONYMOUS_AUTH_METHOD_ID);
|
|
1915
|
+
}
|
|
1916
|
+
function isGuestAccountInfo(input) {
|
|
1917
|
+
return input?.recipeId === "thirdparty" && (input.thirdParty?.id === GUEST_AUTH_METHOD_ID || input.thirdParty?.id === ANONYMOUS_AUTH_METHOD_ID);
|
|
1918
|
+
}
|
|
1919
|
+
function doesAccountInfoMatchAuthMethod(user, accountInfo) {
|
|
1920
|
+
if (!user) {
|
|
1921
|
+
return false;
|
|
1922
|
+
}
|
|
1923
|
+
const normalizedEmail = accountInfo.email?.toLowerCase();
|
|
1924
|
+
if (normalizedEmail) {
|
|
1925
|
+
return user.loginMethods.some((method) => {
|
|
1926
|
+
if (isGuestLoginMethod(method) || !method.email) {
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
return method.email.toLowerCase() === normalizedEmail;
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
if (accountInfo.phoneNumber) {
|
|
1933
|
+
return user.loginMethods.some((method) => {
|
|
1934
|
+
return !isGuestLoginMethod(method) && method.phoneNumber === accountInfo.phoneNumber;
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
return false;
|
|
1938
|
+
}
|
|
1939
|
+
function hasVerifiedRealLoginMethod(user) {
|
|
1940
|
+
return !!user?.loginMethods.some((method) => {
|
|
1941
|
+
if (isGuestLoginMethod(method)) {
|
|
1942
|
+
return false;
|
|
1943
|
+
}
|
|
1944
|
+
if (method.recipeId === "passwordless") {
|
|
1945
|
+
return !!(method.email || method.phoneNumber);
|
|
1946
|
+
}
|
|
1947
|
+
if (method.recipeId === "thirdparty") {
|
|
1948
|
+
return !!getThirdPartyUserId(method) && method.verified === true;
|
|
1949
|
+
}
|
|
1950
|
+
if (method.recipeId === "emailpassword") {
|
|
1951
|
+
return !!method.email && method.verified === true;
|
|
1952
|
+
}
|
|
1953
|
+
return method.verified === true;
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
function getEffectiveAuthLevel(user, originalAuthLevel, verifiedData) {
|
|
1957
|
+
if (hasVerifiedRealLoginMethod(user)) {
|
|
1958
|
+
return "verified";
|
|
1959
|
+
}
|
|
1960
|
+
return getGuestAuthLevel(user) || originalAuthLevel || (verifiedData && Object.keys(verifiedData).length > 0 ? "verified" : "unverified");
|
|
1961
|
+
}
|
|
1962
|
+
function canUpdateUserDataField(field) {
|
|
1963
|
+
const schema = pluginConfig?.schema || DEFAULT_ROWND_SCHEMA;
|
|
1964
|
+
const schemaField = schema[field];
|
|
1965
|
+
if (!schemaField) {
|
|
1966
|
+
return false;
|
|
1967
|
+
}
|
|
1968
|
+
const ownedBy = field === "google_id" || field === "apple_id" ? "app" : schemaField.owned_by || "user";
|
|
1969
|
+
return ownedBy !== "app" && schemaField.read_only !== true;
|
|
1970
|
+
}
|
|
1971
|
+
var BUILTIN_SIGN_IN_METHOD_KEYS = [
|
|
1972
|
+
"email",
|
|
1973
|
+
"phone",
|
|
1974
|
+
"google",
|
|
1975
|
+
"apple",
|
|
1976
|
+
"anonymous"
|
|
1977
|
+
];
|
|
1978
|
+
function normalizeSchemaField(key, field) {
|
|
1979
|
+
let ownedBy = field.owned_by;
|
|
1980
|
+
if (key === "google_id" || key === "apple_id") {
|
|
1981
|
+
ownedBy = "app";
|
|
1982
|
+
} else if (!ownedBy) {
|
|
1983
|
+
ownedBy = "user";
|
|
1984
|
+
}
|
|
1985
|
+
return {
|
|
1986
|
+
display_name: field.display_name,
|
|
1987
|
+
type: field.type,
|
|
1988
|
+
owned_by: ownedBy,
|
|
1989
|
+
user_visible: field.user_visible,
|
|
1990
|
+
read_only: field.read_only ?? ownedBy === "app",
|
|
1991
|
+
show_empty: field.show_empty ?? false
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
var DEFAULT_PRIMARY_COLOR = "#5b5bd6";
|
|
1995
|
+
function buildSignInMethodsConfig(methodsArray) {
|
|
1996
|
+
const methods = (methodsArray ?? []).reduce(
|
|
1997
|
+
(acc, curr) => {
|
|
1998
|
+
acc[curr.method] = curr;
|
|
1999
|
+
return acc;
|
|
2000
|
+
},
|
|
2001
|
+
{}
|
|
2002
|
+
);
|
|
2003
|
+
const customProviders = Object.fromEntries(
|
|
2004
|
+
Object.entries(methods).filter(([key]) => !BUILTIN_SIGN_IN_METHOD_KEYS.includes(key)).map(([key, val]) => {
|
|
2005
|
+
return val ? [
|
|
2006
|
+
key,
|
|
2007
|
+
{
|
|
2008
|
+
enabled: true,
|
|
2009
|
+
display_name: getStringMethodProperty(val, "displayName") ?? key,
|
|
2010
|
+
icon_light_url: getStringMethodProperty(val, "iconLightUrl"),
|
|
2011
|
+
icon_dark_url: getStringMethodProperty(val, "iconDarkUrl")
|
|
2012
|
+
}
|
|
2013
|
+
] : [key, void 0];
|
|
2014
|
+
}).filter(([, v]) => v !== void 0)
|
|
2015
|
+
);
|
|
2016
|
+
const googleMethod = methods.google;
|
|
2017
|
+
const appleMethod = methods.apple;
|
|
2018
|
+
const anonymousMethod = methods.anonymous;
|
|
2019
|
+
const anonymousType = getAnonymousType(anonymousMethod);
|
|
2020
|
+
const googleOneTap = getOneTapConfig(googleMethod);
|
|
2021
|
+
return {
|
|
2022
|
+
email: { enabled: !!methods.email },
|
|
2023
|
+
phone: { enabled: !!methods.phone },
|
|
2024
|
+
google: {
|
|
2025
|
+
enabled: !!googleMethod,
|
|
2026
|
+
client_id: getStringMethodProperty(googleMethod, "clientId") ?? "",
|
|
2027
|
+
ios_client_id: getStringMethodProperty(googleMethod, "iosClientId") ?? "",
|
|
2028
|
+
scopes: getStringArrayMethodProperty(googleMethod, "scopes") ?? [],
|
|
2029
|
+
...getSignInFasterWithGoogle(googleMethod) ? { sign_in_faster_with_google: getSignInFasterWithGoogle(googleMethod) } : {},
|
|
2030
|
+
one_tap: {
|
|
2031
|
+
browser: {
|
|
2032
|
+
auto_prompt: googleOneTap?.browser?.autoPrompt ?? false,
|
|
2033
|
+
delay: googleOneTap?.browser?.delay ?? 7e3
|
|
2034
|
+
},
|
|
2035
|
+
mobile_app: {
|
|
2036
|
+
auto_prompt: googleOneTap?.mobileApp?.autoPrompt ?? false,
|
|
2037
|
+
delay: googleOneTap?.mobileApp?.delay ?? 7e3
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
},
|
|
2041
|
+
apple: {
|
|
2042
|
+
enabled: !!appleMethod,
|
|
2043
|
+
client_id: getStringMethodProperty(appleMethod, "clientId") ?? ""
|
|
2044
|
+
},
|
|
2045
|
+
anonymous: {
|
|
2046
|
+
enabled: !!anonymousMethod && anonymousType !== "instant",
|
|
2047
|
+
...anonymousMethod && anonymousType !== "instant" ? { type: anonymousType } : {},
|
|
2048
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "displayName") !== void 0 ? {
|
|
2049
|
+
display_name: getStringMethodProperty(
|
|
2050
|
+
anonymousMethod,
|
|
2051
|
+
"displayName"
|
|
2052
|
+
)
|
|
2053
|
+
} : {},
|
|
2054
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconLightUrl") !== void 0 ? {
|
|
2055
|
+
icon_light_url: getStringMethodProperty(
|
|
2056
|
+
anonymousMethod,
|
|
2057
|
+
"iconLightUrl"
|
|
2058
|
+
)
|
|
2059
|
+
} : {},
|
|
2060
|
+
...anonymousType !== "instant" && getStringMethodProperty(anonymousMethod, "iconDarkUrl") !== void 0 ? {
|
|
2061
|
+
icon_dark_url: getStringMethodProperty(
|
|
2062
|
+
anonymousMethod,
|
|
2063
|
+
"iconDarkUrl"
|
|
2064
|
+
)
|
|
2065
|
+
} : {}
|
|
2066
|
+
},
|
|
2067
|
+
...customProviders
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
function isInstantAnonymousMethod(methods) {
|
|
2071
|
+
return (methods ?? []).some(
|
|
2072
|
+
(method) => method.method === "anonymous" && getAnonymousType(method) === "instant"
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
function getAnonymousType(method) {
|
|
2076
|
+
const type = getStringMethodProperty(method, "type");
|
|
2077
|
+
return type === "instant" ? "instant" : "guest";
|
|
2078
|
+
}
|
|
2079
|
+
function getSignInFasterWithGoogle(method) {
|
|
2080
|
+
const value = getStringMethodProperty(method, "signInFasterWithGoogle");
|
|
2081
|
+
return value === "enabled" || value === "disabled" ? value : void 0;
|
|
2082
|
+
}
|
|
2083
|
+
function getMethodProperty(method, property) {
|
|
2084
|
+
if (!method) {
|
|
2085
|
+
return void 0;
|
|
2086
|
+
}
|
|
2087
|
+
return method[property];
|
|
2088
|
+
}
|
|
2089
|
+
function getStringMethodProperty(method, property) {
|
|
2090
|
+
const value = getMethodProperty(method, property);
|
|
2091
|
+
return typeof value === "string" ? value : void 0;
|
|
2092
|
+
}
|
|
2093
|
+
function getStringArrayMethodProperty(method, property) {
|
|
2094
|
+
const value = getMethodProperty(method, property);
|
|
2095
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
2096
|
+
}
|
|
2097
|
+
function getOneTapConfig(method) {
|
|
2098
|
+
const oneTap = getMethodProperty(method, "oneTap");
|
|
2099
|
+
if (!isRecord(oneTap)) {
|
|
2100
|
+
return void 0;
|
|
2101
|
+
}
|
|
2102
|
+
return {
|
|
2103
|
+
browser: parseOneTapPlatform(oneTap.browser),
|
|
2104
|
+
mobileApp: parseOneTapPlatform(oneTap.mobileApp)
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
function parseOneTapPlatform(value) {
|
|
2108
|
+
if (!isRecord(value)) {
|
|
2109
|
+
return void 0;
|
|
2110
|
+
}
|
|
2111
|
+
return {
|
|
2112
|
+
autoPrompt: typeof value.autoPrompt === "boolean" ? value.autoPrompt : void 0,
|
|
2113
|
+
delay: typeof value.delay === "number" ? value.delay : void 0
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
function isRecord(value) {
|
|
2117
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2118
|
+
}
|
|
2119
|
+
function getSubBrandVariant(app) {
|
|
2120
|
+
if (isRecord(app) && isRecord(app.variant) && typeof app.variant.id === "string") {
|
|
2121
|
+
return app.variant;
|
|
2122
|
+
}
|
|
2123
|
+
return void 0;
|
|
2124
|
+
}
|
|
2125
|
+
function mergeConfigInput(base, override) {
|
|
2126
|
+
const result = { ...base };
|
|
2127
|
+
for (const [key, value] of Object.entries(override)) {
|
|
2128
|
+
if (value === void 0) {
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
const existing = result[key];
|
|
2132
|
+
result[key] = isRecord(existing) && isRecord(value) ? mergeConfigInput(existing, value) : value;
|
|
2133
|
+
}
|
|
2134
|
+
return result;
|
|
2135
|
+
}
|
|
2136
|
+
function buildAppConfig(config2, stConfig, appVariantId) {
|
|
2137
|
+
const userSchema = config2.schema ?? DEFAULT_ROWND_SCHEMA;
|
|
2138
|
+
const baseApp = config2.appConfig ?? {};
|
|
2139
|
+
const subBrand = appVariantId ? config2.subBrands?.[appVariantId] : void 0;
|
|
2140
|
+
const app = appVariantId ? subBrand && mergeConfigInput(baseApp, subBrand) : baseApp;
|
|
2141
|
+
if (!app) {
|
|
2142
|
+
return void 0;
|
|
2143
|
+
}
|
|
2144
|
+
const branding = app.branding ?? {};
|
|
2145
|
+
const auth = app.auth ?? {};
|
|
2146
|
+
const signInMethods = buildSignInMethodsConfig(app.signInMethods);
|
|
2147
|
+
const variant = getSubBrandVariant(app);
|
|
2148
|
+
const finalSchema = { ...userSchema };
|
|
2149
|
+
if (signInMethods.email.enabled && !finalSchema.email) {
|
|
2150
|
+
finalSchema.email = {
|
|
2151
|
+
display_name: "Email",
|
|
2152
|
+
type: "string",
|
|
2153
|
+
user_visible: true
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
if (signInMethods.phone.enabled && !finalSchema.phone_number) {
|
|
2157
|
+
finalSchema.phone_number = {
|
|
2158
|
+
display_name: "Phone number",
|
|
2159
|
+
type: "string",
|
|
2160
|
+
user_visible: true
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
if (signInMethods.google.enabled && !finalSchema.google_id) {
|
|
2164
|
+
finalSchema.google_id = {
|
|
2165
|
+
display_name: "Google ID",
|
|
2166
|
+
type: "string",
|
|
2167
|
+
user_visible: false
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
if (signInMethods.apple.enabled && !finalSchema.apple_id) {
|
|
2171
|
+
finalSchema.apple_id = {
|
|
2172
|
+
display_name: "Apple ID",
|
|
2173
|
+
type: "string",
|
|
2174
|
+
user_visible: false
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
return {
|
|
2178
|
+
config_type: appVariantId ? "variant" : "app",
|
|
2179
|
+
...variant ? { variant } : {},
|
|
2180
|
+
app: {
|
|
2181
|
+
id: app.id ?? "",
|
|
2182
|
+
name: app.name ?? stConfig.appInfo.appName,
|
|
2183
|
+
icon: app.icon ?? "",
|
|
2184
|
+
...app.userVerificationFields ? { user_verification_fields: app.userVerificationFields } : {},
|
|
2185
|
+
schema: Object.fromEntries(
|
|
2186
|
+
Object.entries(finalSchema).map(([key, field]) => [
|
|
2187
|
+
key,
|
|
2188
|
+
normalizeSchemaField(key, field)
|
|
2189
|
+
])
|
|
2190
|
+
),
|
|
2191
|
+
config: {
|
|
2192
|
+
...app.capabilities ? { capabilities: app.capabilities } : {},
|
|
2193
|
+
...app.web ? { web: app.web } : {},
|
|
2194
|
+
...app.bottomSheet ? { bottom_sheet: app.bottomSheet } : {},
|
|
2195
|
+
...app.profileStorageVersion ? { profile_storage_version: app.profileStorageVersion } : {},
|
|
2196
|
+
customizations: {
|
|
2197
|
+
primary_color: branding.primaryColor ?? DEFAULT_PRIMARY_COLOR,
|
|
2198
|
+
...branding.logo ? { logo: branding.logo } : {},
|
|
2199
|
+
...branding.logoDarkMode ? { logo_dark_mode: branding.logoDarkMode } : {},
|
|
2200
|
+
...branding.animations ? { animations: branding.animations } : {}
|
|
2201
|
+
},
|
|
2202
|
+
hub: {
|
|
2203
|
+
...app.allowedWebOrigins ? { allowed_web_origins: app.allowedWebOrigins } : {},
|
|
2204
|
+
customizations: {
|
|
2205
|
+
rounded_corners: branding.roundedCorners ?? true,
|
|
2206
|
+
...branding.containerBorderRadius !== void 0 ? { container_border_radius: branding.containerBorderRadius } : {},
|
|
2207
|
+
...branding.placement !== void 0 ? { placement: branding.placement } : {},
|
|
2208
|
+
...branding.hubPrimaryColor !== void 0 ? { primary_color: branding.hubPrimaryColor } : {},
|
|
2209
|
+
...branding.primaryColorDarkMode !== void 0 ? { primary_color_dark_mode: branding.primaryColorDarkMode } : {},
|
|
2210
|
+
...branding.backgroundColor !== void 0 ? { background_color: branding.backgroundColor } : {},
|
|
2211
|
+
...branding.fontFamily !== void 0 ? { font_family: branding.fontFamily } : {},
|
|
2212
|
+
...branding.hideVerificationIcons !== void 0 ? { hide_verification_icons: branding.hideVerificationIcons } : {},
|
|
2213
|
+
visual_swoops: branding.visualSwoops ?? true,
|
|
2214
|
+
blur_background: branding.blurBackground ?? true,
|
|
2215
|
+
...branding.blurBackgroundOpacity !== void 0 ? { blur_background_opacity: branding.blurBackgroundOpacity } : {},
|
|
2216
|
+
...branding.offsetX !== void 0 ? { offset_x: branding.offsetX } : {},
|
|
2217
|
+
...branding.offsetY !== void 0 ? { offset_y: branding.offsetY } : {},
|
|
2218
|
+
...branding.propertyOverrides ? { property_overrides: branding.propertyOverrides } : {},
|
|
2219
|
+
dark_mode: branding.darkMode ?? "auto"
|
|
2220
|
+
},
|
|
2221
|
+
...branding.customScripts ? { custom_scripts: branding.customScripts } : {},
|
|
2222
|
+
...branding.customStyles ? { custom_styles: branding.customStyles } : {},
|
|
2223
|
+
auth: {
|
|
2224
|
+
email: buildAuthEmailConfig(auth.email),
|
|
2225
|
+
...auth.mobile ? { mobile: buildAuthMobileConfig(auth.mobile) } : {},
|
|
2226
|
+
sign_in_methods: signInMethods,
|
|
2227
|
+
additional_fields: auth.additionalFields ?? [],
|
|
2228
|
+
...auth.rememberSignInMethod !== void 0 ? { remember_sign_in_method: auth.rememberSignInMethod } : {},
|
|
2229
|
+
...auth.useExplicitSignUpFlow !== void 0 ? { use_explicit_sign_up_flow: auth.useExplicitSignUpFlow } : {},
|
|
2230
|
+
...auth.allowUnverifiedUsers !== void 0 ? { allow_unverified_users: auth.allowUnverifiedUsers } : {},
|
|
2231
|
+
...auth.primarySignUpMethod ? { primary_sign_up_method: auth.primarySignUpMethod } : {},
|
|
2232
|
+
...auth.preferredMethod ? { preferred_method: auth.preferredMethod } : {},
|
|
2233
|
+
...auth.order ? { order: auth.order } : {},
|
|
2234
|
+
...isInstantAnonymousMethod(app.signInMethods) ? { instant_user: { enabled: true } } : {},
|
|
2235
|
+
show_app_icon: branding.showAppIcon ?? false
|
|
2236
|
+
},
|
|
2237
|
+
legal: {
|
|
2238
|
+
...app.legal?.companyName ? { company_name: app.legal.companyName } : {},
|
|
2239
|
+
...app.legal?.privacyPolicyUrl ? { privacy_policy_url: app.legal.privacyPolicyUrl } : {},
|
|
2240
|
+
...app.legal?.termsConditionsUrl ? { terms_conditions_url: app.legal.termsConditionsUrl } : {},
|
|
2241
|
+
...app.legal?.supportEmail ? { support_email: app.legal.supportEmail } : {}
|
|
2242
|
+
},
|
|
2243
|
+
custom_content: {
|
|
2244
|
+
...app.customContent?.signInModal ? {
|
|
2245
|
+
sign_in_modal: {
|
|
2246
|
+
...app.customContent.signInModal.title ? { title: app.customContent.signInModal.title } : {},
|
|
2247
|
+
...app.customContent.signInModal.subtitle ? { subtitle: app.customContent.signInModal.subtitle } : {},
|
|
2248
|
+
...app.customContent.signInModal.signInTitle ? {
|
|
2249
|
+
sign_in_title: app.customContent.signInModal.signInTitle
|
|
2250
|
+
} : {},
|
|
2251
|
+
...app.customContent.signInModal.signUpTitle ? {
|
|
2252
|
+
sign_up_title: app.customContent.signInModal.signUpTitle
|
|
2253
|
+
} : {},
|
|
2254
|
+
...app.customContent.signInModal.signInSubtitle ? {
|
|
2255
|
+
sign_in_subtitle: app.customContent.signInModal.signInSubtitle
|
|
2256
|
+
} : {},
|
|
2257
|
+
...app.customContent.signInModal.signUpSubtitle ? {
|
|
2258
|
+
sign_up_subtitle: app.customContent.signInModal.signUpSubtitle
|
|
2259
|
+
} : {},
|
|
2260
|
+
...app.customContent.signInModal.signInButton ? {
|
|
2261
|
+
sign_in_button: app.customContent.signInModal.signInButton
|
|
2262
|
+
} : {},
|
|
2263
|
+
...app.customContent.signInModal.signUpButton ? {
|
|
2264
|
+
sign_up_button: app.customContent.signInModal.signUpButton
|
|
2265
|
+
} : {}
|
|
2266
|
+
}
|
|
2267
|
+
} : {},
|
|
2268
|
+
...app.customContent?.profileModal ? { profile_modal: app.customContent.profileModal } : {},
|
|
2269
|
+
...app.customContent?.verificationModal ? {
|
|
2270
|
+
verification_modal: {
|
|
2271
|
+
...app.customContent.verificationModal.title ? { title: app.customContent.verificationModal.title } : {},
|
|
2272
|
+
...app.customContent.verificationModal.subtitle ? { subtitle: app.customContent.verificationModal.subtitle } : {}
|
|
2273
|
+
}
|
|
2274
|
+
} : {},
|
|
2275
|
+
...app.customContent?.signInFailureModal ? {
|
|
2276
|
+
sign_in_failure_modal: {
|
|
2277
|
+
failure_message: app.customContent.signInFailureModal.failureMessage
|
|
2278
|
+
}
|
|
2279
|
+
} : {},
|
|
2280
|
+
...app.customContent?.noAccountMessage ? { no_account_message: app.customContent.noAccountMessage } : {},
|
|
2281
|
+
...app.customContent?.mobile ? { mobile: app.customContent.mobile } : {}
|
|
2282
|
+
},
|
|
2283
|
+
profile: {
|
|
2284
|
+
...app.profile?.accountInformation ? { account_information: app.profile.accountInformation } : {},
|
|
2285
|
+
...app.profile?.personalInformation ? { personal_information: app.profile.personalInformation } : {},
|
|
2286
|
+
...app.profile?.preferences ? { preferences: app.profile.preferences } : {},
|
|
2287
|
+
...app.profile?.signOutButton ? { sign_out_button: app.profile.signOutButton } : {},
|
|
2288
|
+
...app.profile?.deleteAccountButton ? { delete_account_button: app.profile.deleteAccountButton } : {},
|
|
2289
|
+
...app.profile?.addSignInMethodsButton ? { add_sign_in_methods_button: app.profile.addSignInMethodsButton } : {}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
function buildAuthEmailConfig(authEmail) {
|
|
2297
|
+
return {
|
|
2298
|
+
from_address: authEmail?.fromAddress ?? "no-reply@rownd.io",
|
|
2299
|
+
image: authEmail?.image ?? "",
|
|
2300
|
+
...authEmail?.subject ? { subject: authEmail.subject } : {},
|
|
2301
|
+
...authEmail?.callToActionText ? { call_to_action_text: authEmail.callToActionText } : {},
|
|
2302
|
+
...authEmail?.verifyTemplate ? { verify_template: authEmail.verifyTemplate } : {},
|
|
2303
|
+
...authEmail?.customContent ? { custom_content: authEmail.customContent } : {},
|
|
2304
|
+
...authEmail?.customClosingContent ? { custom_closing_content: authEmail.customClosingContent } : {}
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
function buildAuthMobileConfig(authMobile) {
|
|
2308
|
+
return {
|
|
2309
|
+
...authMobile?.title ? { title: authMobile.title } : {},
|
|
2310
|
+
...authMobile?.image ? { image: authMobile.image } : {},
|
|
2311
|
+
...authMobile?.callToActionText ? { call_to_action_text: authMobile.callToActionText } : {},
|
|
2312
|
+
...authMobile?.hyperlinkText ? { hyperlink_text: authMobile.hyperlinkText } : {},
|
|
2313
|
+
...authMobile?.hyperlinkRedirectUrl ? { hyperlink_redirect_url: authMobile.hyperlinkRedirectUrl } : {},
|
|
2314
|
+
...authMobile?.customContent ? { custom_content: authMobile.customContent } : {}
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
939
2317
|
|
|
940
2318
|
// src/plugin.ts
|
|
941
2319
|
var init = createPluginInitFunction(
|
|
942
|
-
(
|
|
2320
|
+
(pluginConfig2) => {
|
|
943
2321
|
const rowndClient2 = createInstance({
|
|
944
|
-
app_key:
|
|
945
|
-
app_secret:
|
|
2322
|
+
app_key: pluginConfig2.rowndAppKey,
|
|
2323
|
+
app_secret: pluginConfig2.rowndAppSecret
|
|
946
2324
|
});
|
|
947
|
-
const telemetryClient = createClient(
|
|
2325
|
+
const telemetryClient = createClient(pluginConfig2.telemetry);
|
|
2326
|
+
let hubBootstrapParams;
|
|
2327
|
+
const addHubBootstrapParams = (input, linkKey, targetPath) => {
|
|
2328
|
+
const appVariantId = input?.userContext?.rowndAppVariantId;
|
|
2329
|
+
const displayContext = input?.userContext?.rowndDisplayContext;
|
|
2330
|
+
const redirectToPath = input?.userContext?.rowndRedirectToPath;
|
|
2331
|
+
const bootstrapParams = {
|
|
2332
|
+
appKey: pluginConfig2.rowndAppKey,
|
|
2333
|
+
...hubBootstrapParams ?? {},
|
|
2334
|
+
...typeof appVariantId === "string" ? { appVariantId } : {},
|
|
2335
|
+
...typeof displayContext === "string" ? { displayContext } : {},
|
|
2336
|
+
...typeof redirectToPath === "string" ? { redirectToPath } : {}
|
|
2337
|
+
};
|
|
2338
|
+
return {
|
|
2339
|
+
...input,
|
|
2340
|
+
[linkKey]: input[linkKey] ? rewriteLinkPath(input[linkKey], targetPath, bootstrapParams) : input[linkKey]
|
|
2341
|
+
};
|
|
2342
|
+
};
|
|
948
2343
|
setRowndClient(rowndClient2);
|
|
949
|
-
|
|
2344
|
+
setPluginConfig(pluginConfig2);
|
|
2345
|
+
if (pluginConfig2.enableDebugLogs) {
|
|
950
2346
|
enableDebugLogs();
|
|
951
2347
|
}
|
|
952
2348
|
logDebugMessage("Rownd plugin init complete");
|
|
@@ -959,104 +2355,336 @@ var init = createPluginInitFunction(
|
|
|
959
2355
|
"RowndMigrationPlugin: Session recipe is not initialized. Session migration will fail."
|
|
960
2356
|
);
|
|
961
2357
|
}
|
|
2358
|
+
if (!supertokens.isRecipeInitialized("thirdparty")) {
|
|
2359
|
+
console.warn(
|
|
2360
|
+
"RowndMigrationPlugin: ThirdParty recipe is not initialized. Guest login will fail."
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
if (!supertokens.isRecipeInitialized("emailverification")) {
|
|
2364
|
+
console.warn(
|
|
2365
|
+
"RowndMigrationPlugin: EmailVerification recipe is not initialized. Verified email profile updates will fail."
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
962
2368
|
},
|
|
963
|
-
routeHandlers(
|
|
964
|
-
const apiBasePath =
|
|
2369
|
+
routeHandlers(stConfig) {
|
|
2370
|
+
const apiBasePath = stConfig.appInfo.apiBasePath.getAsStringDangerous();
|
|
2371
|
+
hubBootstrapParams = {
|
|
2372
|
+
apiDomain: stConfig.appInfo.apiDomain.getAsStringDangerous(),
|
|
2373
|
+
apiBasePath
|
|
2374
|
+
};
|
|
2375
|
+
const routeHandlerDeps = {
|
|
2376
|
+
pluginConfig: pluginConfig2,
|
|
2377
|
+
stConfig,
|
|
2378
|
+
telemetryClient
|
|
2379
|
+
};
|
|
965
2380
|
return {
|
|
966
2381
|
status: "OK",
|
|
967
2382
|
routeHandlers: [
|
|
2383
|
+
{
|
|
2384
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/app-config`,
|
|
2385
|
+
method: "get",
|
|
2386
|
+
handler: withRequestHandler(
|
|
2387
|
+
handleGetAppConfig(routeHandlerDeps)
|
|
2388
|
+
)
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/guest`,
|
|
2392
|
+
method: "post",
|
|
2393
|
+
handler: withRequestHandler(handleGuestLogin(routeHandlerDeps))
|
|
2394
|
+
},
|
|
968
2395
|
{
|
|
969
2396
|
path: `${apiBasePath}${HANDLE_BASE_PATH}/migrate`,
|
|
970
2397
|
method: "post",
|
|
971
|
-
handler: withRequestHandler(
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
2398
|
+
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2399
|
+
},
|
|
2400
|
+
{
|
|
2401
|
+
path: `${apiBasePath}/plugin/migrate-session`,
|
|
2402
|
+
method: "post",
|
|
2403
|
+
handler: withRequestHandler(handleMigrate(routeHandlerDeps))
|
|
2404
|
+
},
|
|
2405
|
+
{
|
|
2406
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/signout`,
|
|
2407
|
+
method: "post",
|
|
2408
|
+
verifySessionOptions: {
|
|
2409
|
+
sessionRequired: true,
|
|
2410
|
+
checkDatabase: true
|
|
2411
|
+
},
|
|
2412
|
+
handler: withRequestHandler(handleSignOut())
|
|
2413
|
+
},
|
|
2414
|
+
{
|
|
2415
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2416
|
+
method: "get",
|
|
2417
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2418
|
+
handler: withRequestHandler(handleGetUser())
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2422
|
+
method: "put",
|
|
2423
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2424
|
+
handler: withRequestHandler(handleUpdateUser())
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user`,
|
|
2428
|
+
method: "delete",
|
|
2429
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2430
|
+
handler: withRequestHandler(handleDeleteUser())
|
|
2431
|
+
},
|
|
2432
|
+
{
|
|
2433
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/meta`,
|
|
2434
|
+
method: "get",
|
|
2435
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2436
|
+
handler: withRequestHandler(handleGetUserMeta())
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/meta`,
|
|
2440
|
+
method: "put",
|
|
2441
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2442
|
+
handler: withRequestHandler(handleUpdateUserMeta())
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
|
|
2446
|
+
method: "get",
|
|
2447
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2448
|
+
handler: withRequestHandler(handleGetUserField())
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
path: `${apiBasePath}${HANDLE_BASE_PATH}/user/field`,
|
|
2452
|
+
method: "put",
|
|
2453
|
+
verifySessionOptions: { sessionRequired: true },
|
|
2454
|
+
handler: withRequestHandler(handleUpdateUserField())
|
|
2455
|
+
}
|
|
2456
|
+
]
|
|
2457
|
+
};
|
|
2458
|
+
},
|
|
2459
|
+
overrideMap: {
|
|
2460
|
+
passwordless: {
|
|
2461
|
+
config: (config2) => {
|
|
2462
|
+
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
2463
|
+
const originalSmsDeliveryOverride = config2.smsDelivery?.override;
|
|
2464
|
+
return {
|
|
2465
|
+
...config2,
|
|
2466
|
+
emailDelivery: {
|
|
2467
|
+
...config2.emailDelivery,
|
|
2468
|
+
override: (originalImplementation, builder) => {
|
|
2469
|
+
const implementation = originalEmailDeliveryOverride ? originalEmailDeliveryOverride(
|
|
2470
|
+
originalImplementation,
|
|
2471
|
+
builder
|
|
2472
|
+
) : originalImplementation;
|
|
2473
|
+
return {
|
|
2474
|
+
...implementation,
|
|
2475
|
+
sendEmail: async function(input) {
|
|
2476
|
+
return implementation.sendEmail({
|
|
2477
|
+
...addHubBootstrapParams(
|
|
2478
|
+
input,
|
|
2479
|
+
"urlWithLinkCode",
|
|
2480
|
+
"account/login"
|
|
2481
|
+
)
|
|
2482
|
+
});
|
|
982
2483
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
superTokensUserId = user.id;
|
|
1004
|
-
recipeUserId = user.loginMethods[0]?.recipeUserId;
|
|
1005
|
-
logDebugMessage(
|
|
1006
|
-
`User already migrated. tenantId: ${PUBLIC_TENANT_ID}, rowndUserId: ${rowndUserId}`
|
|
1007
|
-
);
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
},
|
|
2487
|
+
smsDelivery: {
|
|
2488
|
+
...config2.smsDelivery,
|
|
2489
|
+
override: (originalImplementation, builder) => {
|
|
2490
|
+
const implementation = originalSmsDeliveryOverride ? originalSmsDeliveryOverride(
|
|
2491
|
+
originalImplementation,
|
|
2492
|
+
builder
|
|
2493
|
+
) : originalImplementation;
|
|
2494
|
+
return {
|
|
2495
|
+
...implementation,
|
|
2496
|
+
sendSms: async function(input) {
|
|
2497
|
+
return implementation.sendSms({
|
|
2498
|
+
...addHubBootstrapParams(
|
|
2499
|
+
input,
|
|
2500
|
+
"urlWithLinkCode",
|
|
2501
|
+
"account/login"
|
|
2502
|
+
)
|
|
2503
|
+
});
|
|
1008
2504
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
},
|
|
2510
|
+
apis: (originalImplementation) => ({
|
|
2511
|
+
...originalImplementation,
|
|
2512
|
+
createCodePOST: async (input) => {
|
|
2513
|
+
if (originalImplementation.createCodePOST === void 0) {
|
|
2514
|
+
throw new Error("Passwordless createCodePOST is unavailable");
|
|
2515
|
+
}
|
|
2516
|
+
const displayContext = getRequestedDisplayContextFromRequest(
|
|
2517
|
+
input.options.req
|
|
2518
|
+
);
|
|
2519
|
+
const redirectToPath = getRequestedRedirectToPathFromRequest(
|
|
2520
|
+
input.options.req
|
|
2521
|
+
);
|
|
2522
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2523
|
+
input.options.req
|
|
2524
|
+
);
|
|
2525
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2526
|
+
return originalImplementation.createCodePOST({
|
|
2527
|
+
...input,
|
|
2528
|
+
userContext: displayContext || redirectToPath || appVariantId ? {
|
|
2529
|
+
...input.userContext,
|
|
2530
|
+
...appVariantId ? { rowndAppVariantId: appVariantId } : {},
|
|
2531
|
+
...displayContext ? { rowndDisplayContext: displayContext } : {},
|
|
2532
|
+
...redirectToPath ? { rowndRedirectToPath: redirectToPath } : {}
|
|
2533
|
+
} : input.userContext
|
|
2534
|
+
});
|
|
2535
|
+
},
|
|
2536
|
+
consumeCodePOST: async (input) => {
|
|
2537
|
+
if (originalImplementation.consumeCodePOST === void 0) {
|
|
2538
|
+
throw new Error(
|
|
2539
|
+
"Passwordless consumeCodePOST is unavailable"
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2543
|
+
input.options.req
|
|
2544
|
+
);
|
|
2545
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2546
|
+
const response = await originalImplementation.consumeCodePOST({
|
|
2547
|
+
...input,
|
|
2548
|
+
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2549
|
+
});
|
|
2550
|
+
if (response.status === "OK") {
|
|
2551
|
+
await recordRowndAppVariantForUser(
|
|
2552
|
+
response.user.id,
|
|
2553
|
+
appVariantId
|
|
2554
|
+
);
|
|
2555
|
+
}
|
|
2556
|
+
return response;
|
|
2557
|
+
}
|
|
2558
|
+
})
|
|
2559
|
+
},
|
|
2560
|
+
thirdparty: {
|
|
2561
|
+
apis: (originalImplementation) => ({
|
|
2562
|
+
...originalImplementation,
|
|
2563
|
+
signInUpPOST: async (input) => {
|
|
2564
|
+
if (originalImplementation.signInUpPOST === void 0) {
|
|
2565
|
+
throw new Error("ThirdParty signInUpPOST is unavailable");
|
|
2566
|
+
}
|
|
2567
|
+
const appVariantId = getRequestedAppVariantIdFromRequest(
|
|
2568
|
+
input.options.req
|
|
2569
|
+
);
|
|
2570
|
+
assertRowndAppVariantIsConfigured(appVariantId);
|
|
2571
|
+
const response = await originalImplementation.signInUpPOST({
|
|
2572
|
+
...input,
|
|
2573
|
+
userContext: appVariantId ? { ...input.userContext, rowndAppVariantId: appVariantId } : input.userContext
|
|
2574
|
+
});
|
|
2575
|
+
if (response.status === "OK") {
|
|
2576
|
+
await recordRowndAppVariantForUser(
|
|
2577
|
+
response.user.id,
|
|
2578
|
+
appVariantId
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
return response;
|
|
2582
|
+
}
|
|
2583
|
+
})
|
|
2584
|
+
},
|
|
2585
|
+
accountlinking: {
|
|
2586
|
+
recipeInitRequired: true,
|
|
2587
|
+
config: (config2) => {
|
|
2588
|
+
const originalShouldDoAutomaticAccountLinking = config2.shouldDoAutomaticAccountLinking;
|
|
2589
|
+
return {
|
|
2590
|
+
...config2,
|
|
2591
|
+
shouldDoAutomaticAccountLinking: async (...input) => {
|
|
2592
|
+
const rowndLinkingDecision = await shouldLinkRowndAccounts(input);
|
|
2593
|
+
if (rowndLinkingDecision) {
|
|
2594
|
+
return rowndLinkingDecision;
|
|
2595
|
+
}
|
|
2596
|
+
if (originalShouldDoAutomaticAccountLinking) {
|
|
2597
|
+
return originalShouldDoAutomaticAccountLinking(...input);
|
|
2598
|
+
}
|
|
2599
|
+
return {
|
|
2600
|
+
shouldAutomaticallyLink: false,
|
|
2601
|
+
shouldRequireVerification: false
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
},
|
|
2607
|
+
session: {
|
|
2608
|
+
recipeInitRequired: true,
|
|
2609
|
+
functions: (originalImplementation) => ({
|
|
2610
|
+
...originalImplementation,
|
|
2611
|
+
createNewSession: async (input) => {
|
|
2612
|
+
input.accessTokenPayload = {
|
|
2613
|
+
...input.accessTokenPayload,
|
|
2614
|
+
...await buildRowndSessionClaims(
|
|
2615
|
+
input.userId,
|
|
2616
|
+
input.accessTokenPayload
|
|
2617
|
+
),
|
|
2618
|
+
...await RowndIsAnonymousClaim.build(
|
|
2619
|
+
input.userId,
|
|
2620
|
+
input.recipeUserId,
|
|
2621
|
+
input.tenantId,
|
|
2622
|
+
input.accessTokenPayload,
|
|
2623
|
+
input.userContext
|
|
2624
|
+
)
|
|
2625
|
+
};
|
|
2626
|
+
return originalImplementation.createNewSession(input);
|
|
2627
|
+
}
|
|
2628
|
+
})
|
|
2629
|
+
},
|
|
2630
|
+
emailverification: {
|
|
2631
|
+
recipeInitRequired: true,
|
|
2632
|
+
config: (config2) => {
|
|
2633
|
+
const originalEmailDeliveryOverride = config2.emailDelivery?.override;
|
|
2634
|
+
return {
|
|
2635
|
+
...config2,
|
|
2636
|
+
emailDelivery: {
|
|
2637
|
+
...config2.emailDelivery,
|
|
2638
|
+
override: (originalImplementation, builder) => {
|
|
2639
|
+
const implementation = originalEmailDeliveryOverride ? originalEmailDeliveryOverride(
|
|
2640
|
+
originalImplementation,
|
|
2641
|
+
builder
|
|
2642
|
+
) : originalImplementation;
|
|
2643
|
+
return {
|
|
2644
|
+
...implementation,
|
|
2645
|
+
sendEmail: async (input) => {
|
|
2646
|
+
return implementation.sendEmail({
|
|
2647
|
+
...addHubBootstrapParams(
|
|
2648
|
+
input,
|
|
2649
|
+
"emailVerifyLink",
|
|
2650
|
+
"account/verify-email"
|
|
2651
|
+
)
|
|
2652
|
+
});
|
|
1011
2653
|
}
|
|
1012
|
-
|
|
1013
|
-
req,
|
|
1014
|
-
res,
|
|
1015
|
-
PUBLIC_TENANT_ID,
|
|
1016
|
-
recipeUserId,
|
|
1017
|
-
{},
|
|
1018
|
-
{},
|
|
1019
|
-
userContext
|
|
1020
|
-
);
|
|
1021
|
-
logDebugMessage(
|
|
1022
|
-
`Session migrated successfully. tenantId: ${PUBLIC_TENANT_ID}, userId: ${superTokensUserId}`
|
|
1023
|
-
);
|
|
1024
|
-
telemetryClient.recordSuccess({
|
|
1025
|
-
outcome: "success",
|
|
1026
|
-
durationMs: Date.now() - startedAt,
|
|
1027
|
-
tenantId,
|
|
1028
|
-
rowndUserId,
|
|
1029
|
-
superTokensUserId
|
|
1030
|
-
});
|
|
1031
|
-
return { status: "OK" };
|
|
1032
|
-
} catch (error) {
|
|
1033
|
-
logDebugMessage(
|
|
1034
|
-
`Migration failed. Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1035
|
-
);
|
|
1036
|
-
telemetryClient.recordError({
|
|
1037
|
-
error,
|
|
1038
|
-
startedAt,
|
|
1039
|
-
tenantId,
|
|
1040
|
-
rowndUserId,
|
|
1041
|
-
superTokensUserId
|
|
1042
|
-
});
|
|
1043
|
-
return {
|
|
1044
|
-
status: "ERROR",
|
|
1045
|
-
message: error instanceof RowndPluginError ? error.message : "Migration failed"
|
|
1046
|
-
};
|
|
1047
|
-
}
|
|
2654
|
+
};
|
|
1048
2655
|
}
|
|
1049
|
-
|
|
2656
|
+
}
|
|
2657
|
+
};
|
|
2658
|
+
},
|
|
2659
|
+
apis: (originalImplementation) => ({
|
|
2660
|
+
...originalImplementation,
|
|
2661
|
+
verifyEmailPOST: async (input) => {
|
|
2662
|
+
if (originalImplementation.verifyEmailPOST === void 0) {
|
|
2663
|
+
throw new Error(
|
|
2664
|
+
"EmailVerification verifyEmailPOST is unavailable"
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
const response = await originalImplementation.verifyEmailPOST(input);
|
|
2668
|
+
if (response.status === "OK") {
|
|
2669
|
+
await completePendingEmailVerification({
|
|
2670
|
+
recipeUserId: response.user.recipeUserId,
|
|
2671
|
+
email: response.user.email,
|
|
2672
|
+
userContext: input.userContext
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
return response;
|
|
1050
2676
|
}
|
|
1051
|
-
|
|
1052
|
-
}
|
|
2677
|
+
})
|
|
2678
|
+
}
|
|
1053
2679
|
}
|
|
1054
2680
|
};
|
|
1055
2681
|
},
|
|
1056
2682
|
() => ({}),
|
|
1057
2683
|
(config2) => {
|
|
1058
2684
|
if (!config2?.rowndAppKey || !config2?.rowndAppSecret) {
|
|
1059
|
-
throw new Error(
|
|
2685
|
+
throw new Error(
|
|
2686
|
+
"Missing rowndAppKey or rowndAppSecret in plugin config"
|
|
2687
|
+
);
|
|
1060
2688
|
}
|
|
1061
2689
|
if (config2.telemetry?.provider === "axiom") {
|
|
1062
2690
|
if (!config2.telemetry.token || !config2.telemetry.dataset) {
|
|
@@ -1076,7 +2704,10 @@ var init = createPluginInitFunction(
|
|
|
1076
2704
|
rowndAppKey: config2.rowndAppKey,
|
|
1077
2705
|
rowndAppSecret: config2.rowndAppSecret,
|
|
1078
2706
|
enableDebugLogs: config2.enableDebugLogs,
|
|
1079
|
-
telemetry: config2.telemetry
|
|
2707
|
+
telemetry: config2.telemetry,
|
|
2708
|
+
schema: config2.schema,
|
|
2709
|
+
appConfig: config2.appConfig,
|
|
2710
|
+
subBrands: config2.subBrands
|
|
1080
2711
|
};
|
|
1081
2712
|
}
|
|
1082
2713
|
);
|
|
@@ -1084,12 +2715,18 @@ var init = createPluginInitFunction(
|
|
|
1084
2715
|
// src/index.ts
|
|
1085
2716
|
var index_default = { init };
|
|
1086
2717
|
export {
|
|
2718
|
+
ANONYMOUS_AUTH_METHOD_ID,
|
|
2719
|
+
DEFAULT_ROWND_SCHEMA,
|
|
2720
|
+
GUEST_AUTH_METHOD_ID,
|
|
1087
2721
|
HANDLE_BASE_PATH,
|
|
1088
2722
|
PLUGIN_ID,
|
|
1089
2723
|
PLUGIN_SDK_VERSION,
|
|
1090
2724
|
PUBLIC_TENANT_ID,
|
|
2725
|
+
ROWND_JWT_CLAIMS,
|
|
1091
2726
|
ROWND_PLUGIN_ERROR_MESSAGES,
|
|
1092
2727
|
RowndPluginError,
|
|
1093
2728
|
index_default as default,
|
|
1094
|
-
|
|
2729
|
+
getRowndClient,
|
|
2730
|
+
init,
|
|
2731
|
+
setRowndClient
|
|
1095
2732
|
};
|