@absolutejs/auth 0.75.0 → 0.75.1
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/README.md +13 -0
- package/dist/agents/index.js +46 -11
- package/dist/agents/index.js.map +4 -4
- package/dist/agents/oidcAdapter.d.ts +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +63 -24
- package/dist/index.js.map +7 -7
- package/dist/nativePush.d.ts +14 -14
- package/dist/oidc/dpop.d.ts +6 -2
- package/dist/oidc/index.d.ts +1 -0
- package/dist/oidc/index.js +130 -1
- package/dist/oidc/index.js.map +4 -3
- package/dist/server.js +63 -24
- package/dist/server.js.map +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -281,8 +281,11 @@ const authPlugin = await auth({
|
|
|
281
281
|
resource: 'https://api.example.com',
|
|
282
282
|
scopes: ['documents:read', 'documents:write'],
|
|
283
283
|
verifyCredential: createOidcAgentCredentialVerifier({
|
|
284
|
+
// Atomically insert a hash of jkt + jti; return false on conflict.
|
|
285
|
+
consumeDpopJti: replayStore.consume,
|
|
284
286
|
issuer: 'https://auth.example.com',
|
|
285
287
|
publicJwk: signingKey.publicJwk,
|
|
288
|
+
requireDpop: true,
|
|
286
289
|
resource: 'https://api.example.com'
|
|
287
290
|
})
|
|
288
291
|
},
|
|
@@ -300,6 +303,16 @@ registration. Approval through the existing RFC 8628 device flow creates the
|
|
|
300
303
|
user-to-agent delegation. The agent can then use RFC 8693 token exchange to get
|
|
301
304
|
a narrowed, audience-bound access token for the protected API.
|
|
302
305
|
|
|
306
|
+
When `requireDpop` is enabled, the adapter accepts only an RFC 9449-bound
|
|
307
|
+
access token using the `DPoP` authorization scheme, verifies its per-request
|
|
308
|
+
proof and `ath` token hash, and requires the proof key to match `cnf.jkt`.
|
|
309
|
+
Provide `consumeDpopJti` as an atomic shared-store insertion in clustered
|
|
310
|
+
deployments; returning `false` rejects a replay. Proofs without `jti`, proofs
|
|
311
|
+
whose JWK contains private key material, oversized identifiers, and `htu`
|
|
312
|
+
claims containing query or fragment components fail closed. Resource servers
|
|
313
|
+
that require RFC 9449 nonces can use the nonce helpers exported by
|
|
314
|
+
`@absolutejs/auth/oidc` to issue a separate resource nonce challenge.
|
|
315
|
+
|
|
303
316
|
```ts
|
|
304
317
|
app.get('/documents', ({ protectAgent }) =>
|
|
305
318
|
protectAgent(['documents:read'], (agent) => ({
|
package/dist/agents/index.js
CHANGED
|
@@ -1349,6 +1349,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
|
|
|
1349
1349
|
// src/oidc/dpop.ts
|
|
1350
1350
|
init_constants();
|
|
1351
1351
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
1352
|
+
var MAX_JTI_LENGTH = 128;
|
|
1352
1353
|
var SECONDS_TO_MS = 1000;
|
|
1353
1354
|
var NONCE_WINDOW_SECONDS = 120;
|
|
1354
1355
|
var NONCE_WINDOW_MS = NONCE_WINDOW_SECONDS * MILLISECONDS_IN_A_SECOND;
|
|
@@ -1389,17 +1390,38 @@ var verifyDpopNonce = async ({
|
|
|
1389
1390
|
const candidates = await Promise.all(Array.from({ length: NONCE_PREVIOUS_WINDOWS_ACCEPTED + 1 }, (_, offset) => hmacSha256(secret, String(currentWindow - offset))));
|
|
1390
1391
|
return candidates.some((expected) => expected === nonce);
|
|
1391
1392
|
};
|
|
1392
|
-
var decodeHeader = (segment) =>
|
|
1393
|
-
|
|
1393
|
+
var decodeHeader = (segment) => {
|
|
1394
|
+
try {
|
|
1395
|
+
const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
|
|
1396
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
1397
|
+
} catch {
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
var normalizeHtu = (value, proofClaim = false) => {
|
|
1394
1402
|
try {
|
|
1395
1403
|
const url = new URL(String(value));
|
|
1404
|
+
if (url.username || url.password || proofClaim && (url.search.length > 0 || url.hash.length > 0))
|
|
1405
|
+
return;
|
|
1396
1406
|
return `${url.origin}${url.pathname}`;
|
|
1397
1407
|
} catch {
|
|
1398
|
-
return
|
|
1408
|
+
return;
|
|
1399
1409
|
}
|
|
1400
1410
|
};
|
|
1411
|
+
var isPublicEs256Jwk = (value) => {
|
|
1412
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
1413
|
+
return false;
|
|
1414
|
+
const kty = Reflect.get(value, "kty");
|
|
1415
|
+
const crv = Reflect.get(value, "crv");
|
|
1416
|
+
const xCoordinate = Reflect.get(value, "x");
|
|
1417
|
+
const yCoordinate = Reflect.get(value, "y");
|
|
1418
|
+
const privateComponent = Reflect.get(value, "d");
|
|
1419
|
+
const alg = Reflect.get(value, "alg");
|
|
1420
|
+
return kty === "EC" && crv === "P-256" && typeof xCoordinate === "string" && xCoordinate.length > 0 && typeof yCoordinate === "string" && yCoordinate.length > 0 && privateComponent === undefined && (alg === undefined || alg === "ES256");
|
|
1421
|
+
};
|
|
1401
1422
|
var verifyDpopProof = async ({
|
|
1402
1423
|
accessToken,
|
|
1424
|
+
consumeJti,
|
|
1403
1425
|
htm,
|
|
1404
1426
|
htu,
|
|
1405
1427
|
isUsedJti,
|
|
@@ -1409,14 +1431,20 @@ var verifyDpopProof = async ({
|
|
|
1409
1431
|
}) => {
|
|
1410
1432
|
if (proof === undefined)
|
|
1411
1433
|
return;
|
|
1412
|
-
const
|
|
1434
|
+
const segments = proof.split(".");
|
|
1435
|
+
if (segments.length !== 3)
|
|
1436
|
+
return;
|
|
1437
|
+
const [headerSegment] = segments;
|
|
1413
1438
|
if (headerSegment === undefined)
|
|
1414
1439
|
return;
|
|
1415
1440
|
const header = decodeHeader(headerSegment);
|
|
1416
|
-
|
|
1441
|
+
const publicJwk = header === undefined ? undefined : Reflect.get(header, "jwk");
|
|
1442
|
+
if (header === undefined || Reflect.get(header, "typ") !== "dpop+jwt" || Reflect.get(header, "alg") !== "ES256" || !isPublicEs256Jwk(publicJwk)) {
|
|
1417
1443
|
return;
|
|
1418
1444
|
}
|
|
1419
|
-
const verified = await verifyJwt(proof,
|
|
1445
|
+
const verified = await verifyJwt(proof, publicJwk).catch(() => {
|
|
1446
|
+
return;
|
|
1447
|
+
});
|
|
1420
1448
|
if (verified === undefined)
|
|
1421
1449
|
return;
|
|
1422
1450
|
const { payload } = verified;
|
|
@@ -1426,14 +1454,19 @@ var verifyDpopProof = async ({
|
|
|
1426
1454
|
return;
|
|
1427
1455
|
}
|
|
1428
1456
|
const iatMs = typeof payload.iat === "number" ? payload.iat * SECONDS_TO_MS : 0;
|
|
1429
|
-
|
|
1457
|
+
const claimedHtu = normalizeHtu(payload.htu, true);
|
|
1458
|
+
const expectedHtu = normalizeHtu(htu);
|
|
1459
|
+
const jti = typeof payload.jti === "string" ? payload.jti : undefined;
|
|
1460
|
+
if (payload.htm !== htm || claimedHtu === undefined || expectedHtu === undefined || claimedHtu !== expectedHtu || iatMs === 0 || Math.abs(now - iatMs) > maxAgeMs || jti === undefined || jti.length === 0 || jti.length > MAX_JTI_LENGTH) {
|
|
1430
1461
|
return;
|
|
1431
1462
|
}
|
|
1432
|
-
const
|
|
1433
|
-
if (
|
|
1463
|
+
const jkt = await jwkThumbprint(publicJwk);
|
|
1464
|
+
if (isUsedJti !== undefined && await isUsedJti(jti)) {
|
|
1434
1465
|
return;
|
|
1435
1466
|
}
|
|
1436
|
-
|
|
1467
|
+
if (consumeJti !== undefined && !await consumeJti({ expiresAt: iatMs + maxAgeMs, jkt, jti }))
|
|
1468
|
+
return;
|
|
1469
|
+
return { jkt, jti };
|
|
1437
1470
|
};
|
|
1438
1471
|
|
|
1439
1472
|
// src/agents/oidcAdapter.ts
|
|
@@ -1449,6 +1482,7 @@ var readAudience = (audience) => {
|
|
|
1449
1482
|
};
|
|
1450
1483
|
var createOidcAgentCredentialVerifier = ({
|
|
1451
1484
|
issuer,
|
|
1485
|
+
consumeDpopJti,
|
|
1452
1486
|
isUsedDpopJti,
|
|
1453
1487
|
maxDpopAgeMs,
|
|
1454
1488
|
publicJwk,
|
|
@@ -1477,6 +1511,7 @@ var createOidcAgentCredentialVerifier = ({
|
|
|
1477
1511
|
return;
|
|
1478
1512
|
const proof = await verifyDpopProof({
|
|
1479
1513
|
accessToken: token,
|
|
1514
|
+
consumeJti: consumeDpopJti,
|
|
1480
1515
|
htm: request.method,
|
|
1481
1516
|
htu: request.url,
|
|
1482
1517
|
isUsedJti: isUsedDpopJti,
|
|
@@ -13763,5 +13798,5 @@ export {
|
|
|
13763
13798
|
AGENT_CLAIM_GRANT_TYPE
|
|
13764
13799
|
};
|
|
13765
13800
|
|
|
13766
|
-
//# debugId=
|
|
13801
|
+
//# debugId=801AFAC38E904B9A64756E2164756E21
|
|
13767
13802
|
//# sourceMappingURL=index.js.map
|