@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.
- package/dist/{admin_block-BeypnEfb.js → admin_block-BX6YULvJ.js} +8 -8
- package/dist/admin_block-BX6YULvJ.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +1 -1
- package/dist/api/rest/idempotency.d.ts +1 -1
- package/dist/{ast-schema-editor-BrarYZCq.js → ast-schema-editor-BLADP9O2.js} +3 -3
- package/dist/ast-schema-editor-BLADP9O2.js.map +1 -0
- package/dist/auth/crypto-utils.d.ts +4 -0
- package/dist/auth/jwt-crypto.d.ts +91 -0
- package/dist/auth/jwt.d.ts +10 -9
- package/dist/auth/middleware.d.ts +1 -1
- package/dist/auth/rate-limiter.d.ts +10 -3
- package/dist/{auth-DU-nUjPp.js → auth-BVPx33qB.js} +49 -76
- package/dist/auth-BVPx33qB.js.map +1 -0
- package/dist/boot/fetch-bundle.d.ts +18 -1
- package/dist/collections/validate-config.d.ts +14 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +248 -66
- package/dist/index.es.js.map +1 -1
- package/dist/{jwt-DoHkMMWF.js → jwt-5VN4C6ln.js} +230 -38
- package/dist/jwt-5VN4C6ln.js.map +1 -0
- package/dist/{schema-editor-routes-Bf5h5Emf.js → schema-editor-routes-BWCIqZoT.js} +2 -2
- package/dist/{schema-editor-routes-Bf5h5Emf.js.map → schema-editor-routes-BWCIqZoT.js.map} +1 -1
- package/dist/src-B-CmIFMr.js.map +1 -1
- package/dist/src-Cdsw7DqV.js.map +1 -1
- package/dist/storage/keys.d.ts +21 -0
- package/dist/utils/ip-address.d.ts +30 -0
- package/dist/utils/portable-crypto.d.ts +71 -0
- package/dist/utils/request-id.d.ts +20 -0
- package/package.json +5 -5
- package/dist/admin_block-BeypnEfb.js.map +0 -1
- package/dist/ast-schema-editor-BrarYZCq.js.map +0 -1
- package/dist/auth-DU-nUjPp.js.map +0 -1
- 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 {
|
|
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/
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
4689
|
-
const namedKey = resolveVerificationKey(signingKeys, header
|
|
4690
|
-
const decoded = namedKey ?
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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-
|
|
5056
|
+
//# sourceMappingURL=jwt-5VN4C6ln.js.map
|