@camstack/core 0.1.32 → 0.1.34

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