@supertokens-plugins/rownd-nodejs 0.3.0-beta.1 → 0.3.0-beta.3

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