@cairs-npm/express-auth-2fa 0.1.3 → 0.1.5

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
@@ -100,23 +100,27 @@ var configSchema = z.object({
100
100
  )
101
101
  ]).default(false),
102
102
  /**
103
- * Project-wide 2-Step Verification switch. False: password sign-in never asks for a code
104
- * (whatever each user chose) and the two-step setting can't be turned on. Passwordless
105
- * sign-in still sends a code — it's the only factor there.
103
+ * 2-Step Verification switch above each user's own choice. False: password sign-in never
104
+ * asks for a code and the two-step setting can't be turned on. Passwordless sign-in still
105
+ * sends a code — it's the only factor there. Pass a function to decide per user, e.g. from
106
+ * an organization setting.
106
107
  */
107
- twoStepEnabled: z.boolean().default(true),
108
+ twoStepEnabled: z.union([
109
+ z.boolean(),
110
+ z.custom((v) => typeof v === "function")
111
+ ]).default(true),
108
112
  /** 2-Step Verification state for users who have not chosen one yet. */
109
113
  twoStepDefault: z.boolean().default(false),
110
- limits: limitSchema.default({}),
114
+ limits: limitSchema.prefault({}),
111
115
  /**
112
116
  * Set false to turn off every RATE_LIMITED check: the per-IP limits on login / code
113
117
  * verify / send / step-up and the code-send cooldown and caps. Account lockouts after
114
118
  * failed passwords or codes (LOCKED) still apply.
115
119
  */
116
120
  rateLimits: z.boolean().default(true),
117
- argon2: argon2Schema.default({}),
118
- cookies: cookieSchema.default({}),
119
- ttl: ttlSchema.default({}),
121
+ argon2: argon2Schema.prefault({}),
122
+ cookies: cookieSchema.prefault({}),
123
+ ttl: ttlSchema.prefault({}),
120
124
  /** Serve GET /auth/openapi.json (A.7 docs). */
121
125
  exposeDocs: z.boolean().default(false)
122
126
  });
@@ -305,6 +309,12 @@ async function migrate(db, prefix = "auth_", auditDays = 365) {
305
309
  return c;
306
310
  }
307
311
 
312
+ // src/config/twoStep.ts
313
+ async function twoStepAvailableFor(config, userId) {
314
+ const v = config.twoStepEnabled;
315
+ return typeof v === "function" ? Boolean(await v({ userId })) : v;
316
+ }
317
+
308
318
  // src/middleware/errors.ts
309
319
  import { ZodError } from "zod";
310
320
  var ApiErrorException = class extends Error {
@@ -561,14 +571,14 @@ var MethodService = class {
561
571
  const cred = await this.cols.credentials.findOne({ userId });
562
572
  const docs = await this.cols.methods.find({ userId, status: "active" }).toArray();
563
573
  const methods = docs.sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime()).map(toApiMethod);
564
- const available = this.config.twoStepEnabled;
574
+ const available = await twoStepAvailableFor(this.config, userId);
565
575
  const twoStepEnabled = available && (cred?.twoStepEnabled ?? this.config.twoStepDefault);
566
576
  return { twoStepEnabled, twoStepAvailable: available, methods };
567
577
  }
568
578
  /** BR-15 — enable needs ≥1 active method; disable needs step-up + revokes trusted devices. */
569
579
  async setTwoStep(userId, sid, enabled, stepUpToken) {
570
580
  const now = this.config.clock();
571
- if (enabled && !this.config.twoStepEnabled) {
581
+ if (enabled && !await twoStepAvailableFor(this.config, userId)) {
572
582
  throw apiError(409, "VALIDATION_ERROR", "2-Step Verification is turned off for this application.");
573
583
  }
574
584
  if (enabled) {
@@ -810,7 +820,7 @@ var AuthService = class {
810
820
  throw apiError(423, "LOCKED", "Too many attempts. Try again later.", { retryAt: cred.lockedUntil });
811
821
  }
812
822
  await this.resetFailedLogins(cred, now);
813
- const twoStep = this.config.twoStepEnabled && cred.twoStepEnabled;
823
+ const twoStep = cred.twoStepEnabled && await twoStepAvailableFor(this.config, user.id);
814
824
  const trustedOk = twoStep && await this.checkTrustedDevice(req, user.id);
815
825
  if (!twoStep || trustedOk) {
816
826
  await this.audit.record(user.id, "login_success", ctx);