@getstrata/core 2.2.28 → 2.2.29

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.
@@ -510,6 +510,23 @@ function revealMfaSecret(stored) {
510
510
  // ../../src/core/auth/passwordLogin.ts
511
511
  init_dialect();
512
512
 
513
+ // ../../src/core/errors/http.ts
514
+ class HttpError extends Error {
515
+ status;
516
+ details;
517
+ constructor(status, message, details) {
518
+ super(message);
519
+ this.name = new.target.name;
520
+ this.status = status;
521
+ this.details = details;
522
+ }
523
+ }
524
+ class UnprocessableEntityError extends HttpError {
525
+ constructor(message = "Unprocessable Entity", details) {
526
+ super(422, message, details);
527
+ }
528
+ }
529
+
513
530
  // ../../src/core/security/recoveryCodes.ts
514
531
  import { randomBytes as randomBytes2 } from "crypto";
515
532
  import { hashApiToken } from "@getstrata/core/auth/tokenHash";
@@ -730,11 +747,17 @@ async function persistConsumedRecoveryHash(sql, userId, currentRaw, consumedHash
730
747
  if (!consumedHash) {
731
748
  return;
732
749
  }
750
+ const hashes = parseRecoveryHashes(currentRaw);
751
+ if (typeof currentRaw !== "string" || !hashes.includes(consumedHash)) {
752
+ throw new UnprocessableEntityError("mfa_invalid");
753
+ }
733
754
  const dialect = currentSqlDialect();
734
- await sql.unsafe(`UPDATE users SET mfa_recovery_codes = ${dialect.placeholder(1)} WHERE id = ${dialect.placeholder(2)}`, [
735
- JSON.stringify(parseRecoveryHashes(currentRaw).filter((hash) => hash !== consumedHash)),
736
- userId
737
- ]);
755
+ const returning = dialect.returningClause("id");
756
+ const rows = await sql.unsafe(`UPDATE users SET mfa_recovery_codes = ${dialect.placeholder(1)} WHERE id = ${dialect.placeholder(2)} AND mfa_recovery_codes = ${dialect.placeholder(3)}${returning}`, [JSON.stringify(hashes.filter((hash) => hash !== consumedHash)), userId, currentRaw]);
757
+ const header = rows[0];
758
+ const affected = returning ? rows.length : typeof header === "object" && header !== null && ("affectedRows" in header) ? Number(header.affectedRows) : 0;
759
+ if (affected !== 1)
760
+ throw new UnprocessableEntityError("mfa_invalid");
738
761
  }
739
762
  export {
740
763
  completePasswordLogin,