@fonderie/auth 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -45,6 +45,74 @@ var requireEmailLogin = async (ctx, next) => {
45
45
  return next();
46
46
  };
47
47
 
48
+ // src/middlewares/validate.ts
49
+ import { validate } from "@fonderie/core/middlewares";
50
+
51
+ // src/schemas.ts
52
+ var schemas_exports = {};
53
+ __export(schemas_exports, {
54
+ changePasswordSchema: () => changePasswordSchema,
55
+ forgotPasswordSchema: () => forgotPasswordSchema,
56
+ loginSchema: () => loginSchema,
57
+ mfaTokenSchema: () => mfaTokenSchema,
58
+ refreshSchema: () => refreshSchema,
59
+ registerSchema: () => registerSchema,
60
+ resetPasswordSchema: () => resetPasswordSchema,
61
+ updateEmailSchema: () => updateEmailSchema,
62
+ updatePhoneSchema: () => updatePhoneSchema,
63
+ updatePreferencesSchema: () => updatePreferencesSchema,
64
+ updateProfileSchema: () => updateProfileSchema,
65
+ verifySchema: () => verifySchema
66
+ });
67
+ import { z } from "zod";
68
+ var email = z.string().trim().pipe(z.email());
69
+ var password = z.string().min(8, "password must be at least 8 characters").max(128);
70
+ var phone = z.string().refine((v) => /^\+?[1-9]\d{6,14}$/.test(v.replace(/[\s\-()]/g, "")), "Invalid phone number");
71
+ var sixDigitPin = z.string().trim().regex(/^\d{6}$/, "must be a 6-digit code");
72
+ var registerSchema = z.union([
73
+ z.object({
74
+ email,
75
+ password,
76
+ firstName: z.string().max(100).nullish(),
77
+ lastName: z.string().max(100).nullish()
78
+ }),
79
+ z.object({ phone })
80
+ ]);
81
+ var loginSchema = z.union([
82
+ z.object({ email, password: z.string().min(1).max(128) }),
83
+ z.object({ phone })
84
+ ]);
85
+ var refreshSchema = z.object({ refreshToken: z.string().min(1).optional() });
86
+ var forgotPasswordSchema = z.object({ email });
87
+ var resetPasswordSchema = z.object({ pin: sixDigitPin, password });
88
+ var verifySchema = z.object({ token: sixDigitPin });
89
+ var updateProfileSchema = z.object({
90
+ firstName: z.string().max(100).nullable().optional(),
91
+ lastName: z.string().max(100).nullable().optional(),
92
+ avatarUrl: z.string().trim().pipe(z.url()).nullable().optional()
93
+ }).refine(
94
+ (o) => Object.values(o).some((v) => v !== void 0),
95
+ "Provide at least one of: firstName, lastName, avatarUrl"
96
+ );
97
+ var updatePreferencesSchema = z.object({
98
+ locale: z.string().max(35).optional(),
99
+ timezone: z.string().max(64).optional(),
100
+ notifications: z.unknown().optional(),
101
+ emailDigest: z.unknown().optional(),
102
+ dateFormat: z.unknown().optional(),
103
+ timeFormat: z.unknown().optional()
104
+ }).refine(
105
+ (o) => Object.values(o).some((v) => v !== void 0),
106
+ "Provide at least one preference field"
107
+ );
108
+ var updateEmailSchema = z.object({ email });
109
+ var updatePhoneSchema = z.object({ phone });
110
+ var changePasswordSchema = z.object({
111
+ currentPassword: z.string().min(1).max(128),
112
+ newPassword: password
113
+ });
114
+ var mfaTokenSchema = z.object({ token: z.string().trim().min(6).max(64) });
115
+
48
116
  // src/controllers/mfa.controller.ts
49
117
  import QRCode from "qrcode";
50
118
  import { setApiResponse as setApiResponse2, HTTP as HTTP2 } from "@fonderie/core";
