@cairs-npm/express-auth-2fa 0.1.2 → 0.1.4
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.cjs +25 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +25 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -185,6 +185,16 @@ var configSchema = import_zod.z.object({
|
|
|
185
185
|
(v) => typeof v === "function"
|
|
186
186
|
)
|
|
187
187
|
]).default(false),
|
|
188
|
+
/**
|
|
189
|
+
* 2-Step Verification switch above each user's own choice. False: password sign-in never
|
|
190
|
+
* asks for a code and the two-step setting can't be turned on. Passwordless sign-in still
|
|
191
|
+
* sends a code — it's the only factor there. Pass a function to decide per user, e.g. from
|
|
192
|
+
* an organization setting.
|
|
193
|
+
*/
|
|
194
|
+
twoStepEnabled: import_zod.z.union([
|
|
195
|
+
import_zod.z.boolean(),
|
|
196
|
+
import_zod.z.custom((v) => typeof v === "function")
|
|
197
|
+
]).default(true),
|
|
188
198
|
/** 2-Step Verification state for users who have not chosen one yet. */
|
|
189
199
|
twoStepDefault: import_zod.z.boolean().default(false),
|
|
190
200
|
limits: limitSchema.default({}),
|
|
@@ -385,6 +395,12 @@ async function migrate(db, prefix = "auth_", auditDays = 365) {
|
|
|
385
395
|
return c;
|
|
386
396
|
}
|
|
387
397
|
|
|
398
|
+
// src/config/twoStep.ts
|
|
399
|
+
async function twoStepAvailableFor(config, userId) {
|
|
400
|
+
const v = config.twoStepEnabled;
|
|
401
|
+
return typeof v === "function" ? Boolean(await v({ userId })) : v;
|
|
402
|
+
}
|
|
403
|
+
|
|
388
404
|
// src/services/auth.service.ts
|
|
389
405
|
init_hash();
|
|
390
406
|
|
|
@@ -647,11 +663,16 @@ var MethodService = class {
|
|
|
647
663
|
const cred = await this.cols.credentials.findOne({ userId });
|
|
648
664
|
const docs = await this.cols.methods.find({ userId, status: "active" }).toArray();
|
|
649
665
|
const methods = docs.sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime()).map(toApiMethod);
|
|
650
|
-
|
|
666
|
+
const available = await twoStepAvailableFor(this.config, userId);
|
|
667
|
+
const twoStepEnabled = available && (cred?.twoStepEnabled ?? this.config.twoStepDefault);
|
|
668
|
+
return { twoStepEnabled, twoStepAvailable: available, methods };
|
|
651
669
|
}
|
|
652
670
|
/** BR-15 — enable needs ≥1 active method; disable needs step-up + revokes trusted devices. */
|
|
653
671
|
async setTwoStep(userId, sid, enabled, stepUpToken) {
|
|
654
672
|
const now = this.config.clock();
|
|
673
|
+
if (enabled && !await twoStepAvailableFor(this.config, userId)) {
|
|
674
|
+
throw apiError(409, "VALIDATION_ERROR", "2-Step Verification is turned off for this application.");
|
|
675
|
+
}
|
|
655
676
|
if (enabled) {
|
|
656
677
|
await ensureEmailMethod(this.config, this.cols, userId);
|
|
657
678
|
const count = await this.cols.methods.countDocuments({ userId, status: "active" });
|
|
@@ -891,8 +912,9 @@ var AuthService = class {
|
|
|
891
912
|
throw apiError(423, "LOCKED", "Too many attempts. Try again later.", { retryAt: cred.lockedUntil });
|
|
892
913
|
}
|
|
893
914
|
await this.resetFailedLogins(cred, now);
|
|
894
|
-
const
|
|
895
|
-
|
|
915
|
+
const twoStep = cred.twoStepEnabled && await twoStepAvailableFor(this.config, user.id);
|
|
916
|
+
const trustedOk = twoStep && await this.checkTrustedDevice(req, user.id);
|
|
917
|
+
if (!twoStep || trustedOk) {
|
|
896
918
|
await this.audit.record(user.id, "login_success", ctx);
|
|
897
919
|
return this.authenticate(user.id, req, res, ["pwd"], { rememberMe: input.rememberMe });
|
|
898
920
|
}
|