@camstack/core 0.1.33 → 0.1.35
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/auth/api-key-manager.d.ts +2 -2
- package/dist/auth/api-key-manager.d.ts.map +1 -1
- package/dist/auth/auth-manager.d.ts +70 -3
- package/dist/auth/auth-manager.d.ts.map +1 -1
- package/dist/auth/totp-manager.d.ts +53 -0
- package/dist/auth/totp-manager.d.ts.map +1 -0
- package/dist/auth/user-manager.d.ts +3 -3
- package/dist/auth/user-manager.d.ts.map +1 -1
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts.map +1 -1
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js +29 -9
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js.map +1 -1
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs +29 -9
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs.map +1 -1
- package/dist/builtins/local-auth/auth-schema.d.ts +14 -0
- package/dist/builtins/local-auth/auth-schema.d.ts.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.d.ts +1 -0
- package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1014 -22
- package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1025 -33
- package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +14 -0
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts.map +1 -0
- package/dist/builtins/platform-probe/index.d.ts +2 -0
- package/dist/builtins/platform-probe/index.d.ts.map +1 -1
- package/dist/builtins/platform-probe/index.js +198 -5
- package/dist/builtins/platform-probe/index.js.map +1 -1
- package/dist/builtins/platform-probe/index.mjs +198 -6
- package/dist/builtins/platform-probe/index.mjs.map +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +8 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts.map +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +27 -21
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js.map +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +27 -21
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +558 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +558 -94
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -229,14 +229,14 @@ var require_buffer_equal_constant_time = /* @__PURE__ */ require_chunk.__commonJ
|
|
|
229
229
|
//#region ../../node_modules/jwa/index.js
|
|
230
230
|
var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
|
|
231
231
|
var Buffer = require_safe_buffer().Buffer;
|
|
232
|
-
var crypto = require("crypto");
|
|
232
|
+
var crypto$1 = require("crypto");
|
|
233
233
|
var formatEcdsa = require_ecdsa_sig_formatter();
|
|
234
234
|
var util$2 = require("util");
|
|
235
235
|
var MSG_INVALID_ALGORITHM = "\"%s\" is not a valid algorithm.\n Supported algorithms are:\n \"HS256\", \"HS384\", \"HS512\", \"RS256\", \"RS384\", \"RS512\", \"PS256\", \"PS384\", \"PS512\", \"ES256\", \"ES384\", \"ES512\" and \"none\".";
|
|
236
236
|
var MSG_INVALID_SECRET = "secret must be a string or buffer";
|
|
237
237
|
var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer";
|
|
238
238
|
var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object";
|
|
239
|
-
var supportsKeyObjects = typeof crypto.createPublicKey === "function";
|
|
239
|
+
var supportsKeyObjects = typeof crypto$1.createPublicKey === "function";
|
|
240
240
|
if (supportsKeyObjects) {
|
|
241
241
|
MSG_INVALID_VERIFIER_KEY += " or a KeyObject";
|
|
242
242
|
MSG_INVALID_SECRET += "or a KeyObject";
|
|
@@ -289,14 +289,14 @@ var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module)
|
|
|
289
289
|
return function sign(thing, secret) {
|
|
290
290
|
checkIsSecretKey(secret);
|
|
291
291
|
thing = normalizeInput(thing);
|
|
292
|
-
var hmac = crypto.createHmac("sha" + bits, secret);
|
|
292
|
+
var hmac = crypto$1.createHmac("sha" + bits, secret);
|
|
293
293
|
return fromBase64((hmac.update(thing), hmac.digest("base64")));
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
296
|
var bufferEqual;
|
|
297
|
-
var timingSafeEqual = "timingSafeEqual" in crypto ? function timingSafeEqual(a, b) {
|
|
297
|
+
var timingSafeEqual = "timingSafeEqual" in crypto$1 ? function timingSafeEqual(a, b) {
|
|
298
298
|
if (a.byteLength !== b.byteLength) return false;
|
|
299
|
-
return crypto.timingSafeEqual(a, b);
|
|
299
|
+
return crypto$1.timingSafeEqual(a, b);
|
|
300
300
|
} : function timingSafeEqual(a, b) {
|
|
301
301
|
if (!bufferEqual) bufferEqual = require_buffer_equal_constant_time();
|
|
302
302
|
return bufferEqual(a, b);
|
|
@@ -311,7 +311,7 @@ var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module)
|
|
|
311
311
|
return function sign(thing, privateKey) {
|
|
312
312
|
checkIsPrivateKey(privateKey);
|
|
313
313
|
thing = normalizeInput(thing);
|
|
314
|
-
var signer = crypto.createSign("RSA-SHA" + bits);
|
|
314
|
+
var signer = crypto$1.createSign("RSA-SHA" + bits);
|
|
315
315
|
return fromBase64((signer.update(thing), signer.sign(privateKey, "base64")));
|
|
316
316
|
};
|
|
317
317
|
}
|
|
@@ -320,7 +320,7 @@ var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module)
|
|
|
320
320
|
checkIsPublicKey(publicKey);
|
|
321
321
|
thing = normalizeInput(thing);
|
|
322
322
|
signature = toBase64(signature);
|
|
323
|
-
var verifier = crypto.createVerify("RSA-SHA" + bits);
|
|
323
|
+
var verifier = crypto$1.createVerify("RSA-SHA" + bits);
|
|
324
324
|
verifier.update(thing);
|
|
325
325
|
return verifier.verify(publicKey, signature, "base64");
|
|
326
326
|
};
|
|
@@ -329,11 +329,11 @@ var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module)
|
|
|
329
329
|
return function sign(thing, privateKey) {
|
|
330
330
|
checkIsPrivateKey(privateKey);
|
|
331
331
|
thing = normalizeInput(thing);
|
|
332
|
-
var signer = crypto.createSign("RSA-SHA" + bits);
|
|
332
|
+
var signer = crypto$1.createSign("RSA-SHA" + bits);
|
|
333
333
|
return fromBase64((signer.update(thing), signer.sign({
|
|
334
334
|
key: privateKey,
|
|
335
|
-
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
336
|
-
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
|
|
335
|
+
padding: crypto$1.constants.RSA_PKCS1_PSS_PADDING,
|
|
336
|
+
saltLength: crypto$1.constants.RSA_PSS_SALTLEN_DIGEST
|
|
337
337
|
}, "base64")));
|
|
338
338
|
};
|
|
339
339
|
}
|
|
@@ -342,12 +342,12 @@ var require_jwa = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module)
|
|
|
342
342
|
checkIsPublicKey(publicKey);
|
|
343
343
|
thing = normalizeInput(thing);
|
|
344
344
|
signature = toBase64(signature);
|
|
345
|
-
var verifier = crypto.createVerify("RSA-SHA" + bits);
|
|
345
|
+
var verifier = crypto$1.createVerify("RSA-SHA" + bits);
|
|
346
346
|
verifier.update(thing);
|
|
347
347
|
return verifier.verify({
|
|
348
348
|
key: publicKey,
|
|
349
|
-
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
350
|
-
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
|
|
349
|
+
padding: crypto$1.constants.RSA_PKCS1_PSS_PADDING,
|
|
350
|
+
saltLength: crypto$1.constants.RSA_PSS_SALTLEN_DIGEST
|
|
351
351
|
}, signature, "base64");
|
|
352
352
|
};
|
|
353
353
|
}
|
|
@@ -6210,7 +6210,7 @@ var AuthManager = class {
|
|
|
6210
6210
|
const payload = {
|
|
6211
6211
|
userId: opts.agentId,
|
|
6212
6212
|
username: opts.agentId,
|
|
6213
|
-
|
|
6213
|
+
isAdmin: true,
|
|
6214
6214
|
type: "service",
|
|
6215
6215
|
agentId: opts.agentId,
|
|
6216
6216
|
allowedProviders: "*",
|
|
@@ -6219,6 +6219,112 @@ var AuthManager = class {
|
|
|
6219
6219
|
const expiresIn = opts.expiresIn ?? "24h";
|
|
6220
6220
|
return import_jsonwebtoken.sign(payload, this.jwtSecret, { expiresIn });
|
|
6221
6221
|
}
|
|
6222
|
+
/**
|
|
6223
|
+
* Mint a short-lived HMAC-signed bridge token used by SSO-style auth
|
|
6224
|
+
* providers (OIDC, SAML, magic-link, …) to hand the post-callback
|
|
6225
|
+
* claims to `/api/auth/sso/finish` without trusting unsigned query
|
|
6226
|
+
* parameters. The endpoint verifies the token signature with the same
|
|
6227
|
+
* `jwtSecret` and only then mints the user session JWT. This closes
|
|
6228
|
+
* the privilege-escalation gap where any client could craft a
|
|
6229
|
+
* `?isAdmin=1` link to `/sso/finish` and become admin.
|
|
6230
|
+
*
|
|
6231
|
+
* The payload carries `kind: 'sso-bridge'` so `verifySsoBridgeToken`
|
|
6232
|
+
* can reject session tokens reused as bridge tokens (and vice versa).
|
|
6233
|
+
* TTL defaults to 5 minutes — long enough to survive the IdP
|
|
6234
|
+
* redirect bounce, short enough that a leaked URL stops being useful
|
|
6235
|
+
* before the operator notices.
|
|
6236
|
+
*/
|
|
6237
|
+
signSsoBridgeToken(payload, ttlSec = 300) {
|
|
6238
|
+
const body = {
|
|
6239
|
+
kind: "sso-bridge",
|
|
6240
|
+
userId: payload.userId,
|
|
6241
|
+
username: payload.username,
|
|
6242
|
+
isAdmin: payload.isAdmin,
|
|
6243
|
+
provider: payload.provider,
|
|
6244
|
+
...payload.email !== void 0 ? { email: payload.email } : {},
|
|
6245
|
+
...payload.displayName !== void 0 ? { displayName: payload.displayName } : {},
|
|
6246
|
+
...payload.hubUrl !== void 0 ? { hubUrl: payload.hubUrl } : {}
|
|
6247
|
+
};
|
|
6248
|
+
return import_jsonwebtoken.sign(body, this.jwtSecret, { expiresIn: ttlSec });
|
|
6249
|
+
}
|
|
6250
|
+
/**
|
|
6251
|
+
* Verify + decode a bridge token minted by `signSsoBridgeToken`.
|
|
6252
|
+
* Returns `null` for invalid signature, expired token, or wrong
|
|
6253
|
+
* `kind` claim. Never throws — the consumer ( `/sso/finish`) treats
|
|
6254
|
+
* `null` as 400 Bad Request.
|
|
6255
|
+
*/
|
|
6256
|
+
verifySsoBridgeToken(token) {
|
|
6257
|
+
try {
|
|
6258
|
+
const decoded = import_jsonwebtoken.verify(token, this.jwtSecret);
|
|
6259
|
+
if (decoded["kind"] !== "sso-bridge") return null;
|
|
6260
|
+
const userId = decoded["userId"];
|
|
6261
|
+
const username = decoded["username"];
|
|
6262
|
+
const isAdmin = decoded["isAdmin"];
|
|
6263
|
+
const provider = decoded["provider"];
|
|
6264
|
+
if (typeof userId !== "string" || typeof username !== "string" || typeof isAdmin !== "boolean" || typeof provider !== "string") return null;
|
|
6265
|
+
const email = typeof decoded["email"] === "string" ? decoded["email"] : void 0;
|
|
6266
|
+
const displayName = typeof decoded["displayName"] === "string" ? decoded["displayName"] : void 0;
|
|
6267
|
+
const hubUrl = typeof decoded["hubUrl"] === "string" ? decoded["hubUrl"] : void 0;
|
|
6268
|
+
return {
|
|
6269
|
+
userId,
|
|
6270
|
+
username,
|
|
6271
|
+
isAdmin,
|
|
6272
|
+
provider,
|
|
6273
|
+
...email !== void 0 ? { email } : {},
|
|
6274
|
+
...displayName !== void 0 ? { displayName } : {},
|
|
6275
|
+
...hubUrl !== void 0 ? { hubUrl } : {}
|
|
6276
|
+
};
|
|
6277
|
+
} catch {
|
|
6278
|
+
return null;
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
/**
|
|
6282
|
+
* Mint a TOTP challenge token bridging the two-step login flow:
|
|
6283
|
+
*
|
|
6284
|
+
* 1. POST /login → password OK + user has 2FA → returns
|
|
6285
|
+
* `{requiresTotp: true, challengeToken: '...'}` (this token).
|
|
6286
|
+
* 2. POST /login/totp-verify → operator types 6-digit code →
|
|
6287
|
+
* server verifies challenge token + code → mints the real
|
|
6288
|
+
* session JWT.
|
|
6289
|
+
*
|
|
6290
|
+
* The token carries `kind: 'totp-challenge'` so it can't be confused
|
|
6291
|
+
* with a regular session token, plus the `userId` + `username` +
|
|
6292
|
+
* `isAdmin` claims that will end up in the final session if the code
|
|
6293
|
+
* verifies. TTL defaults to 5 minutes — long enough to type the code,
|
|
6294
|
+
* short enough that an abandoned login can't be picked up later.
|
|
6295
|
+
*/
|
|
6296
|
+
signTotpChallengeToken(payload, ttlSec = 300) {
|
|
6297
|
+
return import_jsonwebtoken.sign({
|
|
6298
|
+
kind: "totp-challenge",
|
|
6299
|
+
userId: payload.userId,
|
|
6300
|
+
username: payload.username,
|
|
6301
|
+
isAdmin: payload.isAdmin
|
|
6302
|
+
}, this.jwtSecret, { expiresIn: ttlSec });
|
|
6303
|
+
}
|
|
6304
|
+
/**
|
|
6305
|
+
* Verify + decode a TOTP challenge token. Returns `null` for any
|
|
6306
|
+
* failure (invalid signature, expired, wrong `kind`, missing
|
|
6307
|
+
* claims). The caller MUST also verify the 6-digit code against
|
|
6308
|
+
* `totpManager.verify(userId, code)` before minting the real
|
|
6309
|
+
* session — this method validates the bridge transport only.
|
|
6310
|
+
*/
|
|
6311
|
+
verifyTotpChallengeToken(token) {
|
|
6312
|
+
try {
|
|
6313
|
+
const decoded = import_jsonwebtoken.verify(token, this.jwtSecret);
|
|
6314
|
+
if (decoded["kind"] !== "totp-challenge") return null;
|
|
6315
|
+
const userId = decoded["userId"];
|
|
6316
|
+
const username = decoded["username"];
|
|
6317
|
+
const isAdmin = decoded["isAdmin"];
|
|
6318
|
+
if (typeof userId !== "string" || typeof username !== "string" || typeof isAdmin !== "boolean") return null;
|
|
6319
|
+
return {
|
|
6320
|
+
userId,
|
|
6321
|
+
username,
|
|
6322
|
+
isAdmin
|
|
6323
|
+
};
|
|
6324
|
+
} catch {
|
|
6325
|
+
return null;
|
|
6326
|
+
}
|
|
6327
|
+
}
|
|
6222
6328
|
};
|
|
6223
6329
|
//#endregion
|
|
6224
6330
|
//#region src/auth/parse-record.ts
|
|
@@ -6255,7 +6361,7 @@ var UserManager = class {
|
|
|
6255
6361
|
id: node_crypto.randomUUID(),
|
|
6256
6362
|
username: input.username,
|
|
6257
6363
|
passwordHash,
|
|
6258
|
-
|
|
6364
|
+
isAdmin: input.isAdmin ?? false,
|
|
6259
6365
|
allowedProviders: input.allowedProviders ?? "*",
|
|
6260
6366
|
allowedDevices: input.allowedDevices ?? {},
|
|
6261
6367
|
scopes: input.scopes ?? [],
|
|
@@ -6343,7 +6449,7 @@ var UserManager = class {
|
|
|
6343
6449
|
await this.create({
|
|
6344
6450
|
username: adminUsername,
|
|
6345
6451
|
password: adminPassword,
|
|
6346
|
-
|
|
6452
|
+
isAdmin: true,
|
|
6347
6453
|
allowedProviders: "*",
|
|
6348
6454
|
allowedDevices: {}
|
|
6349
6455
|
});
|
|
@@ -6369,7 +6475,7 @@ var ApiKeyManager = class {
|
|
|
6369
6475
|
const record = {
|
|
6370
6476
|
id: node_crypto.randomUUID(),
|
|
6371
6477
|
label: input.label,
|
|
6372
|
-
|
|
6478
|
+
isAdmin: input.isAdmin ?? false,
|
|
6373
6479
|
allowedProviders: input.allowedProviders ?? "*",
|
|
6374
6480
|
allowedDevices: input.allowedDevices ?? {},
|
|
6375
6481
|
tokenHash: hash,
|
|
@@ -6563,10 +6669,841 @@ var ScopedTokenManager = class {
|
|
|
6563
6669
|
}
|
|
6564
6670
|
};
|
|
6565
6671
|
//#endregion
|
|
6672
|
+
//#region ../../node_modules/@otplib/plugin-crypto/index.js
|
|
6673
|
+
/**
|
|
6674
|
+
* @otplib/plugin-crypto
|
|
6675
|
+
*
|
|
6676
|
+
* @author Gerald Yeo <contact@fusedthought.com>
|
|
6677
|
+
* @version: 12.0.1
|
|
6678
|
+
* @license: MIT
|
|
6679
|
+
**/
|
|
6680
|
+
var require_plugin_crypto = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
6681
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6682
|
+
function _interopDefault(ex) {
|
|
6683
|
+
return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex;
|
|
6684
|
+
}
|
|
6685
|
+
var crypto = _interopDefault(require("crypto"));
|
|
6686
|
+
var createDigest = (algorithm, hmacKey, counter) => {
|
|
6687
|
+
return crypto.createHmac(algorithm, Buffer.from(hmacKey, "hex")).update(Buffer.from(counter, "hex")).digest().toString("hex");
|
|
6688
|
+
};
|
|
6689
|
+
var createRandomBytes = (size, encoding) => {
|
|
6690
|
+
return crypto.randomBytes(size).toString(encoding);
|
|
6691
|
+
};
|
|
6692
|
+
exports.createDigest = createDigest;
|
|
6693
|
+
exports.createRandomBytes = createRandomBytes;
|
|
6694
|
+
}));
|
|
6695
|
+
//#endregion
|
|
6696
|
+
//#region ../../node_modules/thirty-two/lib/thirty-two/thirty-two.js
|
|
6697
|
+
var require_thirty_two$1 = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
6698
|
+
var charTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
6699
|
+
var byteTable = [
|
|
6700
|
+
255,
|
|
6701
|
+
255,
|
|
6702
|
+
26,
|
|
6703
|
+
27,
|
|
6704
|
+
28,
|
|
6705
|
+
29,
|
|
6706
|
+
30,
|
|
6707
|
+
31,
|
|
6708
|
+
255,
|
|
6709
|
+
255,
|
|
6710
|
+
255,
|
|
6711
|
+
255,
|
|
6712
|
+
255,
|
|
6713
|
+
255,
|
|
6714
|
+
255,
|
|
6715
|
+
255,
|
|
6716
|
+
255,
|
|
6717
|
+
0,
|
|
6718
|
+
1,
|
|
6719
|
+
2,
|
|
6720
|
+
3,
|
|
6721
|
+
4,
|
|
6722
|
+
5,
|
|
6723
|
+
6,
|
|
6724
|
+
7,
|
|
6725
|
+
8,
|
|
6726
|
+
9,
|
|
6727
|
+
10,
|
|
6728
|
+
11,
|
|
6729
|
+
12,
|
|
6730
|
+
13,
|
|
6731
|
+
14,
|
|
6732
|
+
15,
|
|
6733
|
+
16,
|
|
6734
|
+
17,
|
|
6735
|
+
18,
|
|
6736
|
+
19,
|
|
6737
|
+
20,
|
|
6738
|
+
21,
|
|
6739
|
+
22,
|
|
6740
|
+
23,
|
|
6741
|
+
24,
|
|
6742
|
+
25,
|
|
6743
|
+
255,
|
|
6744
|
+
255,
|
|
6745
|
+
255,
|
|
6746
|
+
255,
|
|
6747
|
+
255,
|
|
6748
|
+
255,
|
|
6749
|
+
0,
|
|
6750
|
+
1,
|
|
6751
|
+
2,
|
|
6752
|
+
3,
|
|
6753
|
+
4,
|
|
6754
|
+
5,
|
|
6755
|
+
6,
|
|
6756
|
+
7,
|
|
6757
|
+
8,
|
|
6758
|
+
9,
|
|
6759
|
+
10,
|
|
6760
|
+
11,
|
|
6761
|
+
12,
|
|
6762
|
+
13,
|
|
6763
|
+
14,
|
|
6764
|
+
15,
|
|
6765
|
+
16,
|
|
6766
|
+
17,
|
|
6767
|
+
18,
|
|
6768
|
+
19,
|
|
6769
|
+
20,
|
|
6770
|
+
21,
|
|
6771
|
+
22,
|
|
6772
|
+
23,
|
|
6773
|
+
24,
|
|
6774
|
+
25,
|
|
6775
|
+
255,
|
|
6776
|
+
255,
|
|
6777
|
+
255,
|
|
6778
|
+
255,
|
|
6779
|
+
255
|
|
6780
|
+
];
|
|
6781
|
+
function quintetCount(buff) {
|
|
6782
|
+
var quintets = Math.floor(buff.length / 5);
|
|
6783
|
+
return buff.length % 5 === 0 ? quintets : quintets + 1;
|
|
6784
|
+
}
|
|
6785
|
+
exports.encode = function(plain) {
|
|
6786
|
+
if (!Buffer.isBuffer(plain)) plain = new Buffer(plain);
|
|
6787
|
+
var i = 0;
|
|
6788
|
+
var j = 0;
|
|
6789
|
+
var shiftIndex = 0;
|
|
6790
|
+
var digit = 0;
|
|
6791
|
+
var encoded = new Buffer(quintetCount(plain) * 8);
|
|
6792
|
+
while (i < plain.length) {
|
|
6793
|
+
var current = plain[i];
|
|
6794
|
+
if (shiftIndex > 3) {
|
|
6795
|
+
digit = current & 255 >> shiftIndex;
|
|
6796
|
+
shiftIndex = (shiftIndex + 5) % 8;
|
|
6797
|
+
digit = digit << shiftIndex | (i + 1 < plain.length ? plain[i + 1] : 0) >> 8 - shiftIndex;
|
|
6798
|
+
i++;
|
|
6799
|
+
} else {
|
|
6800
|
+
digit = current >> 8 - (shiftIndex + 5) & 31;
|
|
6801
|
+
shiftIndex = (shiftIndex + 5) % 8;
|
|
6802
|
+
if (shiftIndex === 0) i++;
|
|
6803
|
+
}
|
|
6804
|
+
encoded[j] = charTable.charCodeAt(digit);
|
|
6805
|
+
j++;
|
|
6806
|
+
}
|
|
6807
|
+
for (i = j; i < encoded.length; i++) encoded[i] = 61;
|
|
6808
|
+
return encoded;
|
|
6809
|
+
};
|
|
6810
|
+
exports.decode = function(encoded) {
|
|
6811
|
+
var shiftIndex = 0;
|
|
6812
|
+
var plainDigit = 0;
|
|
6813
|
+
var plainChar;
|
|
6814
|
+
var plainPos = 0;
|
|
6815
|
+
if (!Buffer.isBuffer(encoded)) encoded = new Buffer(encoded);
|
|
6816
|
+
var decoded = new Buffer(Math.ceil(encoded.length * 5 / 8));
|
|
6817
|
+
for (var i = 0; i < encoded.length; i++) {
|
|
6818
|
+
if (encoded[i] === 61) break;
|
|
6819
|
+
var encodedByte = encoded[i] - 48;
|
|
6820
|
+
if (encodedByte < byteTable.length) {
|
|
6821
|
+
plainDigit = byteTable[encodedByte];
|
|
6822
|
+
if (shiftIndex <= 3) {
|
|
6823
|
+
shiftIndex = (shiftIndex + 5) % 8;
|
|
6824
|
+
if (shiftIndex === 0) {
|
|
6825
|
+
plainChar |= plainDigit;
|
|
6826
|
+
decoded[plainPos] = plainChar;
|
|
6827
|
+
plainPos++;
|
|
6828
|
+
plainChar = 0;
|
|
6829
|
+
} else plainChar |= 255 & plainDigit << 8 - shiftIndex;
|
|
6830
|
+
} else {
|
|
6831
|
+
shiftIndex = (shiftIndex + 5) % 8;
|
|
6832
|
+
plainChar |= 255 & plainDigit >>> shiftIndex;
|
|
6833
|
+
decoded[plainPos] = plainChar;
|
|
6834
|
+
plainPos++;
|
|
6835
|
+
plainChar = 255 & plainDigit << 8 - shiftIndex;
|
|
6836
|
+
}
|
|
6837
|
+
} else throw new Error("Invalid input - it is not base32 encoded string");
|
|
6838
|
+
}
|
|
6839
|
+
return decoded.slice(0, plainPos);
|
|
6840
|
+
};
|
|
6841
|
+
}));
|
|
6842
|
+
//#endregion
|
|
6843
|
+
//#region ../../node_modules/thirty-two/lib/thirty-two/index.js
|
|
6844
|
+
var require_thirty_two = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
6845
|
+
var base32 = require_thirty_two$1();
|
|
6846
|
+
exports.encode = base32.encode;
|
|
6847
|
+
exports.decode = base32.decode;
|
|
6848
|
+
}));
|
|
6849
|
+
//#endregion
|
|
6850
|
+
//#region ../../node_modules/@otplib/plugin-thirty-two/index.js
|
|
6851
|
+
/**
|
|
6852
|
+
* @otplib/plugin-thirty-two
|
|
6853
|
+
*
|
|
6854
|
+
* @author Gerald Yeo <contact@fusedthought.com>
|
|
6855
|
+
* @version: 12.0.1
|
|
6856
|
+
* @license: MIT
|
|
6857
|
+
**/
|
|
6858
|
+
var require_plugin_thirty_two = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
6859
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6860
|
+
function _interopDefault(ex) {
|
|
6861
|
+
return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex;
|
|
6862
|
+
}
|
|
6863
|
+
var thirtyTwo = _interopDefault(require_thirty_two());
|
|
6864
|
+
var keyDecoder = (encodedSecret, encoding) => {
|
|
6865
|
+
return thirtyTwo.decode(encodedSecret).toString(encoding);
|
|
6866
|
+
};
|
|
6867
|
+
var keyEncoder = (secret, encoding) => {
|
|
6868
|
+
return thirtyTwo.encode(Buffer.from(secret, encoding).toString("ascii")).toString().replace(/=/g, "");
|
|
6869
|
+
};
|
|
6870
|
+
exports.keyDecoder = keyDecoder;
|
|
6871
|
+
exports.keyEncoder = keyEncoder;
|
|
6872
|
+
}));
|
|
6873
|
+
//#endregion
|
|
6874
|
+
//#region ../../node_modules/@otplib/core/index.js
|
|
6875
|
+
/**
|
|
6876
|
+
* @otplib/core
|
|
6877
|
+
*
|
|
6878
|
+
* @author Gerald Yeo <contact@fusedthought.com>
|
|
6879
|
+
* @version: 12.0.1
|
|
6880
|
+
* @license: MIT
|
|
6881
|
+
**/
|
|
6882
|
+
var require_core = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
6883
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6884
|
+
function objectValues(value) {
|
|
6885
|
+
return Object.keys(value).map((key) => value[key]);
|
|
6886
|
+
}
|
|
6887
|
+
(function(HashAlgorithms) {
|
|
6888
|
+
HashAlgorithms["SHA1"] = "sha1";
|
|
6889
|
+
HashAlgorithms["SHA256"] = "sha256";
|
|
6890
|
+
HashAlgorithms["SHA512"] = "sha512";
|
|
6891
|
+
})(exports.HashAlgorithms || (exports.HashAlgorithms = {}));
|
|
6892
|
+
var HASH_ALGORITHMS = objectValues(exports.HashAlgorithms);
|
|
6893
|
+
(function(KeyEncodings) {
|
|
6894
|
+
KeyEncodings["ASCII"] = "ascii";
|
|
6895
|
+
KeyEncodings["BASE64"] = "base64";
|
|
6896
|
+
KeyEncodings["HEX"] = "hex";
|
|
6897
|
+
KeyEncodings["LATIN1"] = "latin1";
|
|
6898
|
+
KeyEncodings["UTF8"] = "utf8";
|
|
6899
|
+
})(exports.KeyEncodings || (exports.KeyEncodings = {}));
|
|
6900
|
+
var KEY_ENCODINGS = objectValues(exports.KeyEncodings);
|
|
6901
|
+
(function(Strategy) {
|
|
6902
|
+
Strategy["HOTP"] = "hotp";
|
|
6903
|
+
Strategy["TOTP"] = "totp";
|
|
6904
|
+
})(exports.Strategy || (exports.Strategy = {}));
|
|
6905
|
+
var STRATEGY = objectValues(exports.Strategy);
|
|
6906
|
+
var createDigestPlaceholder = () => {
|
|
6907
|
+
throw new Error("Please provide an options.createDigest implementation.");
|
|
6908
|
+
};
|
|
6909
|
+
function isTokenValid(value) {
|
|
6910
|
+
return /^(\d+)$/.test(value);
|
|
6911
|
+
}
|
|
6912
|
+
function padStart(value, maxLength, fillString) {
|
|
6913
|
+
if (value.length >= maxLength) return value;
|
|
6914
|
+
return `${Array(maxLength + 1).join(fillString)}${value}`.slice(-1 * maxLength);
|
|
6915
|
+
}
|
|
6916
|
+
function keyuri(options) {
|
|
6917
|
+
const tmpl = `otpauth://${options.type}/{labelPrefix}:{accountName}?secret={secret}{query}`;
|
|
6918
|
+
const params = [];
|
|
6919
|
+
if (STRATEGY.indexOf(options.type) < 0) throw new Error(`Expecting options.type to be one of ${STRATEGY.join(", ")}. Received ${options.type}.`);
|
|
6920
|
+
if (options.type === "hotp") {
|
|
6921
|
+
if (options.counter == null || typeof options.counter !== "number") throw new Error("Expecting options.counter to be a number when options.type is \"hotp\".");
|
|
6922
|
+
params.push(`&counter=${options.counter}`);
|
|
6923
|
+
}
|
|
6924
|
+
if (options.type === "totp" && options.step) params.push(`&period=${options.step}`);
|
|
6925
|
+
if (options.digits) params.push(`&digits=${options.digits}`);
|
|
6926
|
+
if (options.algorithm) params.push(`&algorithm=${options.algorithm.toUpperCase()}`);
|
|
6927
|
+
if (options.issuer) params.push(`&issuer=${encodeURIComponent(options.issuer)}`);
|
|
6928
|
+
return tmpl.replace("{labelPrefix}", encodeURIComponent(options.issuer || options.accountName)).replace("{accountName}", encodeURIComponent(options.accountName)).replace("{secret}", options.secret).replace("{query}", params.join(""));
|
|
6929
|
+
}
|
|
6930
|
+
var OTP = class OTP {
|
|
6931
|
+
constructor(defaultOptions = {}) {
|
|
6932
|
+
this._defaultOptions = Object.freeze({ ...defaultOptions });
|
|
6933
|
+
this._options = Object.freeze({});
|
|
6934
|
+
}
|
|
6935
|
+
create(defaultOptions = {}) {
|
|
6936
|
+
return new OTP(defaultOptions);
|
|
6937
|
+
}
|
|
6938
|
+
clone(defaultOptions = {}) {
|
|
6939
|
+
const instance = this.create({
|
|
6940
|
+
...this._defaultOptions,
|
|
6941
|
+
...defaultOptions
|
|
6942
|
+
});
|
|
6943
|
+
instance.options = this._options;
|
|
6944
|
+
return instance;
|
|
6945
|
+
}
|
|
6946
|
+
get options() {
|
|
6947
|
+
return Object.freeze({
|
|
6948
|
+
...this._defaultOptions,
|
|
6949
|
+
...this._options
|
|
6950
|
+
});
|
|
6951
|
+
}
|
|
6952
|
+
set options(options) {
|
|
6953
|
+
this._options = Object.freeze({
|
|
6954
|
+
...this._options,
|
|
6955
|
+
...options
|
|
6956
|
+
});
|
|
6957
|
+
}
|
|
6958
|
+
allOptions() {
|
|
6959
|
+
return this.options;
|
|
6960
|
+
}
|
|
6961
|
+
resetOptions() {
|
|
6962
|
+
this._options = Object.freeze({});
|
|
6963
|
+
}
|
|
6964
|
+
};
|
|
6965
|
+
function hotpOptionsValidator(options) {
|
|
6966
|
+
if (typeof options.createDigest !== "function") throw new Error("Expecting options.createDigest to be a function.");
|
|
6967
|
+
if (typeof options.createHmacKey !== "function") throw new Error("Expecting options.createHmacKey to be a function.");
|
|
6968
|
+
if (typeof options.digits !== "number") throw new Error("Expecting options.digits to be a number.");
|
|
6969
|
+
if (!options.algorithm || HASH_ALGORITHMS.indexOf(options.algorithm) < 0) throw new Error(`Expecting options.algorithm to be one of ${HASH_ALGORITHMS.join(", ")}. Received ${options.algorithm}.`);
|
|
6970
|
+
if (!options.encoding || KEY_ENCODINGS.indexOf(options.encoding) < 0) throw new Error(`Expecting options.encoding to be one of ${KEY_ENCODINGS.join(", ")}. Received ${options.encoding}.`);
|
|
6971
|
+
}
|
|
6972
|
+
var hotpCreateHmacKey = (algorithm, secret, encoding) => {
|
|
6973
|
+
return Buffer.from(secret, encoding).toString("hex");
|
|
6974
|
+
};
|
|
6975
|
+
function hotpDefaultOptions() {
|
|
6976
|
+
return {
|
|
6977
|
+
algorithm: exports.HashAlgorithms.SHA1,
|
|
6978
|
+
createHmacKey: hotpCreateHmacKey,
|
|
6979
|
+
createDigest: createDigestPlaceholder,
|
|
6980
|
+
digits: 6,
|
|
6981
|
+
encoding: exports.KeyEncodings.ASCII
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6984
|
+
function hotpOptions(opt) {
|
|
6985
|
+
const options = {
|
|
6986
|
+
...hotpDefaultOptions(),
|
|
6987
|
+
...opt
|
|
6988
|
+
};
|
|
6989
|
+
hotpOptionsValidator(options);
|
|
6990
|
+
return Object.freeze(options);
|
|
6991
|
+
}
|
|
6992
|
+
function hotpCounter(counter) {
|
|
6993
|
+
return padStart(counter.toString(16), 16, "0");
|
|
6994
|
+
}
|
|
6995
|
+
function hotpDigestToToken(hexDigest, digits) {
|
|
6996
|
+
const digest = Buffer.from(hexDigest, "hex");
|
|
6997
|
+
const offset = digest[digest.length - 1] & 15;
|
|
6998
|
+
const token = ((digest[offset] & 127) << 24 | (digest[offset + 1] & 255) << 16 | (digest[offset + 2] & 255) << 8 | digest[offset + 3] & 255) % Math.pow(10, digits);
|
|
6999
|
+
return padStart(String(token), digits, "0");
|
|
7000
|
+
}
|
|
7001
|
+
function hotpDigest(secret, counter, options) {
|
|
7002
|
+
const hexCounter = hotpCounter(counter);
|
|
7003
|
+
const hmacKey = options.createHmacKey(options.algorithm, secret, options.encoding);
|
|
7004
|
+
return options.createDigest(options.algorithm, hmacKey, hexCounter);
|
|
7005
|
+
}
|
|
7006
|
+
function hotpToken(secret, counter, options) {
|
|
7007
|
+
return hotpDigestToToken(options.digest || hotpDigest(secret, counter, options), options.digits);
|
|
7008
|
+
}
|
|
7009
|
+
function hotpCheck(token, secret, counter, options) {
|
|
7010
|
+
if (!isTokenValid(token)) return false;
|
|
7011
|
+
return token === hotpToken(secret, counter, options);
|
|
7012
|
+
}
|
|
7013
|
+
function hotpKeyuri(accountName, issuer, secret, counter, options) {
|
|
7014
|
+
return keyuri({
|
|
7015
|
+
algorithm: options.algorithm,
|
|
7016
|
+
digits: options.digits,
|
|
7017
|
+
type: exports.Strategy.HOTP,
|
|
7018
|
+
accountName,
|
|
7019
|
+
counter,
|
|
7020
|
+
issuer,
|
|
7021
|
+
secret
|
|
7022
|
+
});
|
|
7023
|
+
}
|
|
7024
|
+
var HOTP = class HOTP extends OTP {
|
|
7025
|
+
create(defaultOptions = {}) {
|
|
7026
|
+
return new HOTP(defaultOptions);
|
|
7027
|
+
}
|
|
7028
|
+
allOptions() {
|
|
7029
|
+
return hotpOptions(this.options);
|
|
7030
|
+
}
|
|
7031
|
+
generate(secret, counter) {
|
|
7032
|
+
return hotpToken(secret, counter, this.allOptions());
|
|
7033
|
+
}
|
|
7034
|
+
check(token, secret, counter) {
|
|
7035
|
+
return hotpCheck(token, secret, counter, this.allOptions());
|
|
7036
|
+
}
|
|
7037
|
+
verify(opts) {
|
|
7038
|
+
if (typeof opts !== "object") throw new Error("Expecting argument 0 of verify to be an object");
|
|
7039
|
+
return this.check(opts.token, opts.secret, opts.counter);
|
|
7040
|
+
}
|
|
7041
|
+
keyuri(accountName, issuer, secret, counter) {
|
|
7042
|
+
return hotpKeyuri(accountName, issuer, secret, counter, this.allOptions());
|
|
7043
|
+
}
|
|
7044
|
+
};
|
|
7045
|
+
function parseWindowBounds(win) {
|
|
7046
|
+
if (typeof win === "number") return [Math.abs(win), Math.abs(win)];
|
|
7047
|
+
if (Array.isArray(win)) {
|
|
7048
|
+
const [past, future] = win;
|
|
7049
|
+
if (typeof past === "number" && typeof future === "number") return [Math.abs(past), Math.abs(future)];
|
|
7050
|
+
}
|
|
7051
|
+
throw new Error("Expecting options.window to be an number or [number, number].");
|
|
7052
|
+
}
|
|
7053
|
+
function totpOptionsValidator(options) {
|
|
7054
|
+
hotpOptionsValidator(options);
|
|
7055
|
+
parseWindowBounds(options.window);
|
|
7056
|
+
if (typeof options.epoch !== "number") throw new Error("Expecting options.epoch to be a number.");
|
|
7057
|
+
if (typeof options.step !== "number") throw new Error("Expecting options.step to be a number.");
|
|
7058
|
+
}
|
|
7059
|
+
var totpPadSecret = (secret, encoding, minLength) => {
|
|
7060
|
+
const currentLength = secret.length;
|
|
7061
|
+
const hexSecret = Buffer.from(secret, encoding).toString("hex");
|
|
7062
|
+
if (currentLength < minLength) {
|
|
7063
|
+
const newSecret = new Array(minLength - currentLength + 1).join(hexSecret);
|
|
7064
|
+
return Buffer.from(newSecret, "hex").slice(0, minLength).toString("hex");
|
|
7065
|
+
}
|
|
7066
|
+
return hexSecret;
|
|
7067
|
+
};
|
|
7068
|
+
var totpCreateHmacKey = (algorithm, secret, encoding) => {
|
|
7069
|
+
switch (algorithm) {
|
|
7070
|
+
case exports.HashAlgorithms.SHA1: return totpPadSecret(secret, encoding, 20);
|
|
7071
|
+
case exports.HashAlgorithms.SHA256: return totpPadSecret(secret, encoding, 32);
|
|
7072
|
+
case exports.HashAlgorithms.SHA512: return totpPadSecret(secret, encoding, 64);
|
|
7073
|
+
default: throw new Error(`Expecting algorithm to be one of ${HASH_ALGORITHMS.join(", ")}. Received ${algorithm}.`);
|
|
7074
|
+
}
|
|
7075
|
+
};
|
|
7076
|
+
function totpDefaultOptions() {
|
|
7077
|
+
return {
|
|
7078
|
+
algorithm: exports.HashAlgorithms.SHA1,
|
|
7079
|
+
createDigest: createDigestPlaceholder,
|
|
7080
|
+
createHmacKey: totpCreateHmacKey,
|
|
7081
|
+
digits: 6,
|
|
7082
|
+
encoding: exports.KeyEncodings.ASCII,
|
|
7083
|
+
epoch: Date.now(),
|
|
7084
|
+
step: 30,
|
|
7085
|
+
window: 0
|
|
7086
|
+
};
|
|
7087
|
+
}
|
|
7088
|
+
function totpOptions(opt) {
|
|
7089
|
+
const options = {
|
|
7090
|
+
...totpDefaultOptions(),
|
|
7091
|
+
...opt
|
|
7092
|
+
};
|
|
7093
|
+
totpOptionsValidator(options);
|
|
7094
|
+
return Object.freeze(options);
|
|
7095
|
+
}
|
|
7096
|
+
function totpCounter(epoch, step) {
|
|
7097
|
+
return Math.floor(epoch / step / 1e3);
|
|
7098
|
+
}
|
|
7099
|
+
function totpToken(secret, options) {
|
|
7100
|
+
return hotpToken(secret, totpCounter(options.epoch, options.step), options);
|
|
7101
|
+
}
|
|
7102
|
+
function totpEpochsInWindow(epoch, direction, deltaPerEpoch, numOfEpoches) {
|
|
7103
|
+
const result = [];
|
|
7104
|
+
if (numOfEpoches === 0) return result;
|
|
7105
|
+
for (let i = 1; i <= numOfEpoches; i++) {
|
|
7106
|
+
const delta = direction * i * deltaPerEpoch;
|
|
7107
|
+
result.push(epoch + delta);
|
|
7108
|
+
}
|
|
7109
|
+
return result;
|
|
7110
|
+
}
|
|
7111
|
+
function totpEpochAvailable(epoch, step, win) {
|
|
7112
|
+
const bounds = parseWindowBounds(win);
|
|
7113
|
+
const delta = step * 1e3;
|
|
7114
|
+
return {
|
|
7115
|
+
current: epoch,
|
|
7116
|
+
past: totpEpochsInWindow(epoch, -1, delta, bounds[0]),
|
|
7117
|
+
future: totpEpochsInWindow(epoch, 1, delta, bounds[1])
|
|
7118
|
+
};
|
|
7119
|
+
}
|
|
7120
|
+
function totpCheck(token, secret, options) {
|
|
7121
|
+
if (!isTokenValid(token)) return false;
|
|
7122
|
+
return token === totpToken(secret, options);
|
|
7123
|
+
}
|
|
7124
|
+
function totpCheckByEpoch(epochs, token, secret, options) {
|
|
7125
|
+
let position = null;
|
|
7126
|
+
epochs.some((epoch, idx) => {
|
|
7127
|
+
if (totpCheck(token, secret, {
|
|
7128
|
+
...options,
|
|
7129
|
+
epoch
|
|
7130
|
+
})) {
|
|
7131
|
+
position = idx + 1;
|
|
7132
|
+
return true;
|
|
7133
|
+
}
|
|
7134
|
+
return false;
|
|
7135
|
+
});
|
|
7136
|
+
return position;
|
|
7137
|
+
}
|
|
7138
|
+
function totpCheckWithWindow(token, secret, options) {
|
|
7139
|
+
if (totpCheck(token, secret, options)) return 0;
|
|
7140
|
+
const epochs = totpEpochAvailable(options.epoch, options.step, options.window);
|
|
7141
|
+
const backward = totpCheckByEpoch(epochs.past, token, secret, options);
|
|
7142
|
+
if (backward !== null) return backward * -1;
|
|
7143
|
+
return totpCheckByEpoch(epochs.future, token, secret, options);
|
|
7144
|
+
}
|
|
7145
|
+
function totpTimeUsed(epoch, step) {
|
|
7146
|
+
return Math.floor(epoch / 1e3) % step;
|
|
7147
|
+
}
|
|
7148
|
+
function totpTimeRemaining(epoch, step) {
|
|
7149
|
+
return step - totpTimeUsed(epoch, step);
|
|
7150
|
+
}
|
|
7151
|
+
function totpKeyuri(accountName, issuer, secret, options) {
|
|
7152
|
+
return keyuri({
|
|
7153
|
+
algorithm: options.algorithm,
|
|
7154
|
+
digits: options.digits,
|
|
7155
|
+
step: options.step,
|
|
7156
|
+
type: exports.Strategy.TOTP,
|
|
7157
|
+
accountName,
|
|
7158
|
+
issuer,
|
|
7159
|
+
secret
|
|
7160
|
+
});
|
|
7161
|
+
}
|
|
7162
|
+
var TOTP = class TOTP extends HOTP {
|
|
7163
|
+
create(defaultOptions = {}) {
|
|
7164
|
+
return new TOTP(defaultOptions);
|
|
7165
|
+
}
|
|
7166
|
+
allOptions() {
|
|
7167
|
+
return totpOptions(this.options);
|
|
7168
|
+
}
|
|
7169
|
+
generate(secret) {
|
|
7170
|
+
return totpToken(secret, this.allOptions());
|
|
7171
|
+
}
|
|
7172
|
+
checkDelta(token, secret) {
|
|
7173
|
+
return totpCheckWithWindow(token, secret, this.allOptions());
|
|
7174
|
+
}
|
|
7175
|
+
check(token, secret) {
|
|
7176
|
+
return typeof this.checkDelta(token, secret) === "number";
|
|
7177
|
+
}
|
|
7178
|
+
verify(opts) {
|
|
7179
|
+
if (typeof opts !== "object") throw new Error("Expecting argument 0 of verify to be an object");
|
|
7180
|
+
return this.check(opts.token, opts.secret);
|
|
7181
|
+
}
|
|
7182
|
+
timeRemaining() {
|
|
7183
|
+
const options = this.allOptions();
|
|
7184
|
+
return totpTimeRemaining(options.epoch, options.step);
|
|
7185
|
+
}
|
|
7186
|
+
timeUsed() {
|
|
7187
|
+
const options = this.allOptions();
|
|
7188
|
+
return totpTimeUsed(options.epoch, options.step);
|
|
7189
|
+
}
|
|
7190
|
+
keyuri(accountName, issuer, secret) {
|
|
7191
|
+
return totpKeyuri(accountName, issuer, secret, this.allOptions());
|
|
7192
|
+
}
|
|
7193
|
+
};
|
|
7194
|
+
function authenticatorOptionValidator(options) {
|
|
7195
|
+
totpOptionsValidator(options);
|
|
7196
|
+
if (typeof options.keyDecoder !== "function") throw new Error("Expecting options.keyDecoder to be a function.");
|
|
7197
|
+
if (options.keyEncoder && typeof options.keyEncoder !== "function") throw new Error("Expecting options.keyEncoder to be a function.");
|
|
7198
|
+
}
|
|
7199
|
+
function authenticatorDefaultOptions() {
|
|
7200
|
+
return {
|
|
7201
|
+
algorithm: exports.HashAlgorithms.SHA1,
|
|
7202
|
+
createDigest: createDigestPlaceholder,
|
|
7203
|
+
createHmacKey: totpCreateHmacKey,
|
|
7204
|
+
digits: 6,
|
|
7205
|
+
encoding: exports.KeyEncodings.HEX,
|
|
7206
|
+
epoch: Date.now(),
|
|
7207
|
+
step: 30,
|
|
7208
|
+
window: 0
|
|
7209
|
+
};
|
|
7210
|
+
}
|
|
7211
|
+
function authenticatorOptions(opt) {
|
|
7212
|
+
const options = {
|
|
7213
|
+
...authenticatorDefaultOptions(),
|
|
7214
|
+
...opt
|
|
7215
|
+
};
|
|
7216
|
+
authenticatorOptionValidator(options);
|
|
7217
|
+
return Object.freeze(options);
|
|
7218
|
+
}
|
|
7219
|
+
function authenticatorEncoder(secret, options) {
|
|
7220
|
+
return options.keyEncoder(secret, options.encoding);
|
|
7221
|
+
}
|
|
7222
|
+
function authenticatorDecoder(secret, options) {
|
|
7223
|
+
return options.keyDecoder(secret, options.encoding);
|
|
7224
|
+
}
|
|
7225
|
+
function authenticatorGenerateSecret(numberOfBytes, options) {
|
|
7226
|
+
return authenticatorEncoder(options.createRandomBytes(numberOfBytes, options.encoding), options);
|
|
7227
|
+
}
|
|
7228
|
+
function authenticatorToken(secret, options) {
|
|
7229
|
+
return totpToken(authenticatorDecoder(secret, options), options);
|
|
7230
|
+
}
|
|
7231
|
+
function authenticatorCheckWithWindow(token, secret, options) {
|
|
7232
|
+
return totpCheckWithWindow(token, authenticatorDecoder(secret, options), options);
|
|
7233
|
+
}
|
|
7234
|
+
exports.Authenticator = class Authenticator extends TOTP {
|
|
7235
|
+
create(defaultOptions = {}) {
|
|
7236
|
+
return new Authenticator(defaultOptions);
|
|
7237
|
+
}
|
|
7238
|
+
allOptions() {
|
|
7239
|
+
return authenticatorOptions(this.options);
|
|
7240
|
+
}
|
|
7241
|
+
generate(secret) {
|
|
7242
|
+
return authenticatorToken(secret, this.allOptions());
|
|
7243
|
+
}
|
|
7244
|
+
checkDelta(token, secret) {
|
|
7245
|
+
return authenticatorCheckWithWindow(token, secret, this.allOptions());
|
|
7246
|
+
}
|
|
7247
|
+
encode(secret) {
|
|
7248
|
+
return authenticatorEncoder(secret, this.allOptions());
|
|
7249
|
+
}
|
|
7250
|
+
decode(secret) {
|
|
7251
|
+
return authenticatorDecoder(secret, this.allOptions());
|
|
7252
|
+
}
|
|
7253
|
+
generateSecret(numberOfBytes = 10) {
|
|
7254
|
+
return authenticatorGenerateSecret(numberOfBytes, this.allOptions());
|
|
7255
|
+
}
|
|
7256
|
+
};
|
|
7257
|
+
exports.HASH_ALGORITHMS = HASH_ALGORITHMS;
|
|
7258
|
+
exports.HOTP = HOTP;
|
|
7259
|
+
exports.KEY_ENCODINGS = KEY_ENCODINGS;
|
|
7260
|
+
exports.OTP = OTP;
|
|
7261
|
+
exports.STRATEGY = STRATEGY;
|
|
7262
|
+
exports.TOTP = TOTP;
|
|
7263
|
+
exports.authenticatorCheckWithWindow = authenticatorCheckWithWindow;
|
|
7264
|
+
exports.authenticatorDecoder = authenticatorDecoder;
|
|
7265
|
+
exports.authenticatorDefaultOptions = authenticatorDefaultOptions;
|
|
7266
|
+
exports.authenticatorEncoder = authenticatorEncoder;
|
|
7267
|
+
exports.authenticatorGenerateSecret = authenticatorGenerateSecret;
|
|
7268
|
+
exports.authenticatorOptionValidator = authenticatorOptionValidator;
|
|
7269
|
+
exports.authenticatorOptions = authenticatorOptions;
|
|
7270
|
+
exports.authenticatorToken = authenticatorToken;
|
|
7271
|
+
exports.createDigestPlaceholder = createDigestPlaceholder;
|
|
7272
|
+
exports.hotpCheck = hotpCheck;
|
|
7273
|
+
exports.hotpCounter = hotpCounter;
|
|
7274
|
+
exports.hotpCreateHmacKey = hotpCreateHmacKey;
|
|
7275
|
+
exports.hotpDefaultOptions = hotpDefaultOptions;
|
|
7276
|
+
exports.hotpDigestToToken = hotpDigestToToken;
|
|
7277
|
+
exports.hotpKeyuri = hotpKeyuri;
|
|
7278
|
+
exports.hotpOptions = hotpOptions;
|
|
7279
|
+
exports.hotpOptionsValidator = hotpOptionsValidator;
|
|
7280
|
+
exports.hotpToken = hotpToken;
|
|
7281
|
+
exports.isTokenValid = isTokenValid;
|
|
7282
|
+
exports.keyuri = keyuri;
|
|
7283
|
+
exports.objectValues = objectValues;
|
|
7284
|
+
exports.padStart = padStart;
|
|
7285
|
+
exports.totpCheck = totpCheck;
|
|
7286
|
+
exports.totpCheckByEpoch = totpCheckByEpoch;
|
|
7287
|
+
exports.totpCheckWithWindow = totpCheckWithWindow;
|
|
7288
|
+
exports.totpCounter = totpCounter;
|
|
7289
|
+
exports.totpCreateHmacKey = totpCreateHmacKey;
|
|
7290
|
+
exports.totpDefaultOptions = totpDefaultOptions;
|
|
7291
|
+
exports.totpEpochAvailable = totpEpochAvailable;
|
|
7292
|
+
exports.totpKeyuri = totpKeyuri;
|
|
7293
|
+
exports.totpOptions = totpOptions;
|
|
7294
|
+
exports.totpOptionsValidator = totpOptionsValidator;
|
|
7295
|
+
exports.totpPadSecret = totpPadSecret;
|
|
7296
|
+
exports.totpTimeRemaining = totpTimeRemaining;
|
|
7297
|
+
exports.totpTimeUsed = totpTimeUsed;
|
|
7298
|
+
exports.totpToken = totpToken;
|
|
7299
|
+
}));
|
|
7300
|
+
//#endregion
|
|
7301
|
+
//#region ../../node_modules/@otplib/preset-default/index.js
|
|
7302
|
+
/**
|
|
7303
|
+
* @otplib/preset-default
|
|
7304
|
+
*
|
|
7305
|
+
* @author Gerald Yeo <contact@fusedthought.com>
|
|
7306
|
+
* @version: 12.0.1
|
|
7307
|
+
* @license: MIT
|
|
7308
|
+
**/
|
|
7309
|
+
var require_preset_default = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
7310
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7311
|
+
var pluginCrypto = require_plugin_crypto();
|
|
7312
|
+
var pluginThirtyTwo = require_plugin_thirty_two();
|
|
7313
|
+
var core = require_core();
|
|
7314
|
+
var hotp = new core.HOTP({ createDigest: pluginCrypto.createDigest });
|
|
7315
|
+
var totp = new core.TOTP({ createDigest: pluginCrypto.createDigest });
|
|
7316
|
+
exports.authenticator = new core.Authenticator({
|
|
7317
|
+
createDigest: pluginCrypto.createDigest,
|
|
7318
|
+
createRandomBytes: pluginCrypto.createRandomBytes,
|
|
7319
|
+
keyDecoder: pluginThirtyTwo.keyDecoder,
|
|
7320
|
+
keyEncoder: pluginThirtyTwo.keyEncoder
|
|
7321
|
+
});
|
|
7322
|
+
exports.hotp = hotp;
|
|
7323
|
+
exports.totp = totp;
|
|
7324
|
+
}));
|
|
7325
|
+
//#endregion
|
|
7326
|
+
//#region src/auth/totp-manager.ts
|
|
7327
|
+
var import_otplib = (/* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
|
|
7328
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7329
|
+
var presetDefault = require_preset_default();
|
|
7330
|
+
Object.keys(presetDefault).forEach(function(k) {
|
|
7331
|
+
if (k !== "default") Object.defineProperty(exports, k, {
|
|
7332
|
+
enumerable: true,
|
|
7333
|
+
get: function() {
|
|
7334
|
+
return presetDefault[k];
|
|
7335
|
+
}
|
|
7336
|
+
});
|
|
7337
|
+
});
|
|
7338
|
+
})))();
|
|
7339
|
+
/**
|
|
7340
|
+
* TOTP / 2FA enrollment manager.
|
|
7341
|
+
*
|
|
7342
|
+
* Three-state lifecycle keyed by `userId` (PK of `user_totp` collection):
|
|
7343
|
+
*
|
|
7344
|
+
* - No row → not set up. `setup()` inserts a row with a
|
|
7345
|
+
* fresh secret and `confirmedAt = null`.
|
|
7346
|
+
* - `confirmedAt = null` → setup in progress; user has the secret but
|
|
7347
|
+
* hasn't proved possession of the
|
|
7348
|
+
* authenticator yet. `verify()` returns
|
|
7349
|
+
* false even with a valid code — the row is
|
|
7350
|
+
* inert until confirmed.
|
|
7351
|
+
* - `confirmedAt != null` → enrolled; `verify()` accepts codes.
|
|
7352
|
+
*
|
|
7353
|
+
* Disable removes the row entirely. The next `setup()` starts fresh —
|
|
7354
|
+
* we do NOT reuse the old secret on re-enroll (avoids the case where a
|
|
7355
|
+
* leaked secret stays valid after a "rotation").
|
|
7356
|
+
*
|
|
7357
|
+
* The shared `authenticator` from `otplib` uses RFC 6238 TOTP with the
|
|
7358
|
+
* default 30-second window. We bump the validation window from 1 step
|
|
7359
|
+
* to 1 step in either direction (±30s) — operator clock skew is the
|
|
7360
|
+
* usual culprit for "valid code rejected" complaints.
|
|
7361
|
+
*/
|
|
7362
|
+
var TOTP_COLLECTION = "user_totp";
|
|
7363
|
+
var ISSUER = "CamStack";
|
|
7364
|
+
function parseRow(data) {
|
|
7365
|
+
const userId = data["userId"];
|
|
7366
|
+
const secret = data["secret"];
|
|
7367
|
+
const confirmedAt = data["confirmedAt"];
|
|
7368
|
+
const createdAt = data["createdAt"];
|
|
7369
|
+
if (typeof userId !== "string") throw new Error("user_totp row: missing userId");
|
|
7370
|
+
if (typeof secret !== "string") throw new Error("user_totp row: missing secret");
|
|
7371
|
+
if (typeof createdAt !== "number") throw new Error("user_totp row: missing createdAt");
|
|
7372
|
+
return {
|
|
7373
|
+
userId,
|
|
7374
|
+
secret,
|
|
7375
|
+
confirmedAt: typeof confirmedAt === "number" ? confirmedAt : null,
|
|
7376
|
+
createdAt
|
|
7377
|
+
};
|
|
7378
|
+
}
|
|
7379
|
+
var TotpManager = class {
|
|
7380
|
+
constructor(store, opts = {}) {
|
|
7381
|
+
this.store = store;
|
|
7382
|
+
this.opts = opts;
|
|
7383
|
+
import_otplib.authenticator.options = { window: opts.window ?? 1 };
|
|
7384
|
+
}
|
|
7385
|
+
/**
|
|
7386
|
+
* Begin enrollment. Always generates a fresh secret — calling `setup`
|
|
7387
|
+
* a second time on the same user replaces any pending half-enrollment
|
|
7388
|
+
* (the user must rescan the QR / re-enter the secret).
|
|
7389
|
+
*
|
|
7390
|
+
* The user identity (`label`) is what an authenticator displays as
|
|
7391
|
+
* the account name; we use the supplied `username` for human-readable
|
|
7392
|
+
* recognition. `ISSUER` is the CamStack brand.
|
|
7393
|
+
*/
|
|
7394
|
+
async setup(userId, username) {
|
|
7395
|
+
const secret = import_otplib.authenticator.generateSecret();
|
|
7396
|
+
const otpauthUrl = import_otplib.authenticator.keyuri(username, ISSUER, secret);
|
|
7397
|
+
const row = {
|
|
7398
|
+
userId,
|
|
7399
|
+
secret,
|
|
7400
|
+
confirmedAt: null,
|
|
7401
|
+
createdAt: Date.now()
|
|
7402
|
+
};
|
|
7403
|
+
await this.store.delete.mutate({
|
|
7404
|
+
collection: TOTP_COLLECTION,
|
|
7405
|
+
key: userId
|
|
7406
|
+
});
|
|
7407
|
+
await this.store.insert.mutate({
|
|
7408
|
+
collection: TOTP_COLLECTION,
|
|
7409
|
+
record: {
|
|
7410
|
+
id: userId,
|
|
7411
|
+
data: { ...row }
|
|
7412
|
+
}
|
|
7413
|
+
});
|
|
7414
|
+
return {
|
|
7415
|
+
secret,
|
|
7416
|
+
otpauthUrl
|
|
7417
|
+
};
|
|
7418
|
+
}
|
|
7419
|
+
/**
|
|
7420
|
+
* Confirm enrollment by checking a code from the authenticator
|
|
7421
|
+
* against the pending secret. On success, flips `confirmedAt` so
|
|
7422
|
+
* `verify()` starts accepting codes. Idempotent on already-confirmed
|
|
7423
|
+
* rows: re-confirming a valid code just succeeds with no state
|
|
7424
|
+
* change.
|
|
7425
|
+
*/
|
|
7426
|
+
async confirm(userId, code) {
|
|
7427
|
+
const row = await this.find(userId);
|
|
7428
|
+
if (!row) return false;
|
|
7429
|
+
if (!import_otplib.authenticator.check(code, row.secret)) return false;
|
|
7430
|
+
if (row.confirmedAt === null) {
|
|
7431
|
+
const next = {
|
|
7432
|
+
...row,
|
|
7433
|
+
confirmedAt: Date.now()
|
|
7434
|
+
};
|
|
7435
|
+
await this.store.update.mutate({
|
|
7436
|
+
collection: TOTP_COLLECTION,
|
|
7437
|
+
id: userId,
|
|
7438
|
+
data: { ...next }
|
|
7439
|
+
});
|
|
7440
|
+
}
|
|
7441
|
+
return true;
|
|
7442
|
+
}
|
|
7443
|
+
/**
|
|
7444
|
+
* Remove enrollment. Idempotent — no error if the user had no row.
|
|
7445
|
+
*/
|
|
7446
|
+
async disable(userId) {
|
|
7447
|
+
await this.store.delete.mutate({
|
|
7448
|
+
collection: TOTP_COLLECTION,
|
|
7449
|
+
key: userId
|
|
7450
|
+
});
|
|
7451
|
+
}
|
|
7452
|
+
/**
|
|
7453
|
+
* Verify a code against the confirmed secret. Returns `false` when:
|
|
7454
|
+
* - The user has no row (not set up).
|
|
7455
|
+
* - The row is pending (`confirmedAt === null`).
|
|
7456
|
+
* - The code doesn't match within the configured window.
|
|
7457
|
+
*/
|
|
7458
|
+
async verify(userId, code) {
|
|
7459
|
+
const row = await this.find(userId);
|
|
7460
|
+
if (!row) return false;
|
|
7461
|
+
if (row.confirmedAt === null) return false;
|
|
7462
|
+
return import_otplib.authenticator.check(code, row.secret);
|
|
7463
|
+
}
|
|
7464
|
+
/**
|
|
7465
|
+
* Read the user's enrollment status. Pending half-enrollments are
|
|
7466
|
+
* reported as `enabled: false` — only a confirmed row counts.
|
|
7467
|
+
*/
|
|
7468
|
+
async getStatus(userId) {
|
|
7469
|
+
const row = await this.find(userId);
|
|
7470
|
+
if (!row) return {
|
|
7471
|
+
enabled: false,
|
|
7472
|
+
confirmedAt: null
|
|
7473
|
+
};
|
|
7474
|
+
return {
|
|
7475
|
+
enabled: row.confirmedAt !== null,
|
|
7476
|
+
confirmedAt: row.confirmedAt
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
async find(userId) {
|
|
7480
|
+
const results = await this.store.query.query({
|
|
7481
|
+
collection: TOTP_COLLECTION,
|
|
7482
|
+
filter: { where: { userId } }
|
|
7483
|
+
});
|
|
7484
|
+
if (results.length === 0) return null;
|
|
7485
|
+
return parseRow(results[0].data);
|
|
7486
|
+
}
|
|
7487
|
+
};
|
|
7488
|
+
//#endregion
|
|
6566
7489
|
//#region src/builtins/local-auth/auth-schema.ts
|
|
6567
7490
|
var USERS_COLLECTION = "users";
|
|
6568
7491
|
var API_KEYS_COLLECTION = "api_keys";
|
|
6569
7492
|
var SCOPED_TOKENS_COLLECTION = "scoped_tokens";
|
|
7493
|
+
/**
|
|
7494
|
+
* TOTP enrollment table — split off from `users` to avoid the
|
|
7495
|
+
* destructive drop-+-recreate migration `ensureTable` performs when a
|
|
7496
|
+
* new column is added to a typed schema (see `sqlite-settings-backend.ts`).
|
|
7497
|
+
*
|
|
7498
|
+
* Lifecycle:
|
|
7499
|
+
* - Setup: row inserted with `confirmedAt = null` and a fresh secret.
|
|
7500
|
+
* - Confirm: a valid code from the secret flips `confirmedAt` to now().
|
|
7501
|
+
* - Disable: the row is deleted (next setup starts fresh).
|
|
7502
|
+
*
|
|
7503
|
+
* "Enabled" means `confirmedAt != null`. A half-enrolled row (`confirmedAt
|
|
7504
|
+
* = null`) is INACTIVE — `verifyTotp` returns false until confirmation.
|
|
7505
|
+
*/
|
|
7506
|
+
var USER_TOTP_COLLECTION = "user_totp";
|
|
6570
7507
|
var USERS_COLUMNS = [
|
|
6571
7508
|
{
|
|
6572
7509
|
name: "id",
|
|
@@ -6586,8 +7523,8 @@ var USERS_COLUMNS = [
|
|
|
6586
7523
|
notNull: true
|
|
6587
7524
|
},
|
|
6588
7525
|
{
|
|
6589
|
-
name: "
|
|
6590
|
-
type: "
|
|
7526
|
+
name: "isAdmin",
|
|
7527
|
+
type: "BOOLEAN",
|
|
6591
7528
|
notNull: true
|
|
6592
7529
|
},
|
|
6593
7530
|
{
|
|
@@ -6626,8 +7563,8 @@ var API_KEYS_COLUMNS = [
|
|
|
6626
7563
|
notNull: true
|
|
6627
7564
|
},
|
|
6628
7565
|
{
|
|
6629
|
-
name: "
|
|
6630
|
-
type: "
|
|
7566
|
+
name: "isAdmin",
|
|
7567
|
+
type: "BOOLEAN",
|
|
6631
7568
|
notNull: true
|
|
6632
7569
|
},
|
|
6633
7570
|
{
|
|
@@ -6659,6 +7596,28 @@ var API_KEYS_COLUMNS = [
|
|
|
6659
7596
|
type: "INTEGER"
|
|
6660
7597
|
}
|
|
6661
7598
|
];
|
|
7599
|
+
var USER_TOTP_COLUMNS = [
|
|
7600
|
+
{
|
|
7601
|
+
name: "userId",
|
|
7602
|
+
type: "TEXT",
|
|
7603
|
+
primaryKey: true,
|
|
7604
|
+
notNull: true
|
|
7605
|
+
},
|
|
7606
|
+
{
|
|
7607
|
+
name: "secret",
|
|
7608
|
+
type: "TEXT",
|
|
7609
|
+
notNull: true
|
|
7610
|
+
},
|
|
7611
|
+
{
|
|
7612
|
+
name: "confirmedAt",
|
|
7613
|
+
type: "INTEGER"
|
|
7614
|
+
},
|
|
7615
|
+
{
|
|
7616
|
+
name: "createdAt",
|
|
7617
|
+
type: "INTEGER",
|
|
7618
|
+
notNull: true
|
|
7619
|
+
}
|
|
7620
|
+
];
|
|
6662
7621
|
var SCOPED_TOKENS_COLUMNS = [
|
|
6663
7622
|
{
|
|
6664
7623
|
name: "id",
|
|
@@ -6732,6 +7691,10 @@ async function declareAuthSchema(store) {
|
|
|
6732
7691
|
columns: ["userId"]
|
|
6733
7692
|
}]
|
|
6734
7693
|
});
|
|
7694
|
+
await store.declareCollection.mutate({
|
|
7695
|
+
collection: USER_TOTP_COLLECTION,
|
|
7696
|
+
columns: USER_TOTP_COLUMNS
|
|
7697
|
+
});
|
|
6735
7698
|
}
|
|
6736
7699
|
//#endregion
|
|
6737
7700
|
//#region src/builtins/local-auth/local-auth.addon.ts
|
|
@@ -6740,7 +7703,7 @@ function toAuthResult(user) {
|
|
|
6740
7703
|
userId: user.id,
|
|
6741
7704
|
username: user.username,
|
|
6742
7705
|
displayName: user.username,
|
|
6743
|
-
|
|
7706
|
+
isAdmin: user.isAdmin
|
|
6744
7707
|
};
|
|
6745
7708
|
}
|
|
6746
7709
|
var LocalAuthAddon = class extends _camstack_types.BaseAddon {
|
|
@@ -6748,6 +7711,7 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
|
|
|
6748
7711
|
userManager = null;
|
|
6749
7712
|
apiKeyManager = null;
|
|
6750
7713
|
scopedTokenManager = null;
|
|
7714
|
+
totpManager = null;
|
|
6751
7715
|
constructor() {
|
|
6752
7716
|
super({
|
|
6753
7717
|
jwtSecret: "",
|
|
@@ -6780,6 +7744,7 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
|
|
|
6780
7744
|
this.userManager = new UserManager(storageAccess, this.authManager, reader);
|
|
6781
7745
|
this.apiKeyManager = new ApiKeyManager(storageAccess, this.authManager);
|
|
6782
7746
|
this.scopedTokenManager = new ScopedTokenManager(store);
|
|
7747
|
+
this.totpManager = new TotpManager(store);
|
|
6783
7748
|
try {
|
|
6784
7749
|
await this.userManager.ensureAdminExists();
|
|
6785
7750
|
const liveUsers = await this.userManager.listAll();
|
|
@@ -6895,6 +7860,33 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
|
|
|
6895
7860
|
listScopedTokens: async (input) => {
|
|
6896
7861
|
if (!this.scopedTokenManager) return [];
|
|
6897
7862
|
return this.scopedTokenManager.listForUser(input.userId);
|
|
7863
|
+
},
|
|
7864
|
+
setupTotp: async (input) => {
|
|
7865
|
+
if (!this.totpManager || !this.userManager) throw new Error("TOTP management not available");
|
|
7866
|
+
const user = await this.userManager.findById(input.userId);
|
|
7867
|
+
if (!user) throw new Error(`User not found: ${input.userId}`);
|
|
7868
|
+
return this.totpManager.setup(user.id, user.username);
|
|
7869
|
+
},
|
|
7870
|
+
confirmTotp: async (input) => {
|
|
7871
|
+
if (!this.totpManager) throw new Error("TOTP management not available");
|
|
7872
|
+
if (!await this.totpManager.confirm(input.userId, input.code)) throw new Error("TOTP confirmation failed — code did not match");
|
|
7873
|
+
return { success: true };
|
|
7874
|
+
},
|
|
7875
|
+
disableTotp: async (input) => {
|
|
7876
|
+
if (!this.totpManager) throw new Error("TOTP management not available");
|
|
7877
|
+
await this.totpManager.disable(input.userId);
|
|
7878
|
+
return { success: true };
|
|
7879
|
+
},
|
|
7880
|
+
getTotpStatus: async (input) => {
|
|
7881
|
+
if (!this.totpManager) return {
|
|
7882
|
+
enabled: false,
|
|
7883
|
+
confirmedAt: null
|
|
7884
|
+
};
|
|
7885
|
+
return this.totpManager.getStatus(input.userId);
|
|
7886
|
+
},
|
|
7887
|
+
verifyTotp: async (input) => {
|
|
7888
|
+
if (!this.totpManager) return { valid: false };
|
|
7889
|
+
return { valid: await this.totpManager.verify(input.userId, input.code) };
|
|
6898
7890
|
}
|
|
6899
7891
|
};
|
|
6900
7892
|
this.ctx.logger.info("registered auth-provider + user-management capabilities");
|