@@ -181,7 +249,7 @@ function base32Encode(buf) {
181
249
  function generateTotpSecret() {
182
250
  return base32Encode(randomBytes(20));
183
251
  }
184
- function generateTotpUri(email, secret, issuer) {
252
+ function generateTotpUri(email2, secret, issuer) {
185
253
  const params = new URLSearchParams({
186
254
  secret,
187
255
  issuer,
@@ -189,7 +257,7 @@ function generateTotpUri(email, secret, issuer) {
189
257
  digits: String(DIGITS),
190
258
  period: String(STEP)
191
259
  });
192
- return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(email)}?${params}`;
260
+ return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(email2)}?${params}`;
193
261
  }
194
262
  function verifyTotpToken(token, secret) {
195
263
  const t = timeCounter();
@@ -281,21 +349,21 @@ var UserModel = class {
281
349
  );
282
350
  return row ?? null;
283
351
  }
284
- async findByEmail(email) {
352
+ async findByEmail(email2) {
285
353
  const [row] = await this.store.query(
286
354
  `SELECT ${USER_COLUMNS} FROM fonderie_users WHERE email = $1 AND deleted_at IS NULL`,
287
- [email]
355
+ [email2]
288
356
  );
289
357
  return row ?? null;
290
358
  }
291
- async findByPhone(phone) {
359
+ async findByPhone(phone2) {
292
360
  const [row] = await this.store.query(
293
361
  `SELECT ${USER_COLUMNS} FROM fonderie_users WHERE phone = $1 AND deleted_at IS NULL`,
294
- [phone]
362
+ [phone2]
295
363
  );
296
364
  return row ?? null;
297
365
  }
298
- async findOrCreateByPhone(phone, firstName = null, lastName = null) {
366
+ async findOrCreateByPhone(phone2, firstName = null, lastName = null) {
299
367
  const [row] = await this.store.query(
300
368
  `INSERT INTO fonderie_users (phone, first_name, last_name)
301
369
  VALUES ($1, $2, $3)
@@ -304,16 +372,16 @@ var UserModel = class {
304
372
  last_name = COALESCE(EXCLUDED.last_name, fonderie_users.last_name),
305
373
  updated_at = now()
306
374
  RETURNING id`,
307
- [phone, firstName, lastName]
375
+ [phone2, firstName, lastName]
308
376
  );
309
377
  return row;
310
378
  }
311
- async create(email, passwordHash, firstName, lastName) {
379
+ async create(email2, passwordHash, firstName, lastName) {
312
380
  const [row] = await this.store.query(
313
381
  `INSERT INTO fonderie_users (email, password_hash, first_name, last_name)
314
382
  VALUES ($1, $2, $3, $4)
315
383
  RETURNING id`,
316
- [email.toLowerCase().trim(), passwordHash, firstName, lastName]
384
+ [email2.toLowerCase().trim(), passwordHash, firstName, lastName]
317
385
  );
318
386
  return row ?? null;
319
387
  }
@@ -400,16 +468,16 @@ var UserModel = class {
400
468
  [id]
401
469
  );
402
470
  }
403
- async updateEmail(id, email) {
471
+ async updateEmail(id, email2) {
404
472
  await this.store.query(
405
473
  `UPDATE fonderie_users SET email = $1, email_verified_at = NULL, updated_at = now() WHERE id = $2`,
406
- [email.toLowerCase().trim(), id]
474
+ [email2.toLowerCase().trim(), id]
407
475
  );
408
476
  }
409
- async updatePhone(id, phone) {
477
+ async updatePhone(id, phone2) {
410
478
  await this.store.query(
411
479
  `UPDATE fonderie_users SET phone = $1, updated_at = now() WHERE id = $2`,
412
- [phone, id]
480
+ [phone2, id]
413
481
  );
414
482
  }
415
483
  async updatePreferences(id, fields) {
@@ -442,14 +510,14 @@ var UserModel = class {
442
510
  );
443
511
  return row?.mfa_secret ?? null;
444
512
  }
445
- async upsertByProvider(email, provider, providerId) {
513
+ async upsertByProvider(email2, provider, providerId) {
446
514
  const [row] = await this.store.query(
447
515
  `INSERT INTO fonderie_users (email, email_verified_at, provider, provider_id)
448
516
  VALUES ($1, now(), $2, $3)
449
517
  ON CONFLICT (email) DO UPDATE
450
518
  SET provider = $2, provider_id = $3
451
519
  RETURNING id`,
452
- [email, provider, providerId]
520
+ [email2, provider, providerId]
453
521
  );
454
522
  return row ?? null;
455
523
  }
@@ -706,11 +774,11 @@ function checkCooldown(lastSentAt, cooldownMs) {
706
774
  init_password();
707
775
 
708
776
  // src/services/email.ts
709
- function normalizeEmail(email) {
710
- if (typeof email !== "string" || email.length === 0) {
777
+ function normalizeEmail(email2) {
778
+ if (typeof email2 !== "string" || email2.length === 0) {
711
779
  throw new Error("Invalid email");
712
780
  }
713
- const lower = email.trim().toLowerCase();
781
+ const lower = email2.trim().toLowerCase();
714
782
  if (lower.length === 0) {
715
783
  throw new Error("Email cannot be empty");
716
784
  }
@@ -730,9 +798,9 @@ function normalizeEmail(email) {
730
798
  }
731
799
  return `${normalizedLocal}@${domain}`;
732
800
  }
733
- function normalizeEmailSafe(email) {
801
+ function normalizeEmailSafe(email2) {
734
802
  try {
735
- return normalizeEmail(email);
803
+ return normalizeEmail(email2);
736
804
  } catch {
737
805
  return null;
738
806
  }
@@ -833,13 +901,13 @@ var PhoneVerificationModel = class {
833
901
  this.store = store;
834
902
  }
835
903
  store;
836
- async upsert(userId, phone, otp, expiresAt) {
904
+ async upsert(userId, phone2, otp, expiresAt) {
837
905
  await this.store.query(
838
906
  `INSERT INTO fonderie_phone_verifications (phone, user_id, otp, expires_at)
839
907
  VALUES ($1, $2, $3, $4)
840
908
  ON CONFLICT (phone) DO UPDATE
841
909
  SET user_id = $2, otp = $3, expires_at = $4, created_at = now()`,
842
- [phone, userId, otp, expiresAt]
910
+ [phone2, userId, otp, expiresAt]
843
911
  );
844
912
  }
845
913
  async findByUser(userId, otp) {
@@ -864,11 +932,11 @@ var PhoneVerificationModel = class {
864
932
  };
865
933
 
866
934
  // src/controllers/auth.controller.ts
867
- function normalizePhone(phone) {
868
- return phone.trim().replace(/[\s()\-\.]/g, "");
935
+ function normalizePhone(phone2) {
936
+ return phone2.trim().replace(/[\s()\-\.]/g, "");
869
937
  }
870
- function isValidPhone(phone) {
871
- return typeof phone === "string" && /^\+?[1-9]\d{6,14}$/.test(normalizePhone(phone));
938
+ function isValidPhone(phone2) {
939
+ return typeof phone2 === "string" && /^\+?[1-9]\d{6,14}$/.test(normalizePhone(phone2));
872
940
  }
873
941
  function extractRefreshToken(ctx) {
874
942
  const body = ctx.meta["body"];
@@ -889,13 +957,13 @@ function authController(store, config, bus) {
889
957
  return {
890
958
  register: async (ctx) => {
891
959
  const body = ctx.meta["body"];
892
- const { email, password, phone, firstName = null, lastName = null } = body ?? {};
893
- if (typeof email === "string" && typeof password === "string") {
894
- const normalizedEmail = normalizeEmailSafe(email);
960
+ const { email: email2, password: password2, phone: phone2, firstName = null, lastName = null } = body ?? {};
961
+ if (typeof email2 === "string" && typeof password2 === "string") {
962
+ const normalizedEmail = normalizeEmailSafe(email2);
895
963
  if (!normalizedEmail) {
896
964
  return setApiResponse3(HTTP3.UNPROCESSABLE, "INVALID_PARAMETER", "Invalid email address");
897
965
  }
898
- if (password.length < 8) {
966
+ if (password2.length < 8) {
899
967
  return setApiResponse3(
900
968
  HTTP3.UNPROCESSABLE,
901
969
  "INVALID_PARAMETER",
@@ -906,7 +974,7 @@ function authController(store, config, bus) {
906
974
  if (existing) {
907
975
  return setApiResponse3(HTTP3.CONFLICT, "USER_ALREADY_EXISTS", "Email already registered");
908
976
  }
909
- const passwordHash = await hashPassword(password);
977
+ const passwordHash = await hashPassword(password2);
910
978
  const row = await users.create(
911
979
  normalizedEmail,
912
980
  passwordHash,
@@ -974,19 +1042,19 @@ function authController(store, config, bus) {
974
1042
  }
975
1043
  );
976
1044
  }
977
- if (isValidPhone(phone)) {
978
- const existing = await users.findByPhone(normalizePhone(phone));
1045
+ if (isValidPhone(phone2)) {
1046
+ const existing = await users.findByPhone(normalizePhone(phone2));
979
1047
  if (existing) {
980
1048
  return setApiResponse3(HTTP3.CONFLICT, "USER_ALREADY_EXISTS", "Phone already registered");
981
1049
  }
982
1050
  const { id } = await users.findOrCreateByPhone(
983
- normalizePhone(phone),
1051
+ normalizePhone(phone2),
984
1052
  firstName ?? null,
985
1053
  lastName ?? null
986
1054
  );
987
1055
  const otp = randomInt(1e5, 1e6).toString();
988
1056
  const expiresAt = new Date(Date.now() + OTP_TTL_MS);
989
- await phoneVerif.upsert(id, normalizePhone(phone), otp, expiresAt);
1057
+ await phoneVerif.upsert(id, normalizePhone(phone2), otp, expiresAt);
990
1058
  const user = await users.findById(id);
991
1059
  if (!user) {
992
1060
  return setApiResponse3(HTTP3.SERVER_ERROR, "SERVER_ERROR", "Registration failed");
@@ -998,7 +1066,7 @@ function authController(store, config, bus) {
998
1066
  {
999
1067
  type: MESSAGE_KEYS.phoneOtp,
1000
1068
  data: { otp },
1001
- recipient: { email: null, phone: normalizePhone(phone), deviceToken: null }
1069
+ recipient: { email: null, phone: normalizePhone(phone2), deviceToken: null }
1002
1070
  },
1003
1071
  reqOpts2
1004
1072
  ).catch(() => {
@@ -1048,16 +1116,16 @@ function authController(store, config, bus) {
1048
1116
  login: async (ctx) => {
1049
1117
  const body = ctx.meta["body"];
1050
1118
  if (typeof body?.["email"] === "string" && typeof body?.["password"] === "string") {
1051
- const { email: rawEmail, password } = body;
1052
- const email = normalizeEmailSafe(rawEmail);
1053
- if (!email) {
1119
+ const { email: rawEmail, password: password2 } = body;
1120
+ const email2 = normalizeEmailSafe(rawEmail);
1121
+ if (!email2) {
1054
1122
  return setApiResponse3(HTTP3.UNPROCESSABLE, "INVALID_PARAMETER", "Invalid email address");
1055
1123
  }
1056
- const user = await users.findByEmail(email);
1124
+ const user = await users.findByEmail(email2);
1057
1125
  if (!user || !user.passwordHash) {
1058
1126
  return setApiResponse3(HTTP3.UNAUTHORIZED, "INVALID_CREDENTIALS", "Invalid credentials");
1059
1127
  }
1060
- const valid = await verifyPassword(password, user.passwordHash);
1128
+ const valid = await verifyPassword(password2, user.passwordHash);
1061
1129
  if (!valid) {
1062
1130
  return setApiResponse3(HTTP3.UNAUTHORIZED, "INVALID_CREDENTIALS", "Invalid credentials");
1063
1131
  }
@@ -1101,9 +1169,9 @@ function authController(store, config, bus) {
1101
1169
  }
1102
1170
  );
1103
1171
  }
1104
- const phone = body?.["phone"];
1105
- if (isValidPhone(phone)) {
1106
- const user = await users.findByPhone(normalizePhone(phone));
1172
+ const phone2 = body?.["phone"];
1173
+ if (isValidPhone(phone2)) {
1174
+ const user = await users.findByPhone(normalizePhone(phone2));
1107
1175
  if (!user) {
1108
1176
  return setApiResponse3(HTTP3.UNAUTHORIZED, "INVALID_CREDENTIALS", "Invalid credentials");
1109
1177
  }
@@ -1116,11 +1184,11 @@ function authController(store, config, bus) {
1116
1184
  }
1117
1185
  const otp = randomInt(1e5, 1e6).toString();
1118
1186
  const expiresAt = new Date(Date.now() + OTP_TTL_MS);
1119
- await phoneVerif.upsert(user.id, normalizePhone(phone), otp, expiresAt);
1187
+ await phoneVerif.upsert(user.id, normalizePhone(phone2), otp, expiresAt);
1120
1188
  bus?.emit(NOTIFICATION_EVENT2, {
1121
1189
  type: MESSAGE_KEYS.phoneOtp,
1122
1190
  data: { otp },
1123
- recipient: { email: null, phone: normalizePhone(phone), deviceToken: null }
1191
+ recipient: { email: null, phone: normalizePhone(phone2), deviceToken: null }
1124
1192
  }).catch(() => {
1125
1193
  });
1126
1194
  const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
@@ -1221,11 +1289,11 @@ function authController(store, config, bus) {
1221
1289
  if (typeof rawEmail !== "string") {
1222
1290
  return setApiResponse3(HTTP3.UNPROCESSABLE, "INVALID_PARAMETER", "email is required");
1223
1291
  }
1224
- const email = normalizeEmailSafe(rawEmail);
1225
- if (!email) {
1292
+ const email2 = normalizeEmailSafe(rawEmail);
1293
+ if (!email2) {
1226
1294
  return setApiResponse3(HTTP3.UNPROCESSABLE, "INVALID_PARAMETER", "Invalid email address");
1227
1295
  }
1228
- const user = await users.findByEmail(email);
1296
+ const user = await users.findByEmail(email2);
1229
1297
  if (!user) {
1230
1298
  return setApiResponse3(
1231
1299
  HTTP3.OK,
@@ -1251,7 +1319,7 @@ function authController(store, config, bus) {
1251
1319
  await passwordReset.create(user.id, pin, expiresAt);
1252
1320
  bus?.emit(NOTIFICATION_EVENT2, {
1253
1321
  type: MESSAGE_KEYS.passwordReset,
1254
- recipient: { email, phone: null, deviceToken: null },
1322
+ recipient: { email: email2, phone: null, deviceToken: null },
1255
1323
  data: { pin }
1256
1324
  }).catch(() => {
1257
1325
  });
@@ -1264,8 +1332,8 @@ function authController(store, config, bus) {
1264
1332
  resetPassword: async (ctx) => {
1265
1333
  const body = ctx.meta["body"];
1266
1334
  const raw = body?.["pin"];
1267
- const password = body?.["password"];
1268
- if (typeof raw !== "string" || typeof password !== "string") {
1335
+ const password2 = body?.["password"];
1336
+ if (typeof raw !== "string" || typeof password2 !== "string") {
1269
1337
  return setApiResponse3(
1270
1338
  HTTP3.UNPROCESSABLE,
1271
1339
  "INVALID_PARAMETER",
@@ -1279,7 +1347,7 @@ function authController(store, config, bus) {
1279
1347
  "pin must be a 6-digit code"
1280
1348
  );
1281
1349
  }
1282
- if (password.length < 8) {
1350
+ if (password2.length < 8) {
1283
1351
  return setApiResponse3(
1284
1352
  HTTP3.UNPROCESSABLE,
1285
1353
  "INVALID_PARAMETER",
@@ -1291,7 +1359,7 @@ function authController(store, config, bus) {
1291
1359
  if (!row || /* @__PURE__ */ new Date() > row.expiresAt) {
1292
1360
  return setApiResponse3(HTTP3.BAD_REQUEST, "PASSWORD_RESET_FAILED", "Invalid or expired pin");
1293
1361
  }
1294
- const passwordHash = await hashPassword(password);
1362
+ const passwordHash = await hashPassword(password2);
1295
1363
  await store.transaction(async (tx) => {
1296
1364
  await Promise.all([
1297
1365
  tx.query(`UPDATE fonderie_users SET password_hash = $1 WHERE id = $2`, [
@@ -1398,8 +1466,8 @@ function authController(store, config, bus) {
1398
1466
  const resolved = { ...config, ...config.resolve?.(ctx) };
1399
1467
  const cooldown = resolved.verificationCooldown ?? DEFAULT_VERIFICATION_COOLDOWN;
1400
1468
  if (ctx.user.loginMethod === "phone") {
1401
- const phone = ctx.user.phone;
1402
- if (!phone) {
1469
+ const phone2 = ctx.user.phone;
1470
+ if (!phone2) {
1403
1471
  return setApiResponse3(
1404
1472
  HTTP3.BAD_REQUEST,
1405
1473
  "NO_PHONE_ON_ACCOUNT",
@@ -1419,11 +1487,11 @@ function authController(store, config, bus) {
1419
1487
  }
1420
1488
  const otp = randomInt(1e5, 1e6).toString();
1421
1489
  const expiresAt2 = new Date(Date.now() + OTP_TTL_MS);
1422
- await phoneVerif.upsert(ctx.user.id, phone, otp, expiresAt2);
1490
+ await phoneVerif.upsert(ctx.user.id, phone2, otp, expiresAt2);
1423
1491
  bus?.emit(NOTIFICATION_EVENT2, {
1424
1492
  type: MESSAGE_KEYS.phoneOtp,
1425
1493
  data: { otp },
1426
- recipient: { email: null, phone, deviceToken: null }
1494
+ recipient: { email: null, phone: phone2, deviceToken: null }
1427
1495
  }).catch(() => {
1428
1496
  });
1429
1497
  return setApiResponse3(
@@ -1476,11 +1544,11 @@ function authController(store, config, bus) {
1476
1544
  import { randomInt as randomInt2 } from "crypto";
1477
1545
  import { setApiResponse as setApiResponse4, HTTP as HTTP4 } from "@fonderie/core";
1478
1546
  import { NOTIFICATION_EVENT as NOTIFICATION_EVENT3 } from "@fonderie/events";
1479
- function normalizePhone2(phone) {
1480
- return phone.trim().replace(/[\s()\-\.]/g, "");
1547
+ function normalizePhone2(phone2) {
1548
+ return phone2.trim().replace(/[\s()\-\.]/g, "");
1481
1549
  }
1482
- function isValidPhone2(phone) {
1483
- return typeof phone === "string" && /^\+?[1-9]\d{6,14}$/.test(normalizePhone2(phone));
1550
+ function isValidPhone2(phone2) {
1551
+ return typeof phone2 === "string" && /^\+?[1-9]\d{6,14}$/.test(normalizePhone2(phone2));
1484
1552
  }
1485
1553
  function userController(store, bus) {
1486
1554
  const users = new UserModel(store);
@@ -1798,39 +1866,40 @@ function buildAuthRoutes(store, config, bus) {
1798
1866
  const verifyGate = config.requireVerification ? requireVerified : (_ctx, next) => next();
1799
1867
  const routes = [
1800
1868
  // Registration & Login (Public)
1801
- ["POST", "/auth/register", auth.register],
1802
- ["POST", "/auth/login", auth.login],
1869
+ ["POST", "/auth/register", validate(registerSchema), auth.register],
1870
+ ["POST", "/auth/login", validate(loginSchema), auth.login],
1803
1871
  // Token Management (Public)
1804
- ["POST", "/auth/refresh", auth.refresh],
1872
+ ["POST", "/auth/refresh", validate(refreshSchema), auth.refresh],
1805
1873
  // Email — Password Recovery (Public)
1806
- ["POST", "/auth/email/forgot", auth.forgotPassword],
1807
- ["POST", "/auth/email/reset", auth.resetPassword],
1874
+ ["POST", "/auth/email/forgot", validate(forgotPasswordSchema), auth.forgotPassword],
1875
+ ["POST", "/auth/email/reset", validate(resetPasswordSchema), auth.resetPassword],
1808
1876
  // Verification (Protected — email or phone, determined by loginMethod)
1809
- ["POST", "/auth/verify", requireAuth, auth.verify],
1877
+ ["POST", "/auth/verify", requireAuth, validate(verifySchema), auth.verify],
1810
1878
  ["GET", "/auth/send-verification", requireAuth, auth.sendVerification],
1811
1879
  // Account Management (Protected)
1812
- ["POST", "/auth/logout", requireAuth, auth.logout],
1880
+ ["POST", "/auth/logout", requireAuth, validate(refreshSchema), auth.logout],
1813
1881
  // User Profile (Protected; writes also gate on requireVerification)
1814
1882
  ["GET", "/users", requireAuth, user.me],
1815
- ["PUT", "/users/profile", requireAuth, verifyGate, user.updateProfile],
1816
- ["PUT", "/users/preferences", requireAuth, verifyGate, user.updatePreferences],
1817
- ["PUT", "/users/email", requireAuth, verifyGate, user.updateEmail],
1818
- ["PUT", "/users/phone", requireAuth, verifyGate, user.updatePhone],
1819
- ["PUT", "/users/password", requireAuth, user.changePassword],
1883
+ ["PUT", "/users/profile", requireAuth, verifyGate, validate(updateProfileSchema), user.updateProfile],
1884
+ ["PUT", "/users/preferences", requireAuth, verifyGate, validate(updatePreferencesSchema), user.updatePreferences],
1885
+ ["PUT", "/users/email", requireAuth, verifyGate, validate(updateEmailSchema), user.updateEmail],
1886
+ ["PUT", "/users/phone", requireAuth, verifyGate, validate(updatePhoneSchema), user.updatePhone],
1887
+ ["PUT", "/users/password", requireAuth, validate(changePasswordSchema), user.changePassword],
1820
1888
  ["DELETE", "/users", requireAuth, verifyGate, user.deleteMe],
1821
1889
  // MFA (email sessions only — requireVerified is always enforced here
1822
1890
  // because MFA is a security feature and email verification is meaningful)
1823
1891
  ["POST", "/auth/mfa/setup", requireAuth, requireEmailLogin, requireVerified, mfa.setup],
1824
1892
  // /auth/mfa/verify accepts both mfaPending tokens (TOTP/backup-code login)
1825
1893
  // and full tokens (setup confirmation), so requireAnyAuth is used here.
1826
- ["POST", "/auth/mfa/verify", requireAnyAuth, requireEmailLogin, requireVerified, mfa.verify],
1827
- ["POST", "/auth/mfa/disable", requireAuth, requireEmailLogin, requireVerified, mfa.disable],
1894
+ ["POST", "/auth/mfa/verify", requireAnyAuth, requireEmailLogin, requireVerified, validate(mfaTokenSchema), mfa.verify],
1895
+ ["POST", "/auth/mfa/disable", requireAuth, requireEmailLogin, requireVerified, validate(mfaTokenSchema), mfa.disable],
1828
1896
  [
1829
1897
  "POST",
1830
1898
  "/auth/mfa/backup-codes",
1831
1899
  requireAuth,
1832
1900
  requireEmailLogin,
1833
1901
  requireVerified,
1902
+ validate(mfaTokenSchema),
1834
1903
  mfa.regenerateBackupCodes
1835
1904
  ]
1836
1905
  ];
@@ -1909,7 +1978,9 @@ export {
1909
1978
  normalizeEmail,
1910
1979
  normalizeEmailSafe,
1911
1980
  requireAuth2 as requireAuth,
1981
+ schemas_exports as schemas,
1912
1982
  toUserDTO,
1983
+ validate,
1913
1984
  withSession
1914
1985
  };
1915
1986
  //# sourceMappingURL=index.js.map