@rebasepro/server 0.16.1-canary.gef08a6e → 0.17.0

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 (33) hide show
  1. package/dist/{admin_block-BeypnEfb.js → admin_block-BX6YULvJ.js} +8 -8
  2. package/dist/admin_block-BX6YULvJ.js.map +1 -0
  3. package/dist/api/ast-schema-editor.d.ts +1 -1
  4. package/dist/api/rest/idempotency.d.ts +1 -1
  5. package/dist/{ast-schema-editor-BrarYZCq.js → ast-schema-editor-BLADP9O2.js} +3 -3
  6. package/dist/ast-schema-editor-BLADP9O2.js.map +1 -0
  7. package/dist/auth/crypto-utils.d.ts +4 -0
  8. package/dist/auth/jwt-crypto.d.ts +91 -0
  9. package/dist/auth/jwt.d.ts +10 -9
  10. package/dist/auth/middleware.d.ts +1 -1
  11. package/dist/auth/rate-limiter.d.ts +10 -3
  12. package/dist/{auth-DU-nUjPp.js → auth-BVPx33qB.js} +49 -76
  13. package/dist/auth-BVPx33qB.js.map +1 -0
  14. package/dist/boot/fetch-bundle.d.ts +18 -1
  15. package/dist/collections/validate-config.d.ts +14 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.es.js +248 -66
  18. package/dist/index.es.js.map +1 -1
  19. package/dist/{jwt-DoHkMMWF.js → jwt-5VN4C6ln.js} +230 -38
  20. package/dist/jwt-5VN4C6ln.js.map +1 -0
  21. package/dist/{schema-editor-routes-Bf5h5Emf.js → schema-editor-routes-BWCIqZoT.js} +2 -2
  22. package/dist/{schema-editor-routes-Bf5h5Emf.js.map → schema-editor-routes-BWCIqZoT.js.map} +1 -1
  23. package/dist/src-B-CmIFMr.js.map +1 -1
  24. package/dist/src-Cdsw7DqV.js.map +1 -1
  25. package/dist/storage/keys.d.ts +21 -0
  26. package/dist/utils/ip-address.d.ts +30 -0
  27. package/dist/utils/portable-crypto.d.ts +71 -0
  28. package/dist/utils/request-id.d.ts +20 -0
  29. package/package.json +5 -5
  30. package/dist/admin_block-BeypnEfb.js.map +0 -1
  31. package/dist/ast-schema-editor-BrarYZCq.js.map +0 -1
  32. package/dist/auth-DU-nUjPp.js.map +0 -1
  33. package/dist/jwt-DoHkMMWF.js.map +0 -1
@@ -5,8 +5,7 @@ __rebaseCreateRequire(import.meta.url);
5
5
  import { i as __toESM, n as __exportAll, r as __require, t as __commonJSMin } from "./rolldown-runtime-dW7B1o5h.js";
6
6
  import "./src-Cdsw7DqV.js";
7
7
  import { t as logger } from "./logger-DS03e908.js";
8
- import { createHash, createPrivateKey, createPublicKey, randomBytes } from "crypto";
9
- import path from "node:path";
8
+ import { createPrivateKey, createPublicKey } from "crypto";
10
9
  //#region ../types/src/types/storage_source.ts
