@vsaas/loopback 12.1.1 → 12.2.1
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/README.md +6 -0
- package/common/models/rate-limit-state.json +121 -0
- package/common/models/two-factor-challenge.json +112 -0
- package/common/models/two-factor-credential.json +117 -0
- package/common/models/two-factor-recovery-code.json +63 -0
- package/common/models/user.json +30 -0
- package/dist/_virtual/_rolldown/runtime.cjs +9 -2
- package/dist/common/models/access-token.d.cts +3 -0
- package/dist/common/models/acl.cjs +1 -3
- package/dist/common/models/acl.d.cts +1 -0
- package/dist/common/models/application.d.cts +3 -0
- package/dist/common/models/change.cjs +21 -18
- package/dist/common/models/change.d.cts +1 -0
- package/dist/common/models/checkpoint.d.cts +3 -0
- package/dist/common/models/email.d.cts +3 -0
- package/dist/common/models/key-value-model.d.cts +3 -0
- package/dist/common/models/rate-limit-state.cjs +7 -0
- package/dist/common/models/rate-limit-state.d.cts +3 -0
- package/dist/common/models/rate-limit-state2.cjs +109 -0
- package/dist/common/models/role-mapping.d.cts +3 -0
- package/dist/common/models/role.cjs +12 -8
- package/dist/common/models/role.d.cts +1 -0
- package/dist/common/models/scope.d.cts +3 -0
- package/dist/common/models/two-factor-challenge.cjs +7 -0
- package/dist/common/models/two-factor-challenge.d.cts +3 -0
- package/dist/common/models/two-factor-challenge2.cjs +108 -0
- package/dist/common/models/two-factor-credential.cjs +7 -0
- package/dist/common/models/two-factor-credential.d.cts +3 -0
- package/dist/common/models/two-factor-credential2.cjs +103 -0
- package/dist/common/models/two-factor-recovery-code.cjs +7 -0
- package/dist/common/models/two-factor-recovery-code.d.cts +3 -0
- package/dist/common/models/two-factor-recovery-code2.cjs +57 -0
- package/dist/common/models/user.cjs +261 -21
- package/dist/common/models/user.d.cts +3 -0
- package/dist/common/models/user2.cjs +30 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +3 -0
- package/dist/lib/access-context.d.cts +33 -0
- package/dist/lib/application.d.cts +1 -0
- package/dist/lib/authentication-security-initializer.cjs +351 -0
- package/dist/lib/authentication-security-initializer.d.cts +4 -0
- package/dist/lib/builtin-models.cjs +12 -0
- package/dist/lib/builtin-models.d.cts +3 -0
- package/dist/lib/configure-shared-methods.cjs +7 -4
- package/dist/lib/configure-shared-methods.d.cts +3 -0
- package/dist/lib/connectors/base-connector.d.cts +6 -0
- package/dist/lib/connectors/mail-direct-transport.cjs +26 -23
- package/dist/lib/connectors/mail-direct-transport.d.cts +22 -0
- package/dist/lib/connectors/mail-stub-transport.d.cts +14 -0
- package/dist/lib/connectors/mail.d.cts +7 -0
- package/dist/lib/connectors/memory.d.cts +6 -0
- package/dist/lib/current-context.d.cts +3 -0
- package/dist/lib/globalize.d.cts +10 -0
- package/dist/lib/loopback.cjs +6 -0
- package/dist/lib/loopback.d.cts +1 -0
- package/dist/lib/model.d.cts +1 -0
- package/dist/lib/persisted-model.cjs +7 -12
- package/dist/lib/persisted-model.d.cts +1 -0
- package/dist/lib/rate-limit/algorithms/common.cjs +37 -0
- package/dist/lib/rate-limit/algorithms/failure-lockout.cjs +70 -0
- package/dist/lib/rate-limit/algorithms/fixed-window.cjs +61 -0
- package/dist/lib/rate-limit/algorithms/index.cjs +55 -0
- package/dist/lib/rate-limit/config.cjs +107 -0
- package/dist/lib/rate-limit/errors.cjs +58 -0
- package/dist/lib/rate-limit/index.cjs +15 -0
- package/dist/lib/rate-limit/index.d.cts +1 -0
- package/dist/lib/rate-limit/initializer.cjs +92 -0
- package/dist/lib/rate-limit/initializer.d.cts +4 -0
- package/dist/lib/rate-limit/limiter.cjs +368 -0
- package/dist/lib/rate-limit/registry.cjs +92 -0
- package/dist/lib/rate-limit/remote.cjs +180 -0
- package/dist/lib/rate-limit/remote.d.cts +6 -0
- package/dist/lib/rate-limit/stores/persisted-model.cjs +353 -0
- package/dist/lib/rate-limit/subject.cjs +127 -0
- package/dist/lib/rate-limit/types.cjs +1 -0
- package/dist/lib/rate-limit/types.d.cts +167 -0
- package/dist/lib/registry.d.cts +1 -0
- package/dist/lib/runtime.d.cts +10 -0
- package/dist/lib/server-app.d.cts +3 -0
- package/dist/lib/two-factor/config.cjs +199 -0
- package/dist/lib/two-factor/config.d.cts +55 -0
- package/dist/lib/two-factor/crypto.cjs +68 -0
- package/dist/lib/two-factor/crypto.d.cts +16 -0
- package/dist/lib/two-factor/errors.cjs +58 -0
- package/dist/lib/two-factor/errors.d.cts +18 -0
- package/dist/lib/two-factor/rate-limit.cjs +153 -0
- package/dist/lib/two-factor/rate-limit.d.cts +22 -0
- package/dist/lib/two-factor/recovery-codes.cjs +49 -0
- package/dist/lib/two-factor/recovery-codes.d.cts +16 -0
- package/dist/lib/two-factor/service.cjs +1108 -0
- package/dist/lib/two-factor/service.d.cts +78 -0
- package/dist/lib/two-factor/totp.cjs +50 -0
- package/dist/lib/two-factor/totp.d.cts +14 -0
- package/dist/lib/utils.d.cts +36 -0
- package/dist/package.cjs +46 -10
- package/dist/server/middleware/context.d.cts +3 -0
- package/dist/server/middleware/error-handler.d.cts +3 -0
- package/dist/server/middleware/favicon.d.cts +7 -0
- package/dist/server/middleware/rest.d.cts +6 -0
- package/dist/server/middleware/static.d.cts +3 -0
- package/dist/server/middleware/status.d.cts +19 -0
- package/dist/server/middleware/token.d.cts +3 -0
- package/dist/server/middleware/url-not-found.d.cts +13 -0
- package/docs/api-explorer-details.md +110 -0
- package/docs/application-guide.md +1110 -0
- package/docs/assets/explorer-api.png +0 -0
- package/docs/assets/explorer-endpoint.png +0 -0
- package/docs/assets/explorer-listing.png +0 -0
- package/docs/assets/explorer-req-res.png +0 -0
- package/docs/assets/lb-modules.png +0 -0
- package/docs/assets/loopback-architecture.png +0 -0
- package/docs/assets/loopback-concepts.png +0 -0
- package/docs/assets/loopback_ov.png +0 -0
- package/docs/rate-limiting.md +879 -0
- package/docs/two-factor-authentication.md +611 -0
- package/docs/two-factor-authenticator-loss-runbook.md +117 -0
- package/package.json +56 -20
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_errors = require("../rate-limit/errors.cjs");
|
|
4
|
+
const require_config = require("../rate-limit/config.cjs");
|
|
5
|
+
//#region src/lib/two-factor/config.ts
|
|
6
|
+
function integer(value, fallback, name, minimum = 1, maximum = 31536e3) {
|
|
7
|
+
const result = value == null ? fallback : value;
|
|
8
|
+
if (!Number.isSafeInteger(result) || result < minimum || result > maximum) throw require_errors.rateLimitConfigurationError(`${name} is invalid`);
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
function parseKeyring(input, name, exactBytes) {
|
|
12
|
+
if (!input || !KEY_ID.test(input.currentKeyId || "") || !input.keys) throw require_errors.rateLimitConfigurationError(`${name} is invalid`);
|
|
13
|
+
const keys = {};
|
|
14
|
+
for (const [keyId, encoded] of Object.entries(input.keys)) {
|
|
15
|
+
if (!KEY_ID.test(keyId) || typeof encoded !== "string") throw require_errors.rateLimitConfigurationError(`${name} is invalid`);
|
|
16
|
+
const key = Buffer.from(encoded, "base64");
|
|
17
|
+
if (exactBytes != null && key.length !== exactBytes || exactBytes == null && key.length < 32) throw require_errors.rateLimitConfigurationError(`${name} contains an invalid key`);
|
|
18
|
+
keys[keyId] = key;
|
|
19
|
+
}
|
|
20
|
+
if (!keys[input.currentKeyId]) throw require_errors.rateLimitConfigurationError(`${name}.currentKeyId was not found`);
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
currentKeyId: input.currentKeyId,
|
|
23
|
+
keys: Object.freeze(keys)
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function sessionPolicy(value, fallback, name) {
|
|
27
|
+
const result = value || fallback;
|
|
28
|
+
if (result !== "revoke-all" && result !== "revoke-others") throw require_errors.rateLimitConfigurationError(`${name} is invalid`);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function normalizeTwoFactorConfig(input) {
|
|
32
|
+
if (input == null) return null;
|
|
33
|
+
const mode = input.mode || "disabled";
|
|
34
|
+
if (![
|
|
35
|
+
"disabled",
|
|
36
|
+
"verify-only",
|
|
37
|
+
"enroll-and-verify"
|
|
38
|
+
].includes(mode)) throw require_errors.rateLimitConfigurationError("twoFactor.mode is invalid");
|
|
39
|
+
if (mode === "disabled") return Object.freeze({ mode });
|
|
40
|
+
const algorithm = String(input.totp?.algorithm || "SHA1").toUpperCase();
|
|
41
|
+
if (algorithm !== "SHA1" && algorithm !== "SHA256") throw require_errors.rateLimitConfigurationError("twoFactor.totp.algorithm is invalid");
|
|
42
|
+
const digits = integer(input.totp?.digits, 6, "twoFactor.totp.digits", 6, 8);
|
|
43
|
+
if (digits !== 6 && digits !== 8) throw require_errors.rateLimitConfigurationError("twoFactor.totp.digits is invalid");
|
|
44
|
+
const challengeTtl = integer(input.challengeTtl, 300, "twoFactor.challengeTtl", 60, 600);
|
|
45
|
+
const issuer = String(input.issuer || "").trim();
|
|
46
|
+
if (!issuer) throw require_errors.rateLimitConfigurationError("twoFactor.issuer is required");
|
|
47
|
+
const leaseTtl = integer(input.verificationLeaseTtl, 30, "twoFactor.verificationLeaseTtl", 1, challengeTtl - 1);
|
|
48
|
+
const encryption = parseKeyring(input.encryption, "twoFactor.encryption", 32);
|
|
49
|
+
const recoveryCodeHmac = parseKeyring(input.recoveryCodeHmac, "twoFactor.recoveryCodeHmac");
|
|
50
|
+
const principalBindingHmac = parseKeyring(input.principalBindingHmac, "twoFactor.principalBindingHmac");
|
|
51
|
+
for (const recoveryKey of Object.values(recoveryCodeHmac.keys)) for (const bindingKey of Object.values(principalBindingHmac.keys)) if (crypto.timingSafeEqual(crypto.createHash("sha256").update(recoveryKey).digest(), crypto.createHash("sha256").update(bindingKey).digest())) throw require_errors.rateLimitConfigurationError("Two-factor HMAC keyrings must use distinct keys");
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
mode,
|
|
54
|
+
issuer,
|
|
55
|
+
challengeTtlMs: challengeTtl * 1e3,
|
|
56
|
+
verificationLeaseTtlMs: leaseTtl * 1e3,
|
|
57
|
+
enrollmentTtlMs: integer(input.enrollmentTtl, 600, "twoFactor.enrollmentTtl", 60, 3600) * 1e3,
|
|
58
|
+
maxAttempts: integer(input.maxAttempts, 5, "twoFactor.maxAttempts", 1, 10),
|
|
59
|
+
maxClockSkewSteps: integer(input.maxClockSkewSteps, 1, "twoFactor.maxClockSkewSteps", 0, 2),
|
|
60
|
+
totp: Object.freeze({
|
|
61
|
+
algorithm,
|
|
62
|
+
digits,
|
|
63
|
+
period: integer(input.totp?.period, 30, "twoFactor.totp.period", 15, 120),
|
|
64
|
+
secretBytes: integer(input.totp?.secretBytes, 20, "twoFactor.totp.secretBytes", 20, 64)
|
|
65
|
+
}),
|
|
66
|
+
recoveryCodeCount: integer(input.recoveryCodeCount, 10, "twoFactor.recoveryCodeCount", 1, 32),
|
|
67
|
+
recoveryCodeBytes: integer(input.recoveryCodeBytes, 16, "twoFactor.recoveryCodeBytes", 10, 32),
|
|
68
|
+
loginContextMaxBytes: integer(input.loginContextMaxBytes, 8192, "twoFactor.loginContextMaxBytes", 256, 65536),
|
|
69
|
+
sessions: Object.freeze({
|
|
70
|
+
onEnable: sessionPolicy(input.sessions?.onEnable, "revoke-others", "sessions.onEnable"),
|
|
71
|
+
onDisable: sessionPolicy(input.sessions?.onDisable, "revoke-all", "sessions.onDisable"),
|
|
72
|
+
onRecoveryCodesRegenerated: sessionPolicy(input.sessions?.onRecoveryCodesRegenerated, "revoke-others", "sessions.onRecoveryCodesRegenerated")
|
|
73
|
+
}),
|
|
74
|
+
encryption,
|
|
75
|
+
recoveryCodeHmac,
|
|
76
|
+
principalBindingHmac
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function failurePolicy(input, defaults, name, stateTtlMs) {
|
|
80
|
+
const merged = {
|
|
81
|
+
...defaults,
|
|
82
|
+
...input || {}
|
|
83
|
+
};
|
|
84
|
+
const strategy = merged.strategy || "exponential";
|
|
85
|
+
if (strategy !== "fixed" && strategy !== "exponential") throw require_errors.rateLimitConfigurationError(`${name}.strategy is invalid`);
|
|
86
|
+
return Object.freeze(require_config.normalizeFailureLockoutPolicy({
|
|
87
|
+
policyId: merged.policyId,
|
|
88
|
+
policyVersion: String(merged.policyVersion || "1"),
|
|
89
|
+
maxFailedAttempts: integer(merged.maxFailedAttempts, defaults.maxFailedAttempts, `${name}.maxFailedAttempts`, 1, 100),
|
|
90
|
+
observationWindowMs: integer(merged.observationWindow, defaults.observationWindow, `${name}.observationWindow`) * 1e3,
|
|
91
|
+
blockDurationMs: integer(merged.blockDuration, defaults.blockDuration, `${name}.blockDuration`) * 1e3,
|
|
92
|
+
strategy,
|
|
93
|
+
multiplier: integer(merged.multiplier, defaults.multiplier || 2, `${name}.multiplier`, 1, 10),
|
|
94
|
+
maxBlockDurationMs: integer(merged.maxBlockDuration, defaults.maxBlockDuration, `${name}.maxBlockDuration`) * 1e3,
|
|
95
|
+
backoffResetAfterMs: integer(merged.backoffResetAfter, defaults.backoffResetAfter, `${name}.backoffResetAfter`) * 1e3,
|
|
96
|
+
stateTtlMs: merged.stateTtl == null ? stateTtlMs : integer(merged.stateTtl, 0, `${name}.stateTtl`) * 1e3
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
function volumePolicy(input, defaults, name, stateTtlMs) {
|
|
100
|
+
const merged = {
|
|
101
|
+
...defaults,
|
|
102
|
+
...input || {}
|
|
103
|
+
};
|
|
104
|
+
return Object.freeze(require_config.normalizeFixedWindowPolicy({
|
|
105
|
+
policyId: merged.policyId,
|
|
106
|
+
policyVersion: String(merged.policyVersion || "1"),
|
|
107
|
+
maxAttempts: integer(merged.maxAttempts, defaults.maxAttempts, `${name}.maxAttempts`, 1, 1e5),
|
|
108
|
+
observationWindowMs: integer(merged.observationWindow, defaults.observationWindow, `${name}.observationWindow`) * 1e3,
|
|
109
|
+
blockDurationMs: integer(merged.blockDuration, defaults.blockDuration, `${name}.blockDuration`) * 1e3,
|
|
110
|
+
stateTtlMs: merged.stateTtl == null ? stateTtlMs : integer(merged.stateTtl, 0, `${name}.stateTtl`) * 1e3
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
function normalizeAuthenticationRateLimitConfig(input, modelName, applicationRateLimit = {}) {
|
|
114
|
+
if (input == null || input.enabled !== true) return null;
|
|
115
|
+
const usesApplicationStore = typeof input.store === "string";
|
|
116
|
+
const store = usesApplicationStore ? applicationRateLimit.stores?.[input.store] : input.store;
|
|
117
|
+
if (!store || typeof store.namespace !== "string" || !store.namespace.trim()) throw require_errors.rateLimitConfigurationError(`${modelName}.authenticationRateLimit.store is invalid`);
|
|
118
|
+
const stateTtlMs = integer(input.stateTtl, 604800, "authenticationRateLimit.stateTtl") * 1e3;
|
|
119
|
+
const storeName = usesApplicationStore ? input.store : `authentication-${modelName}`;
|
|
120
|
+
const keyringName = `authentication-${modelName}`;
|
|
121
|
+
const policies = input.policies || input;
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
enabled: true,
|
|
124
|
+
namespace: store.namespace,
|
|
125
|
+
storeName,
|
|
126
|
+
keyringName,
|
|
127
|
+
store: Object.freeze({ ...store }),
|
|
128
|
+
usesApplicationStore,
|
|
129
|
+
subjectHash: parseKeyring(input.subjectHash, "authenticationRateLimit.subjectHash"),
|
|
130
|
+
retryAfterGranularity: integer(input.retryAfterGranularity, 30, "authenticationRateLimit.retryAfterGranularity", 1, 3600),
|
|
131
|
+
stateTtlMs,
|
|
132
|
+
password: failurePolicy(policies.password, {
|
|
133
|
+
policyId: "authentication-password",
|
|
134
|
+
maxFailedAttempts: 5,
|
|
135
|
+
observationWindow: 900,
|
|
136
|
+
blockDuration: 60,
|
|
137
|
+
multiplier: 2,
|
|
138
|
+
maxBlockDuration: 3600,
|
|
139
|
+
backoffResetAfter: 86400
|
|
140
|
+
}, "authenticationRateLimit.password", stateTtlMs),
|
|
141
|
+
secondFactor: failurePolicy(policies.secondFactor, {
|
|
142
|
+
policyId: "authentication-second-factor",
|
|
143
|
+
maxFailedAttempts: 5,
|
|
144
|
+
observationWindow: 300,
|
|
145
|
+
blockDuration: 30,
|
|
146
|
+
multiplier: 2,
|
|
147
|
+
maxBlockDuration: 900,
|
|
148
|
+
backoffResetAfter: 86400
|
|
149
|
+
}, "authenticationRateLimit.secondFactor", stateTtlMs),
|
|
150
|
+
challengeIssuance: volumePolicy(policies.challengeIssuance, {
|
|
151
|
+
policyId: "authentication-challenge-issuance",
|
|
152
|
+
maxAttempts: 10,
|
|
153
|
+
observationWindow: 300,
|
|
154
|
+
blockDuration: 300
|
|
155
|
+
}, "authenticationRateLimit.challengeIssuance", stateTtlMs),
|
|
156
|
+
management: failurePolicy(policies.management, {
|
|
157
|
+
policyId: "authentication-management",
|
|
158
|
+
maxFailedAttempts: 3,
|
|
159
|
+
observationWindow: 600,
|
|
160
|
+
blockDuration: 300,
|
|
161
|
+
strategy: "fixed",
|
|
162
|
+
multiplier: 1,
|
|
163
|
+
maxBlockDuration: 300,
|
|
164
|
+
backoffResetAfter: 86400
|
|
165
|
+
}, "authenticationRateLimit.management", stateTtlMs),
|
|
166
|
+
source: Object.freeze({
|
|
167
|
+
enabled: input.source?.enabled !== false,
|
|
168
|
+
login: volumePolicy(input.source?.login, {
|
|
169
|
+
policyId: "authentication-source-login",
|
|
170
|
+
maxAttempts: 100,
|
|
171
|
+
observationWindow: 60,
|
|
172
|
+
blockDuration: 60
|
|
173
|
+
}, "authenticationRateLimit.source.login", stateTtlMs),
|
|
174
|
+
secondFactor: volumePolicy(input.source?.secondFactor, {
|
|
175
|
+
policyId: "authentication-source-second-factor",
|
|
176
|
+
maxAttempts: 60,
|
|
177
|
+
observationWindow: 60,
|
|
178
|
+
blockDuration: 60
|
|
179
|
+
}, "authenticationRateLimit.source.secondFactor", stateTtlMs)
|
|
180
|
+
})
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
var crypto, KEY_ID;
|
|
184
|
+
var init_config = require_runtime.__esmMin((() => {
|
|
185
|
+
require_config.init_config();
|
|
186
|
+
require_errors.init_errors();
|
|
187
|
+
crypto = require("crypto");
|
|
188
|
+
KEY_ID = /^[A-Za-z0-9._-]{1,64}$/;
|
|
189
|
+
}));
|
|
190
|
+
//#endregion
|
|
191
|
+
init_config();
|
|
192
|
+
Object.defineProperty(exports, "init_config", {
|
|
193
|
+
enumerable: true,
|
|
194
|
+
get: function() {
|
|
195
|
+
return init_config;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
exports.normalizeAuthenticationRateLimitConfig = normalizeAuthenticationRateLimitConfig;
|
|
199
|
+
exports.normalizeTwoFactorConfig = normalizeTwoFactorConfig;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/lib/two-factor/config.d.ts
|
|
2
|
+
interface BinaryKeyring {
|
|
3
|
+
currentKeyId: string;
|
|
4
|
+
keys: Record<string, Buffer>;
|
|
5
|
+
}
|
|
6
|
+
interface NormalizedTwoFactorConfig {
|
|
7
|
+
mode: 'disabled' | 'verify-only' | 'enroll-and-verify';
|
|
8
|
+
issuer: string;
|
|
9
|
+
challengeTtlMs: number;
|
|
10
|
+
verificationLeaseTtlMs: number;
|
|
11
|
+
enrollmentTtlMs: number;
|
|
12
|
+
maxAttempts: number;
|
|
13
|
+
maxClockSkewSteps: number;
|
|
14
|
+
totp: {
|
|
15
|
+
algorithm: 'SHA1' | 'SHA256';
|
|
16
|
+
digits: 6 | 8;
|
|
17
|
+
period: number;
|
|
18
|
+
secretBytes: number;
|
|
19
|
+
};
|
|
20
|
+
recoveryCodeCount: number;
|
|
21
|
+
recoveryCodeBytes: number;
|
|
22
|
+
loginContextMaxBytes: number;
|
|
23
|
+
sessions: {
|
|
24
|
+
onEnable: 'revoke-all' | 'revoke-others';
|
|
25
|
+
onDisable: 'revoke-all' | 'revoke-others';
|
|
26
|
+
onRecoveryCodesRegenerated: 'revoke-all' | 'revoke-others';
|
|
27
|
+
};
|
|
28
|
+
encryption: BinaryKeyring;
|
|
29
|
+
recoveryCodeHmac: BinaryKeyring;
|
|
30
|
+
principalBindingHmac: BinaryKeyring;
|
|
31
|
+
}
|
|
32
|
+
interface NormalizedAuthenticationRateLimitConfig {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
namespace: string;
|
|
35
|
+
storeName: string;
|
|
36
|
+
keyringName: string;
|
|
37
|
+
store: Record<string, any>;
|
|
38
|
+
usesApplicationStore: boolean;
|
|
39
|
+
subjectHash: BinaryKeyring;
|
|
40
|
+
retryAfterGranularity: number;
|
|
41
|
+
stateTtlMs: number;
|
|
42
|
+
password: Record<string, any>;
|
|
43
|
+
secondFactor: Record<string, any>;
|
|
44
|
+
challengeIssuance: Record<string, any>;
|
|
45
|
+
management: Record<string, any>;
|
|
46
|
+
source: {
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
login: Record<string, any>;
|
|
49
|
+
secondFactor: Record<string, any>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
declare function normalizeTwoFactorConfig(input: any): NormalizedTwoFactorConfig | null;
|
|
53
|
+
declare function normalizeAuthenticationRateLimitConfig(input: any, modelName: string, applicationRateLimit?: any): NormalizedAuthenticationRateLimitConfig | null;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { BinaryKeyring, NormalizedAuthenticationRateLimitConfig, NormalizedTwoFactorConfig, normalizeAuthenticationRateLimitConfig, normalizeTwoFactorConfig };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_subject = require("../rate-limit/subject.cjs");
|
|
4
|
+
//#region src/lib/two-factor/crypto.ts
|
|
5
|
+
function sha256(value) {
|
|
6
|
+
return crypto.createHash("sha256").update(value).digest("base64url");
|
|
7
|
+
}
|
|
8
|
+
function randomToken(bytes = 32) {
|
|
9
|
+
return crypto.randomBytes(bytes).toString("base64url");
|
|
10
|
+
}
|
|
11
|
+
function encrypt(plaintext, keyring, aadParts) {
|
|
12
|
+
const keyId = keyring.currentKeyId;
|
|
13
|
+
const nonce = crypto.randomBytes(12);
|
|
14
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", keyring.keys[keyId], nonce);
|
|
15
|
+
cipher.setAAD(require_subject.encodeLengthPrefixed(aadParts));
|
|
16
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
17
|
+
const envelope = {
|
|
18
|
+
v: 1,
|
|
19
|
+
k: keyId,
|
|
20
|
+
n: nonce.toString("base64url"),
|
|
21
|
+
c: ciphertext.toString("base64url"),
|
|
22
|
+
t: cipher.getAuthTag().toString("base64url")
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
envelope: Buffer.from(JSON.stringify(envelope)).toString("base64url"),
|
|
26
|
+
keyId
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function decrypt(encoded, keyring, aadParts) {
|
|
30
|
+
const envelope = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
31
|
+
if (envelope.v !== 1 || !keyring.keys[envelope.k]) throw new Error("Invalid encrypted envelope");
|
|
32
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", keyring.keys[envelope.k], Buffer.from(envelope.n, "base64url"));
|
|
33
|
+
decipher.setAAD(require_subject.encodeLengthPrefixed(aadParts));
|
|
34
|
+
decipher.setAuthTag(Buffer.from(envelope.t, "base64url"));
|
|
35
|
+
return Buffer.concat([decipher.update(Buffer.from(envelope.c, "base64url")), decipher.final()]);
|
|
36
|
+
}
|
|
37
|
+
function keyedDigest(domain, parts, keyring, keyId = keyring.currentKeyId) {
|
|
38
|
+
const key = keyring.keys[keyId];
|
|
39
|
+
if (!key) throw new Error("Unknown HMAC key");
|
|
40
|
+
return {
|
|
41
|
+
digest: crypto.createHmac("sha256", key).update(require_subject.encodeLengthPrefixed([domain, ...parts])).digest("base64url"),
|
|
42
|
+
keyId
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function timingSafeEqual(left, right) {
|
|
46
|
+
const a = Buffer.from(left);
|
|
47
|
+
const b = Buffer.from(right);
|
|
48
|
+
return a.length === b.length && crypto.timingSafeEqual(a, b);
|
|
49
|
+
}
|
|
50
|
+
var crypto;
|
|
51
|
+
var init_crypto = require_runtime.__esmMin((() => {
|
|
52
|
+
require_subject.init_subject();
|
|
53
|
+
crypto = require("crypto");
|
|
54
|
+
}));
|
|
55
|
+
//#endregion
|
|
56
|
+
init_crypto();
|
|
57
|
+
exports.decrypt = decrypt;
|
|
58
|
+
exports.encrypt = encrypt;
|
|
59
|
+
Object.defineProperty(exports, "init_crypto", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
get: function() {
|
|
62
|
+
return init_crypto;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
exports.keyedDigest = keyedDigest;
|
|
66
|
+
exports.randomToken = randomToken;
|
|
67
|
+
exports.sha256 = sha256;
|
|
68
|
+
exports.timingSafeEqual = timingSafeEqual;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BinaryKeyring } from "./config.cjs";
|
|
2
|
+
//#region src/lib/two-factor/crypto.d.ts
|
|
3
|
+
declare function sha256(value: string | Buffer): string;
|
|
4
|
+
declare function randomToken(bytes?: number): string;
|
|
5
|
+
declare function encrypt(plaintext: string | Buffer, keyring: BinaryKeyring, aadParts: ReadonlyArray<string | Buffer>): {
|
|
6
|
+
envelope: string;
|
|
7
|
+
keyId: string;
|
|
8
|
+
};
|
|
9
|
+
declare function decrypt(encoded: string, keyring: BinaryKeyring, aadParts: ReadonlyArray<string | Buffer>): Buffer;
|
|
10
|
+
declare function keyedDigest(domain: string, parts: ReadonlyArray<string | Buffer>, keyring: BinaryKeyring, keyId?: string): {
|
|
11
|
+
digest: string;
|
|
12
|
+
keyId: string;
|
|
13
|
+
};
|
|
14
|
+
declare function timingSafeEqual(left: string, right: string): boolean;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { decrypt, encrypt, keyedDigest, randomToken, sha256, timingSafeEqual };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
//#region src/lib/two-factor/errors.ts
|
|
4
|
+
function authError(code, statusCode, message = "Authentication failed") {
|
|
5
|
+
return new AuthenticationSecurityError(code, message, statusCode);
|
|
6
|
+
}
|
|
7
|
+
function invalidChallenge() {
|
|
8
|
+
return authError("INVALID_TWO_FACTOR_CHALLENGE", 401, "Invalid two-factor challenge");
|
|
9
|
+
}
|
|
10
|
+
function invalidFactor() {
|
|
11
|
+
return authError("INVALID_TWO_FACTOR_CODE", 401, "Invalid two-factor code");
|
|
12
|
+
}
|
|
13
|
+
function twoFactorRequired(challenge, expiresIn, methods = ["totp"]) {
|
|
14
|
+
return new AuthenticationSecurityError("TWO_FACTOR_REQUIRED", "Two-factor authentication is required", 403, { details: {
|
|
15
|
+
challengeId: challenge,
|
|
16
|
+
methods,
|
|
17
|
+
expiresIn
|
|
18
|
+
} });
|
|
19
|
+
}
|
|
20
|
+
function authRateLimited(blockedUntilMs, granularitySeconds = 1) {
|
|
21
|
+
const raw = blockedUntilMs == null ? 1 : Math.max(1, Math.ceil((blockedUntilMs - Date.now()) / 1e3));
|
|
22
|
+
const retryAfter = Math.ceil(raw / granularitySeconds) * granularitySeconds;
|
|
23
|
+
return new AuthenticationSecurityError("AUTHENTICATION_RATE_LIMITED", "Too many authentication attempts", 429, {
|
|
24
|
+
retryAfter,
|
|
25
|
+
details: { retryAfter }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
var AuthenticationSecurityError;
|
|
29
|
+
var init_errors = require_runtime.__esmMin((() => {
|
|
30
|
+
AuthenticationSecurityError = class extends Error {
|
|
31
|
+
code;
|
|
32
|
+
statusCode;
|
|
33
|
+
details;
|
|
34
|
+
retryAfter;
|
|
35
|
+
constructor(code, message, statusCode, options = {}) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "AuthenticationSecurityError";
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.statusCode = statusCode;
|
|
40
|
+
this.details = options.details;
|
|
41
|
+
this.retryAfter = options.retryAfter;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}));
|
|
45
|
+
//#endregion
|
|
46
|
+
init_errors();
|
|
47
|
+
exports.AuthenticationSecurityError = AuthenticationSecurityError;
|
|
48
|
+
exports.authError = authError;
|
|
49
|
+
exports.authRateLimited = authRateLimited;
|
|
50
|
+
Object.defineProperty(exports, "init_errors", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: function() {
|
|
53
|
+
return init_errors;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
exports.invalidChallenge = invalidChallenge;
|
|
57
|
+
exports.invalidFactor = invalidFactor;
|
|
58
|
+
exports.twoFactorRequired = twoFactorRequired;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/lib/two-factor/errors.d.ts
|
|
2
|
+
declare class AuthenticationSecurityError extends Error {
|
|
3
|
+
code: string;
|
|
4
|
+
statusCode: number;
|
|
5
|
+
details?: Record<string, unknown>;
|
|
6
|
+
retryAfter?: number;
|
|
7
|
+
constructor(code: string, message: string, statusCode: number, options?: {
|
|
8
|
+
details?: Record<string, unknown>;
|
|
9
|
+
retryAfter?: number;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
declare function authError(code: string, statusCode: number, message?: string): AuthenticationSecurityError;
|
|
13
|
+
declare function invalidChallenge(): AuthenticationSecurityError;
|
|
14
|
+
declare function invalidFactor(): AuthenticationSecurityError;
|
|
15
|
+
declare function twoFactorRequired(challenge: string, expiresIn: number, methods?: string[]): AuthenticationSecurityError;
|
|
16
|
+
declare function authRateLimited(blockedUntilMs: number | null, granularitySeconds?: number): AuthenticationSecurityError;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { AuthenticationSecurityError, authError, authRateLimited, invalidChallenge, invalidFactor, twoFactorRequired };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_config = require("../rate-limit/config.cjs");
|
|
4
|
+
const require_lib_two_factor_errors = require("./errors.cjs");
|
|
5
|
+
//#region src/lib/two-factor/rate-limit.ts
|
|
6
|
+
function resolvedPolicy(config, bucket) {
|
|
7
|
+
const value = config[bucket];
|
|
8
|
+
if (bucket === "challengeIssuance") return require_config.normalizeFixedWindowPolicy({
|
|
9
|
+
policyId: value.policyId || `authentication-${bucket}`,
|
|
10
|
+
policyVersion: value.policyVersion,
|
|
11
|
+
maxAttempts: value.maxAttempts,
|
|
12
|
+
observationWindowMs: value.observationWindowMs,
|
|
13
|
+
blockDurationMs: value.blockDurationMs,
|
|
14
|
+
stateTtlMs: value.stateTtlMs
|
|
15
|
+
});
|
|
16
|
+
return require_config.normalizeFailureLockoutPolicy({
|
|
17
|
+
policyId: value.policyId || `authentication-${bucket}`,
|
|
18
|
+
policyVersion: value.policyVersion,
|
|
19
|
+
maxFailedAttempts: value.maxFailedAttempts,
|
|
20
|
+
observationWindowMs: value.observationWindowMs,
|
|
21
|
+
blockDurationMs: value.blockDurationMs,
|
|
22
|
+
strategy: value.strategy,
|
|
23
|
+
multiplier: value.multiplier,
|
|
24
|
+
maxBlockDurationMs: value.maxBlockDurationMs,
|
|
25
|
+
backoffResetAfterMs: value.backoffResetAfterMs,
|
|
26
|
+
stateTtlMs: value.stateTtlMs
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function sourcePolicy(config, bucket) {
|
|
30
|
+
const value = config.source[bucket];
|
|
31
|
+
return require_config.normalizeFixedWindowPolicy({
|
|
32
|
+
policyId: value.policyId || `authentication-source-${bucket}`,
|
|
33
|
+
policyVersion: value.policyVersion,
|
|
34
|
+
maxAttempts: value.maxAttempts,
|
|
35
|
+
observationWindowMs: value.observationWindowMs,
|
|
36
|
+
blockDurationMs: value.blockDurationMs,
|
|
37
|
+
stateTtlMs: value.stateTtlMs
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
var AuthenticationRateLimiter;
|
|
41
|
+
var init_rate_limit = require_runtime.__esmMin((() => {
|
|
42
|
+
require_config.init_config();
|
|
43
|
+
require_lib_two_factor_errors.init_errors();
|
|
44
|
+
AuthenticationRateLimiter = class {
|
|
45
|
+
modelName;
|
|
46
|
+
config;
|
|
47
|
+
limiter;
|
|
48
|
+
constructor(modelName, config, limiter) {
|
|
49
|
+
this.modelName = modelName;
|
|
50
|
+
this.config = config;
|
|
51
|
+
this.limiter = limiter;
|
|
52
|
+
}
|
|
53
|
+
get sourceEnabled() {
|
|
54
|
+
return this.config.source.enabled;
|
|
55
|
+
}
|
|
56
|
+
rule(bucket, subjectType, canonicalValue) {
|
|
57
|
+
return {
|
|
58
|
+
ruleId: `authentication-${bucket}-${subjectType}`,
|
|
59
|
+
store: this.config.storeName,
|
|
60
|
+
subjectKeyring: this.config.keyringName,
|
|
61
|
+
subject: {
|
|
62
|
+
namespace: this.config.namespace,
|
|
63
|
+
scope: this.modelName,
|
|
64
|
+
subjectType,
|
|
65
|
+
canonicalValue
|
|
66
|
+
},
|
|
67
|
+
bucket,
|
|
68
|
+
policy: resolvedPolicy(this.config, bucket),
|
|
69
|
+
cost: 1,
|
|
70
|
+
onStoreError: "deny",
|
|
71
|
+
phase: subjectType === "identifier" ? "early" : "resolved",
|
|
72
|
+
order: 0,
|
|
73
|
+
shortCircuitOnDeny: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
sourceRule(bucket, source) {
|
|
77
|
+
return {
|
|
78
|
+
ruleId: `authentication-source-${bucket}`,
|
|
79
|
+
store: this.config.storeName,
|
|
80
|
+
subjectKeyring: this.config.keyringName,
|
|
81
|
+
subject: {
|
|
82
|
+
namespace: this.config.namespace,
|
|
83
|
+
scope: this.modelName,
|
|
84
|
+
subjectType: "source",
|
|
85
|
+
canonicalValue: source
|
|
86
|
+
},
|
|
87
|
+
bucket: `source-${bucket}`,
|
|
88
|
+
policy: sourcePolicy(this.config, bucket),
|
|
89
|
+
cost: 1,
|
|
90
|
+
onStoreError: "deny",
|
|
91
|
+
phase: "early",
|
|
92
|
+
order: -100,
|
|
93
|
+
shortCircuitOnDeny: true
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async inspect(rule) {
|
|
97
|
+
let result;
|
|
98
|
+
try {
|
|
99
|
+
result = await this.limiter.inspect(rule);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw this.translateStoreError(error);
|
|
102
|
+
}
|
|
103
|
+
if (!result.decision.allowed) throw require_lib_two_factor_errors.authRateLimited(result.decision.blockedUntilMs, this.config.retryAfterGranularity);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
async consume(rule) {
|
|
107
|
+
let result;
|
|
108
|
+
try {
|
|
109
|
+
result = await this.limiter.consume(rule);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw this.translateStoreError(error);
|
|
112
|
+
}
|
|
113
|
+
if (!result.decision.allowed) throw require_lib_two_factor_errors.authRateLimited(result.decision.blockedUntilMs, this.config.retryAfterGranularity);
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
async reset(observed) {
|
|
117
|
+
for (const item of observed) {
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = await this.limiter.reset(item.rule, item.revision);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw this.translateStoreError(error);
|
|
123
|
+
}
|
|
124
|
+
if (!result.allowed) throw require_lib_two_factor_errors.authRateLimited(result.blockedUntilMs, this.config.retryAfterGranularity);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async deletePrincipal(principalId) {
|
|
128
|
+
try {
|
|
129
|
+
await this.limiter.delete([
|
|
130
|
+
this.rule("password", "principal", principalId),
|
|
131
|
+
this.rule("secondFactor", "principal", principalId),
|
|
132
|
+
this.rule("challengeIssuance", "principal", principalId),
|
|
133
|
+
this.rule("management", "principal", principalId)
|
|
134
|
+
]);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw this.translateStoreError(error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
translateStoreError(error) {
|
|
140
|
+
if (error?.code === "RATE_LIMIT_UNAVAILABLE" || error?.code === "RATE_LIMIT_CONFIGURATION_INVALID") return require_lib_two_factor_errors.authError("AUTHENTICATION_RATE_LIMIT_UNAVAILABLE", 503, "Authentication rate limit service unavailable");
|
|
141
|
+
return error;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}));
|
|
145
|
+
//#endregion
|
|
146
|
+
init_rate_limit();
|
|
147
|
+
exports.AuthenticationRateLimiter = AuthenticationRateLimiter;
|
|
148
|
+
Object.defineProperty(exports, "init_rate_limit", {
|
|
149
|
+
enumerable: true,
|
|
150
|
+
get: function() {
|
|
151
|
+
return init_rate_limit;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ResolvedRateLimitRule } from "../rate-limit/types.cjs";
|
|
2
|
+
import { NormalizedAuthenticationRateLimitConfig } from "./config.cjs";
|
|
3
|
+
//#region src/lib/two-factor/rate-limit.d.ts
|
|
4
|
+
declare class AuthenticationRateLimiter {
|
|
5
|
+
private readonly modelName;
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly limiter;
|
|
8
|
+
constructor(modelName: string, config: NormalizedAuthenticationRateLimitConfig, limiter: any);
|
|
9
|
+
get sourceEnabled(): boolean;
|
|
10
|
+
rule(bucket: 'password' | 'secondFactor' | 'challengeIssuance' | 'management', subjectType: 'identifier' | 'principal', canonicalValue: string): ResolvedRateLimitRule;
|
|
11
|
+
sourceRule(bucket: 'login' | 'secondFactor', source: string): ResolvedRateLimitRule;
|
|
12
|
+
inspect(rule: ResolvedRateLimitRule): Promise<any>;
|
|
13
|
+
consume(rule: ResolvedRateLimitRule): Promise<any>;
|
|
14
|
+
reset(observed: Array<{
|
|
15
|
+
rule: ResolvedRateLimitRule;
|
|
16
|
+
revision: number;
|
|
17
|
+
}>): Promise<void>;
|
|
18
|
+
deletePrincipal(principalId: string): Promise<void>;
|
|
19
|
+
private translateStoreError;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { AuthenticationRateLimiter };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_lib_two_factor_crypto = require("./crypto.cjs");
|
|
4
|
+
//#region src/lib/two-factor/recovery-codes.ts
|
|
5
|
+
function normalizeRecoveryCode(value) {
|
|
6
|
+
if (typeof value !== "string") return null;
|
|
7
|
+
const normalized = value.replace(/[\s-]/g, "").toUpperCase();
|
|
8
|
+
return /^[A-Z2-7]+$/.test(normalized) ? normalized : null;
|
|
9
|
+
}
|
|
10
|
+
function formatRecoveryCode(value) {
|
|
11
|
+
return value.match(/.{1,5}/g).join("-");
|
|
12
|
+
}
|
|
13
|
+
function generateRecoveryCodes(count, bytes) {
|
|
14
|
+
const unique = /* @__PURE__ */ new Set();
|
|
15
|
+
for (let attempts = 0; unique.size < count && attempts < count * 10; attempts++) unique.add(otplib.generateSecret({ length: bytes }));
|
|
16
|
+
if (unique.size !== count) throw new Error("Unable to generate unique recovery codes");
|
|
17
|
+
const normalized = Array.from(unique);
|
|
18
|
+
return {
|
|
19
|
+
setId: require_lib_two_factor_crypto.randomToken(24),
|
|
20
|
+
codes: normalized.map(formatRecoveryCode),
|
|
21
|
+
normalized
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function recoveryCodeId(credentialId, setId, normalizedCode, keyring, keyId = keyring.currentKeyId) {
|
|
25
|
+
return require_lib_two_factor_crypto.keyedDigest("two-factor-recovery-code:v1", [
|
|
26
|
+
credentialId,
|
|
27
|
+
setId,
|
|
28
|
+
normalizedCode
|
|
29
|
+
], keyring, keyId);
|
|
30
|
+
}
|
|
31
|
+
var otplib, RECOVERY_CODE_FORMAT_VERSION;
|
|
32
|
+
var init_recovery_codes = require_runtime.__esmMin((() => {
|
|
33
|
+
require_lib_two_factor_crypto.init_crypto();
|
|
34
|
+
otplib = require("otplib");
|
|
35
|
+
RECOVERY_CODE_FORMAT_VERSION = 1;
|
|
36
|
+
}));
|
|
37
|
+
//#endregion
|
|
38
|
+
init_recovery_codes();
|
|
39
|
+
exports.RECOVERY_CODE_FORMAT_VERSION = RECOVERY_CODE_FORMAT_VERSION;
|
|
40
|
+
exports.formatRecoveryCode = formatRecoveryCode;
|
|
41
|
+
exports.generateRecoveryCodes = generateRecoveryCodes;
|
|
42
|
+
Object.defineProperty(exports, "init_recovery_codes", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function() {
|
|
45
|
+
return init_recovery_codes;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
exports.normalizeRecoveryCode = normalizeRecoveryCode;
|
|
49
|
+
exports.recoveryCodeId = recoveryCodeId;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BinaryKeyring } from "./config.cjs";
|
|
2
|
+
//#region src/lib/two-factor/recovery-codes.d.ts
|
|
3
|
+
declare const RECOVERY_CODE_FORMAT_VERSION = 1;
|
|
4
|
+
declare function normalizeRecoveryCode(value: unknown): string | null;
|
|
5
|
+
declare function formatRecoveryCode(value: string): string;
|
|
6
|
+
declare function generateRecoveryCodes(count: number, bytes: number): {
|
|
7
|
+
setId: string;
|
|
8
|
+
codes: string[];
|
|
9
|
+
normalized: string[];
|
|
10
|
+
};
|
|
11
|
+
declare function recoveryCodeId(credentialId: string, setId: string, normalizedCode: string, keyring: BinaryKeyring, keyId?: string): {
|
|
12
|
+
digest: string;
|
|
13
|
+
keyId: string;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { RECOVERY_CODE_FORMAT_VERSION, formatRecoveryCode, generateRecoveryCodes, normalizeRecoveryCode, recoveryCodeId };
|