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