@palbase/backend 39.1.0 → 39.1.2
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/bin/palbase-backend.cjs +70 -40
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +1 -1
- package/dist/{chunk-JJSR4BUX.js → chunk-C525N4OW.js} +71 -41
- package/dist/chunk-C525N4OW.js.map +1 -0
- package/dist/engine/index.cjs +70 -40
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +1 -1
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +1 -1
- package/dist/{index-B0mjUnxy.d.cts → index-Brhp8cxj.d.cts} +9 -15
- package/dist/{index-yDno9Ju9.d.ts → index-Dlu2b9-f.d.ts} +9 -15
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +29 -0
- package/docs/auth.md +20 -0
- package/docs/llms-full.txt +49 -0
- package/package.json +2 -2
- package/dist/chunk-JJSR4BUX.js.map +0 -1
|
@@ -1354,6 +1354,11 @@ var DeclarationRefused = class extends Error {
|
|
|
1354
1354
|
};
|
|
1355
1355
|
|
|
1356
1356
|
// src/engine/auth.ts
|
|
1357
|
+
var KEYSET_RETRY_INTERVAL_MS = 1e3;
|
|
1358
|
+
function authUnavailable() {
|
|
1359
|
+
return markEngineRaised(new HttpError(503, "auth_unavailable", "Authentication is temporarily unavailable"));
|
|
1360
|
+
}
|
|
1361
|
+
__name(authUnavailable, "authUnavailable");
|
|
1357
1362
|
function b64urlToBytes(s) {
|
|
1358
1363
|
const pad = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
1359
1364
|
const full = pad.padEnd(Math.ceil(pad.length / 4) * 4, "=");
|
|
@@ -1370,6 +1375,8 @@ var AuthVerifier = class {
|
|
|
1370
1375
|
keys = /* @__PURE__ */ new Map();
|
|
1371
1376
|
fetchedAt = 0;
|
|
1372
1377
|
inflight = null;
|
|
1378
|
+
nextRefreshAt = 0;
|
|
1379
|
+
refreshFailed = false;
|
|
1373
1380
|
jwksUrl;
|
|
1374
1381
|
issuer;
|
|
1375
1382
|
fetchImpl;
|
|
@@ -1380,54 +1387,65 @@ var AuthVerifier = class {
|
|
|
1380
1387
|
this.fetchImpl = opts.fetchImpl ?? ((...a) => fetch(...a));
|
|
1381
1388
|
this.ttl = opts.keysetTtlMs ?? 5 * 6e4;
|
|
1382
1389
|
}
|
|
1383
|
-
/** Fetch
|
|
1384
|
-
|
|
1390
|
+
/** Fetch once concurrently. Known keys use their trust TTL; misses and
|
|
1391
|
+
* failures share a short cooldown so arbitrary kids cannot force a fetch
|
|
1392
|
+
* per request, and recovery does not wait for an entire trust TTL. */
|
|
1393
|
+
refresh() {
|
|
1385
1394
|
if (this.inflight) return this.inflight;
|
|
1386
|
-
this.inflight = (
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1395
|
+
this.inflight = this.fetchKeyset().finally(() => {
|
|
1396
|
+
this.nextRefreshAt = Date.now() + KEYSET_RETRY_INTERVAL_MS;
|
|
1397
|
+
this.inflight = null;
|
|
1398
|
+
});
|
|
1399
|
+
return this.inflight;
|
|
1400
|
+
}
|
|
1401
|
+
async fetchKeyset() {
|
|
1402
|
+
try {
|
|
1403
|
+
const res = await this.fetchImpl(this.jwksUrl);
|
|
1404
|
+
if (!res.ok) throw authUnavailable();
|
|
1405
|
+
const body = await res.json();
|
|
1406
|
+
if (!body || !Array.isArray(body.keys)) throw authUnavailable();
|
|
1407
|
+
const next = /* @__PURE__ */ new Map();
|
|
1408
|
+
for (const jwk of body.keys) {
|
|
1409
|
+
if (!jwk || jwk.kty !== "EC" || jwk.crv !== "P-256" || typeof jwk.kid !== "string" || !jwk.kid) continue;
|
|
1410
|
+
try {
|
|
1411
|
+
next.set(jwk.kid, await crypto.subtle.importKey("jwk", {
|
|
1412
|
+
kty: "EC",
|
|
1413
|
+
crv: jwk.crv,
|
|
1414
|
+
x: jwk.x,
|
|
1415
|
+
y: jwk.y,
|
|
1416
|
+
ext: true
|
|
1417
|
+
}, {
|
|
1418
|
+
name: "ECDSA",
|
|
1419
|
+
namedCurve: "P-256"
|
|
1420
|
+
}, true, [
|
|
1421
|
+
"verify"
|
|
1422
|
+
]));
|
|
1423
|
+
} catch {
|
|
1413
1424
|
}
|
|
1414
|
-
} finally {
|
|
1415
|
-
this.inflight = null;
|
|
1416
1425
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1426
|
+
if (next.size === 0) throw authUnavailable();
|
|
1427
|
+
this.keys = next;
|
|
1428
|
+
this.fetchedAt = Date.now();
|
|
1429
|
+
this.refreshFailed = false;
|
|
1430
|
+
} catch {
|
|
1431
|
+
this.refreshFailed = true;
|
|
1432
|
+
throw authUnavailable();
|
|
1433
|
+
}
|
|
1419
1434
|
}
|
|
1420
1435
|
async key(kid) {
|
|
1421
1436
|
const stale = Date.now() - this.fetchedAt > this.ttl;
|
|
1422
|
-
if (
|
|
1437
|
+
if (this.keys.has(kid) && !stale) return this.keys.get(kid);
|
|
1438
|
+
if (this.inflight || Date.now() >= this.nextRefreshAt) await this.refresh();
|
|
1439
|
+
else if (this.refreshFailed || stale) throw authUnavailable();
|
|
1423
1440
|
return this.keys.get(kid) ?? null;
|
|
1424
1441
|
}
|
|
1425
1442
|
/**
|
|
1426
1443
|
* Verify an `Authorization` header value.
|
|
1427
1444
|
*
|
|
1428
1445
|
* @returns the verified claims, or `null` for absent / malformed / expired /
|
|
1429
|
-
* wrong-issuer / bad-signature.
|
|
1430
|
-
*
|
|
1446
|
+
* wrong-issuer / bad-signature. Credential refusals deliberately share one
|
|
1447
|
+
* result. An unavailable keyset throws 503 instead: no authentication
|
|
1448
|
+
* decision was possible, and the caller's session must not be invalidated.
|
|
1431
1449
|
*/
|
|
1432
1450
|
async verify(authorization) {
|
|
1433
1451
|
if (!authorization || !authorization.startsWith("Bearer ")) return null;
|
|
@@ -1445,7 +1463,10 @@ var AuthVerifier = class {
|
|
|
1445
1463
|
} catch {
|
|
1446
1464
|
return null;
|
|
1447
1465
|
}
|
|
1448
|
-
if (header.alg !== "ES256" || !header.kid) return null;
|
|
1466
|
+
if (!header || typeof header !== "object" || header.alg !== "ES256" || typeof header.kid !== "string" || !header.kid) return null;
|
|
1467
|
+
if (!claims || typeof claims !== "object" || Array.isArray(claims)) return null;
|
|
1468
|
+
if (typeof claims.exp === "number" && claims.exp * 1e3 <= Date.now()) return null;
|
|
1469
|
+
if (this.issuer && claims.iss !== this.issuer) return null;
|
|
1449
1470
|
const key2 = await this.key(header.kid);
|
|
1450
1471
|
if (!key2) return null;
|
|
1451
1472
|
let ok = false;
|
|
@@ -1459,7 +1480,6 @@ var AuthVerifier = class {
|
|
|
1459
1480
|
}
|
|
1460
1481
|
if (!ok) return null;
|
|
1461
1482
|
if (typeof claims.exp === "number" && claims.exp * 1e3 <= Date.now()) return null;
|
|
1462
|
-
if (this.issuer && claims.iss !== this.issuer) return null;
|
|
1463
1483
|
return claims;
|
|
1464
1484
|
}
|
|
1465
1485
|
};
|
|
@@ -6807,7 +6827,12 @@ async function createApp(opts) {
|
|
|
6807
6827
|
}
|
|
6808
6828
|
const target = matchRoute(routes, body.method ?? "POST", body.path);
|
|
6809
6829
|
const spec = effectiveAuth(target?.entry.meta.options?.auth, target?.entry.controllerAuth);
|
|
6810
|
-
|
|
6830
|
+
let callerClaims;
|
|
6831
|
+
try {
|
|
6832
|
+
callerClaims = await auth.verify(req.headers.get("authorization"));
|
|
6833
|
+
} catch (err) {
|
|
6834
|
+
return errorResponse(err, "upload authentication", requestId);
|
|
6835
|
+
}
|
|
6811
6836
|
if (spec.required && !callerClaims) {
|
|
6812
6837
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
6813
6838
|
}
|
|
@@ -6857,7 +6882,12 @@ async function createApp(opts) {
|
|
|
6857
6882
|
let attestedDevice = null;
|
|
6858
6883
|
let attestChallengeOut = null;
|
|
6859
6884
|
const spec = effectiveAuth(meta.options?.auth, hit.entry.controllerAuth);
|
|
6860
|
-
|
|
6885
|
+
let claims;
|
|
6886
|
+
try {
|
|
6887
|
+
claims = await auth.verify(req.headers.get("authorization"));
|
|
6888
|
+
} catch (err) {
|
|
6889
|
+
return errorResponse(err, "authentication", requestId);
|
|
6890
|
+
}
|
|
6861
6891
|
if (spec.required && !claims) {
|
|
6862
6892
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
6863
6893
|
}
|