11
10
  /**
12
11
  * Describes a named storage backend — a place files live.
@@ -120,6 +119,105 @@ function normalizeStorageSources(declared, exported) {
120
119
  return Array.from(merged.values());
121
120
  }
122
121
  //#endregion
122
+ //#region src/utils/portable-crypto.ts
123
+ /**
124
+ * The cryptography the request path needs, on primitives every runtime has.
125
+ *
126
+ * `node:crypto` is the obvious way to hash a string in a Node process and it is
127
+ * the right one for anything that only ever runs during boot, a migration or a
128
+ * CLI command. On the *request* path it is the difference between code that
129
+ * could one day serve a request from somewhere other than a Node process and
130
+ * code that could not — see `contracts/portable-core.txt` and the gate that
131
+ * renders it.
132
+ *
133
+ * The trade is that WebCrypto's digest is **async** where `createHash` is
134
+ * synchronous. That is the whole cost of this file, it is paid at the call
135
+ * sites rather than here, and it is paid deliberately now: a sync-to-async
136
+ * change ripples through every caller, and doing it while the callers are few
137
+ * and the tests are green is enormously cheaper than doing it as one line item
138
+ * inside a runtime port.
139
+ *
140
+ * Not everything moves. Signing and key parsing (`auth/jwt-keys.ts`) still need
141
+ * `node:crypto`, because the portable replacement is a JWT library this package
142
+ * does not depend on yet. Those stay recorded in the contract file rather than
143
+ * hidden behind a wrapper that would imply they had moved.
144
+ *
145
+ * @module
146
+ */
147
+ var encoder = new TextEncoder();
148
+ /**
149
+ * SHA-256 of a string, lowercase hex — `createHash("sha256").digest("hex")`.
150
+ *
151
+ * Async because `crypto.subtle` is. Every current caller was already inside an
152
+ * `async` function awaiting a database round trip, so the cost at the call site
153
+ * is one keyword.
154
+ */
155
+ async function sha256Hex(input) {
156
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(input));
157
+ return hex(new Uint8Array(digest));
158
+ }
159
+ /**
160
+ * Constant-time string comparison, for secrets.
161
+ *
162
+ * Replaces `timingSafeEqual` over two padded Buffers. It stays synchronous —
163
+ * WebCrypto is not involved, and there was never a reason for this one to be
164
+ * async.
165
+ *
166
+ * The comparison is over UTF-8 **bytes**, never `String.length`: sizing a
167
+ * buffer by code units truncates on any multi-byte character, so the trailing
168
+ * bytes go unexamined and a guess matching everything but the final character
169
+ * of a secret containing one non-ASCII character compares equal. That is the
170
+ * exact failure this function exists to prevent, and it is why the encoding
171
+ * happens first.
172
+ *
173
+ * The length difference is folded into the same accumulator as the content
174
+ * rather than checked separately, so there is no second comparison to short-
175
+ * circuit. What remains observable is the *longer* of the two lengths, which is
176
+ * a property of the input the caller already sent.
177
+ */
178
+ function constantTimeEqual(a, b) {
179
+ const bytesA = encoder.encode(a);
180
+ const bytesB = encoder.encode(b);
181
+ let difference = bytesA.length ^ bytesB.length;
182
+ const length = Math.max(bytesA.length, bytesB.length);
183
+ for (let index = 0; index < length; index++) difference |= (bytesA[index] ?? 0) ^ (bytesB[index] ?? 0);
184
+ return difference === 0;
185
+ }
186
+ /**
187
+ * `bytes` cryptographically random bytes as lowercase hex —
188
+ * `randomBytes(n).toString("hex")`.
189
+ */
190
+ function randomHex(bytes) {
191
+ return hex(crypto.getRandomValues(new Uint8Array(bytes)));
192
+ }
193
+ /**
194
+ * A uniform integer in `[0, maxExclusive)` — `randomInt`, without `node:crypto`.
195
+ *
196
+ * Rejection sampling, not `% maxExclusive`: the modulo of a uniform 32-bit
197
+ * draw is biased towards the low values whenever the range does not divide
198
+ * 2^32, and these draws are one-time passcodes. The loop retries with
199
+ * probability under 1/2 per iteration for any range, so it terminates.
200
+ *
201
+ * The bound stops at 2^32 where `node:crypto`'s goes to 2^48, because one
202
+ * `Uint32Array` draw is all any caller has ever needed and a wider one is only
203
+ * worth writing when something needs it. A bound past the ceiling throws
204
+ * rather than silently narrowing.
205
+ */
206
+ function randomInt(maxExclusive) {
207
+ if (!Number.isInteger(maxExclusive) || maxExclusive <= 0 || maxExclusive > 2 ** 32) throw new RangeError(`randomInt needs an integer bound in (0, 2^32]; got ${maxExclusive}`);
208
+ const limit = Math.floor(2 ** 32 / maxExclusive) * maxExclusive;
209
+ const draw = /* @__PURE__ */ new Uint32Array(1);
210
+ for (;;) {
211
+ crypto.getRandomValues(draw);
212
+ if (draw[0] < limit) return draw[0] % maxExclusive;
213
+ }
214
+ }
215
+ function hex(bytes) {
216
+ let out = "";
217
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
218
+ return out;
219
+ }
220
+ //#endregion
123
221
  //#region ../../node_modules/.pnpm/safe-buffer@5.2.1/node_modules/safe-buffer/index.js
124
222
  var require_safe_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
125
223
  /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
@@ -349,14 +447,14 @@ var require_buffer_equal_constant_time = /* @__PURE__ */ __commonJSMin(((exports
349
447
  //#region ../../node_modules/.pnpm/jwa@2.0.1/node_modules/jwa/index.js
350
448
  var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
351
449
  var Buffer = require_safe_buffer().Buffer;
352
- var crypto = __require("crypto");
450
+ var crypto$1 = __require("crypto");
353
451
  var formatEcdsa = require_ecdsa_sig_formatter();
354
452
  var util$2 = __require("util");
355
453
  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\".";
356
454
  var MSG_INVALID_SECRET = "secret must be a string or buffer";
357
455
  var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer";
358
456
  var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object";
359
- var supportsKeyObjects = typeof crypto.createPublicKey === "function";
457
+ var supportsKeyObjects = typeof crypto$1.createPublicKey === "function";
360
458
  if (supportsKeyObjects) {
361
459
  MSG_INVALID_VERIFIER_KEY += " or a KeyObject";
362
460
  MSG_INVALID_SECRET += "or a KeyObject";
@@ -409,14 +507,14 @@ var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
409
507
  return function sign(thing, secret) {
410
508
  checkIsSecretKey(secret);
411
509
  thing = normalizeInput(thing);
412
- var hmac = crypto.createHmac("sha" + bits, secret);
510
+ var hmac = crypto$1.createHmac("sha" + bits, secret);
413
511
  return fromBase64((hmac.update(thing), hmac.digest("base64")));
414
512
  };
415
513
  }
416
514
  var bufferEqual;
417
- var timingSafeEqual = "timingSafeEqual" in crypto ? function timingSafeEqual(a, b) {
515
+ var timingSafeEqual = "timingSafeEqual" in crypto$1 ? function timingSafeEqual(a, b) {
418
516
  if (a.byteLength !== b.byteLength) return false;
419
- return crypto.timingSafeEqual(a, b);
517
+ return crypto$1.timingSafeEqual(a, b);
420
518
  } : function timingSafeEqual(a, b) {
421
519
  if (!bufferEqual) bufferEqual = require_buffer_equal_constant_time();
422
520
  return bufferEqual(a, b);
@@ -431,7 +529,7 @@ var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
431
529
  return function sign(thing, privateKey) {
432
530
  checkIsPrivateKey(privateKey);
433
531
  thing = normalizeInput(thing);
434
- var signer = crypto.createSign("RSA-SHA" + bits);
532
+ var signer = crypto$1.createSign("RSA-SHA" + bits);
435
533
  return fromBase64((signer.update(thing), signer.sign(privateKey, "base64")));
436
534
  };
437
535
  }
@@ -440,7 +538,7 @@ var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
440
538
  checkIsPublicKey(publicKey);
441
539
  thing = normalizeInput(thing);
442
540
  signature = toBase64(signature);
443
- var verifier = crypto.createVerify("RSA-SHA" + bits);
541
+ var verifier = crypto$1.createVerify("RSA-SHA" + bits);
444
542
  verifier.update(thing);
445
543
  return verifier.verify(publicKey, signature, "base64");
446
544
  };
@@ -449,11 +547,11 @@ var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
449
547
  return function sign(thing, privateKey) {
450
548
  checkIsPrivateKey(privateKey);
451
549
  thing = normalizeInput(thing);
452
- var signer = crypto.createSign("RSA-SHA" + bits);
550
+ var signer = crypto$1.createSign("RSA-SHA" + bits);
453
551
  return fromBase64((signer.update(thing), signer.sign({
454
552
  key: privateKey,
455
- padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
456
- saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
553
+ padding: crypto$1.constants.RSA_PKCS1_PSS_PADDING,
554
+ saltLength: crypto$1.constants.RSA_PSS_SALTLEN_DIGEST
457
555
  }, "base64")));
458
556
  };
459
557
  }
@@ -462,12 +560,12 @@ var require_jwa = /* @__PURE__ */ __commonJSMin(((exports, module) => {
462
560
  checkIsPublicKey(publicKey);
463
561
  thing = normalizeInput(thing);
464
562
  signature = toBase64(signature);
465
- var verifier = crypto.createVerify("RSA-SHA" + bits);
563
+ var verifier = crypto$1.createVerify("RSA-SHA" + bits);
466
564
  verifier.update(thing);
467
565
  return verifier.verify({
468
566
  key: publicKey,
469
- padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
470
- saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
567
+ padding: crypto$1.constants.RSA_PKCS1_PSS_PADDING,
568
+ saltLength: crypto$1.constants.RSA_PSS_SALTLEN_DIGEST
471
569
  }, signature, "base64");
472
570
  };
473
571
  }
@@ -4263,8 +4361,61 @@ var require_jsonwebtoken = /* @__PURE__ */ __commonJSMin(((exports, module) => {
4263
4361
  };
4264
4362
  }));
4265
4363
  //#endregion
4266
- //#region src/storage/keys.ts
4364
+ //#region src/auth/jwt-crypto.ts
4267
4365
  var import_jsonwebtoken = /* @__PURE__ */ __toESM(require_jsonwebtoken(), 1);
4366
+ /** Sign a set of claims. Rejects if the key and algorithm disagree. */
4367
+ async function signJwt(payload, key, options) {
4368
+ return import_jsonwebtoken.default.sign(payload, key, {
4369
+ algorithm: options.algorithm,
4370
+ ...options.expiresIn === void 0 ? {} : { expiresIn: options.expiresIn },
4371
+ ...options.keyid === void 0 ? {} : { keyid: options.keyid }
4372
+ });
4373
+ }
4374
+ /**
4375
+ * Verify a token against one key and an explicit algorithm list, and return its
4376
+ * claims. Rejects — never returns null — when the token is not valid.
4377
+ *
4378
+ * `algorithms` has no default and must not acquire one. Letting the verifier
4379
+ * read `alg` out of the token's own header is the canonical JWT vulnerability:
4380
+ * an attacker takes a published RSA public key, HMACs a payload of their
4381
+ * choosing with it, sets `alg: HS256`, and a verifier holding that same public
4382
+ * key as "the secret" agrees. The caller decides the algorithm from the key it
4383
+ * chose, and {@link decodeProtectedHeader} exists so it can choose that key
4384
+ * without trusting anything the token asserts.
4385
+ */
4386
+ async function verifyJwt(token, key, options) {
4387
+ return import_jsonwebtoken.default.verify(token, key, { algorithms: options.algorithms });
4388
+ }
4389
+ /**
4390
+ * The token's unverified header — `kid` and `alg` — or an empty object.
4391
+ *
4392
+ * Unverified is the point: this is read *in order to* pick the key that will
4393
+ * verify it, so it cannot itself be verified first. Nothing but key selection
4394
+ * may depend on it, and key selection then pins the algorithm.
4395
+ *
4396
+ * Implemented here rather than via the JWT library because it is plain
4397
+ * base64url and JSON, needs no cryptography, and there is no reason for the
4398
+ * portable half of this module to wait on the non-portable half.
4399
+ */
4400
+ function decodeProtectedHeader(token) {
4401
+ const encoded = token.split(".")[0];
4402
+ if (!encoded) return {};
4403
+ try {
4404
+ const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
4405
+ const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
4406
+ const header = JSON.parse(atob(padded));
4407
+ if (typeof header !== "object" || header === null) return {};
4408
+ const { kid, alg } = header;
4409
+ return {
4410
+ ...typeof kid === "string" ? { kid } : {},
4411
+ ...typeof alg === "string" ? { alg } : {}
4412
+ };
4413
+ } catch {
4414
+ return {};
4415
+ }
4416
+ }
4417
+ //#endregion
4418
+ //#region src/storage/keys.ts
4268
4419
  /**
4269
4420
  * Canonical storage keys and bucket names.
4270
4421
  *
@@ -4297,6 +4448,48 @@ var import_jsonwebtoken = /* @__PURE__ */ __toESM(require_jsonwebtoken(), 1);
4297
4448
  * `users/alice/..../bob/x` — still comfortably inside alice's prefix, which is
4298
4449
  * exactly right. Only a real `..` segment is refused.
4299
4450
  */
4451
+ /**
4452
+ * `path.posix.normalize`, without `node:path`.
4453
+ *
4454
+ * A storage key is a POSIX-shaped string that never touches a filesystem, so
4455
+ * reaching for `node:path` to fold `.` and `//` out of it was always a little
4456
+ * wrong on its own terms — on Windows the platform `path` would have applied
4457
+ * different rules to the same key. It also put the module that every storage
4458
+ * read, ownership row and audit line goes through on the list of things that
4459
+ * only run in a Node process.
4460
+ *
4461
+ * This is the algorithm Node implements, transcribed: segments are folded left
4462
+ * to right, `..` pops unless it would climb past the root of a relative path,
4463
+ * and both the leading and the trailing separator survive the round trip. The
4464
+ * `..` branch is unreachable from {@link canonicalStorageKey}, which refuses
4465
+ * those keys outright before it gets here — it exists so that this function is
4466
+ * *the* normalizer rather than a subset of one, and
4467
+ * `storage-keys.property.test.ts` holds it to that with a property test against
4468
+ * `path.posix.normalize` itself. Exported for exactly that — nothing outside
4469
+ * this module should be normalizing a key.
4470
+ */
4471
+ function normalizePosix(input) {
4472
+ if (input.length === 0) return ".";
4473
+ const isAbsolute = input.startsWith("/");
4474
+ const trailingSeparator = input.endsWith("/");
4475
+ const segments = [];
4476
+ for (const segment of input.split("/")) {
4477
+ if (segment === "" || segment === ".") continue;
4478
+ if (segment === "..") {
4479
+ if (segments.length > 0 && segments[segments.length - 1] !== "..") segments.pop();
4480
+ else if (!isAbsolute) segments.push("..");
4481
+ continue;
4482
+ }
4483
+ segments.push(segment);
4484
+ }
4485
+ const joined = segments.join("/");
4486
+ if (joined === "") {
4487
+ if (isAbsolute) return "/";
4488
+ return trailingSeparator ? "./" : ".";
4489
+ }
4490
+ const withTrailing = trailingSeparator ? `${joined}/` : joined;
4491
+ return isAbsolute ? `/${withTrailing}` : withTrailing;
4492
+ }
4300
4493
  /** Longest key accepted, in UTF-16 code units. Matches the previous cap. */
4301
4494
  var MAX_STORAGE_KEY_LENGTH = 1024;
4302
4495
  /**
@@ -4331,7 +4524,7 @@ function canonicalStorageKey(rawKey) {
4331
4524
  const withoutLeadingSlashes = rawKey.replace(/^\/+/, "");
4332
4525
  if (withoutLeadingSlashes === "") return "";
4333
4526
  const denotesDirectory = /(?:^|\/)\.?$/.test(withoutLeadingSlashes);
4334
- const normalized = path.posix.normalize(withoutLeadingSlashes);
4527
+ const normalized = normalizePosix(withoutLeadingSlashes);
4335
4528
  if (normalized === "." || normalized === "./") return "";
4336
4529
  const key = normalized.replace(/^\.\//, "").replace(/^\/+/, "");
4337
4530
  if (key === "") return "";
@@ -4629,7 +4822,7 @@ function isJwtConfigured() {
4629
4822
  /**
4630
4823
  * Generate an access token (short-lived, 1 hour by default)
4631
4824
  */
4632
- function generateAccessToken(uid, roles, aal = "aal1", customClaims) {
4825
+ async function generateAccessToken(uid, roles, aal = "aal1", customClaims) {
4633
4826
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4634
4827
  const payload = {
4635
4828
  uid,
@@ -4637,12 +4830,12 @@ function generateAccessToken(uid, roles, aal = "aal1", customClaims) {
4637
4830
  ...customClaims,
4638
4831
  aal
4639
4832
  };
4640
- if (activeSigningKey) return import_jsonwebtoken.default.sign(payload, activeSigningKey.privateKey, {
4833
+ if (activeSigningKey) return signJwt(payload, activeSigningKey.privateKey, {
4641
4834
  expiresIn: jwtConfig.accessExpiresIn,
4642
4835
  algorithm: activeSigningKey.algorithm,
4643
4836
  keyid: activeSigningKey.kid
4644
4837
  });
4645
- return import_jsonwebtoken.default.sign(payload, jwtConfig.secret, {
4838
+ return signJwt(payload, jwtConfig.secret, {
4646
4839
  expiresIn: jwtConfig.accessExpiresIn,
4647
4840
  algorithm: "HS256"
4648
4841
  });
@@ -4682,12 +4875,12 @@ function getAccessTokenExpiry() {
4682
4875
  * checked explicitly: a token minted for reading a file is not a token for
4683
4876
  * being a user.
4684
4877
  */
4685
- function verifyAccessToken(token) {
4878
+ async function verifyAccessToken(token) {
4686
4879
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4687
4880
  try {
4688
- const header = import_jsonwebtoken.default.decode(token, { complete: true })?.header;
4689
- const namedKey = resolveVerificationKey(signingKeys, header?.kid);
4690
- const decoded = namedKey ? import_jsonwebtoken.default.verify(token, namedKey.publicKey, { algorithms: [namedKey.algorithm] }) : import_jsonwebtoken.default.verify(token, jwtConfig.secret, { algorithms: ["HS256"] });
4881
+ const header = decodeProtectedHeader(token);
4882
+ const namedKey = resolveVerificationKey(signingKeys, header.kid);
4883
+ const decoded = namedKey ? await verifyJwt(token, namedKey.publicKey, { algorithms: [namedKey.algorithm] }) : await verifyJwt(token, jwtConfig.secret, { algorithms: ["HS256"] });
4691
4884
  if (decoded.purpose) {
4692
4885
  logger.error("[JWT] Verification failed: a purpose-scoped token is not an access token", { purpose: decoded.purpose });
4693
4886
  return null;
@@ -4716,13 +4909,13 @@ function verifyAccessToken(token) {
4716
4909
  * Generate a random refresh token (long-lived, 30 days by default)
4717
4910
  */
4718
4911
  function generateRefreshToken() {
4719
- return randomBytes(40).toString("hex");
4912
+ return randomHex(40);
4720
4913
  }
4721
4914
  /**
4722
4915
  * Hash a refresh token for database storage (don't store raw tokens)
4723
4916
  */
4724
4917
  function hashRefreshToken(token) {
4725
- return createHash("sha256").update(token).digest("hex");
4918
+ return sha256Hex(token);
4726
4919
  }
4727
4920
  /**
4728
4921
  * The longest a cookie can live. Chrome (since 104) and RFC 6265bis silently
@@ -4778,9 +4971,9 @@ var MFA_PENDING_PURPOSE = "mfa-pending";
4778
4971
  * first factor may present the second, not a session to be carried around. Five
4779
4972
  * minutes matches the challenge TTL.
4780
4973
  */
4781
- function generateMfaPendingToken(uid, expiresInSeconds = 300) {
4974
+ async function generateMfaPendingToken(uid, expiresInSeconds = 300) {
4782
4975
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4783
- return import_jsonwebtoken.default.sign({
4976
+ return signJwt({
4784
4977
  purpose: MFA_PENDING_PURPOSE,
4785
4978
  uid
4786
4979
  }, jwtConfig.secret, {
@@ -4794,10 +4987,10 @@ function generateMfaPendingToken(uid, expiresInSeconds = 300) {
4794
4987
  * Returns `null` for anything else — including a perfectly valid *access*
4795
4988
  * token, which must not be interchangeable with this one in either direction.
4796
4989
  */
4797
- function verifyMfaPendingToken(token) {
4990
+ async function verifyMfaPendingToken(token) {
4798
4991
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4799
4992
  try {
4800
- const decoded = import_jsonwebtoken.default.verify(token, jwtConfig.secret, { algorithms: ["HS256"] });
4993
+ const decoded = await verifyJwt(token, jwtConfig.secret, { algorithms: ["HS256"] });
4801
4994
  if (decoded.purpose !== "mfa-pending" || !decoded.uid) return null;
4802
4995
  return { uid: decoded.uid };
4803
4996
  } catch {
@@ -4821,14 +5014,13 @@ function verifyMfaPendingToken(token) {
4821
5014
  * site that forgets to pass a named source produces a default-scoped token,
4822
5015
  * which fails closed at `/file/*` rather than over-granting.
4823
5016
  */
4824
- function generateDownloadToken(path, expiresInSeconds = 300, storageId) {
5017
+ async function generateDownloadToken(path, expiresInSeconds = 300, storageId) {
4825
5018
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4826
- const payload = {
5019
+ return signJwt({
4827
5020
  purpose: "file-read",
4828
5021
  path,
4829
5022
  storageId: canonicalStorageId(storageId)
4830
- };
4831
- return import_jsonwebtoken.default.sign(payload, jwtConfig.secret, {
5023
+ }, jwtConfig.secret, {
4832
5024
  expiresIn: expiresInSeconds,
4833
5025
  algorithm: "HS256"
4834
5026
  });
@@ -4843,10 +5035,10 @@ function generateDownloadToken(path, expiresInSeconds = 300, storageId) {
4843
5035
  * five-minute TTL — for at most that long after a deploy, an in-flight token
4844
5036
  * for a *named* source is refused and the client re-fetches `/metadata`.
4845
5037
  */
4846
- function verifyDownloadToken(token) {
5038
+ async function verifyDownloadToken(token) {
4847
5039
  if (!jwtConfig.secret) throw new Error("JWT secret not configured. Call configureJwt() first.");
4848
5040
  try {
4849
- const decoded = import_jsonwebtoken.default.verify(token, jwtConfig.secret, { algorithms: ["HS256"] });
5041
+ const decoded = await verifyJwt(token, jwtConfig.secret, { algorithms: ["HS256"] });
4850
5042
  if (decoded && decoded.purpose === "file-read" && typeof decoded.path === "string") return {
4851
5043
  purpose: "file-read",
4852
5044
  path: decoded.path,
@@ -4859,6 +5051,6 @@ function verifyDownloadToken(token) {
4859
5051
  }
4860
5052
  }
4861
5053
  //#endregion
4862
- export { canonicalStorageKey as C, findStorageSuffixCollision as D, DEFAULT_STORAGE_SOURCE_KEY as E, normalizeStorageSources as O, canonicalStorageId as S, require_jsonwebtoken as T, verifyMfaPendingToken as _, generateMfaPendingToken as a, InvalidStorageKeyError as b, getJwks as c, hasAsymmetricSigningKey as d, hashRefreshToken as f, verifyDownloadToken as g, verifyAccessToken as h, generateDownloadToken as i, storageEnvSuffix as k, getRefreshTokenExpiry as l, jwt_exports as m, configureJwt as n, generateRefreshToken as o, isJwtConfigured as p, generateAccessToken as r, getAccessTokenExpiry as s, MAX_COOKIE_AGE_MS as t, getRefreshTokenTtlMs as u, normalizePemFromEnv as v, tryCanonicalStorageKey as w, canonicalStorageBucket as x, InvalidStorageBucketError as y };
5054
+ export { findStorageSuffixCollision as A, canonicalStorageKey as C, randomInt as D, constantTimeEqual as E, storageEnvSuffix as M, sha256Hex as O, canonicalStorageId as S, require_jsonwebtoken as T, verifyMfaPendingToken as _, generateMfaPendingToken as a, InvalidStorageKeyError as b, getJwks as c, hasAsymmetricSigningKey as d, hashRefreshToken as f, verifyDownloadToken as g, verifyAccessToken as h, generateDownloadToken as i, normalizeStorageSources as j, DEFAULT_STORAGE_SOURCE_KEY as k, getRefreshTokenExpiry as l, jwt_exports as m, configureJwt as n, generateRefreshToken as o, isJwtConfigured as p, generateAccessToken as r, getAccessTokenExpiry as s, MAX_COOKIE_AGE_MS as t, getRefreshTokenTtlMs as u, normalizePemFromEnv as v, tryCanonicalStorageKey as w, canonicalStorageBucket as x, InvalidStorageBucketError as y };
4863
5055
 
4864
- //# sourceMappingURL=jwt-DoHkMMWF.js.map
5056
+ //# sourceMappingURL=jwt-5VN4C6ln.js.map