@absolutejs/auth 0.54.6 → 0.54.8
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/agents/index.d.ts +1 -0
- package/dist/agents/index.js +209 -3
- package/dist/agents/index.js.map +7 -4
- package/dist/agents/oidcAdapter.d.ts +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +124 -9
- package/dist/index.js.map +9 -8
- package/dist/manifest.js +21 -2
- package/dist/manifest.js.map +3 -3
- package/dist/oidc/clientIdMetadata.d.ts +32 -0
- package/dist/oidc/config.d.ts +1 -0
- package/dist/oidc/dpop.d.ts +2 -1
- package/package.json +2 -2
|
@@ -2,8 +2,11 @@ import type { AgentCredentialVerifier } from './types';
|
|
|
2
2
|
/** Adapter for Absolute Auth's RFC 9068-style JWT access tokens. It validates
|
|
3
3
|
* signature, issuer, audience, expiry, and extracts the OAuth client as the
|
|
4
4
|
* agent identity while retaining `sub` as the authorizing user. */
|
|
5
|
-
export declare const createOidcAgentCredentialVerifier: ({ issuer, publicJwk, resource }: {
|
|
5
|
+
export declare const createOidcAgentCredentialVerifier: ({ issuer, isUsedDpopJti, maxDpopAgeMs, publicJwk, requireDpop, resource }: {
|
|
6
6
|
issuer: string;
|
|
7
|
+
isUsedDpopJti?: (jti: string) => boolean | Promise<boolean>;
|
|
8
|
+
maxDpopAgeMs?: number;
|
|
7
9
|
publicJwk: JsonWebKey;
|
|
10
|
+
requireDpop?: boolean;
|
|
8
11
|
resource: string;
|
|
9
12
|
}) => AgentCredentialVerifier;
|
package/dist/index.d.ts
CHANGED
|
@@ -363,6 +363,7 @@ export { agentDelegationsTable, agentRegistrationsTable, createNeonAgentDelegati
|
|
|
363
363
|
export { createInMemoryAccessTokenStore, createInMemoryApiClientStore, createInMemoryApiKeyStore } from './apikeys/inMemoryStores';
|
|
364
364
|
export { accessTokensTable, apiClientsTable, apiKeysTable, createNeonAccessTokenStore, createNeonApiClientStore, createNeonApiKeyStore, createPostgresAccessTokenStore, createPostgresApiClientStore, createPostgresApiKeyStore } from './apikeys/postgresStores';
|
|
365
365
|
export * from './oidc/config';
|
|
366
|
+
export * from './oidc/clientIdMetadata';
|
|
366
367
|
export * from './oidc/types';
|
|
367
368
|
export { oidcProviderRoutes } from './oidc/routes';
|
|
368
369
|
export { generateSigningKey, jwkThumbprint, signJwt, toPublicJwk, verifyJwt } from './oidc/keys';
|
package/dist/index.js
CHANGED
|
@@ -5488,7 +5488,7 @@ var issueBackchannelAuth = async ({
|
|
|
5488
5488
|
if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
|
|
5489
5489
|
return { error: "invalid_request", ok: false };
|
|
5490
5490
|
}
|
|
5491
|
-
const client = await config.clientStore.findClient(clientId);
|
|
5491
|
+
const client = await config.clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
|
|
5492
5492
|
if (!client)
|
|
5493
5493
|
return { error: "invalid_client", ok: false };
|
|
5494
5494
|
const resolved = await config.resolveBackchannelUser({
|
|
@@ -6169,6 +6169,7 @@ var normalizeHtu = (value) => {
|
|
|
6169
6169
|
}
|
|
6170
6170
|
};
|
|
6171
6171
|
var verifyDpopProof = async ({
|
|
6172
|
+
accessToken,
|
|
6172
6173
|
htm,
|
|
6173
6174
|
htu,
|
|
6174
6175
|
isUsedJti,
|
|
@@ -6189,6 +6190,11 @@ var verifyDpopProof = async ({
|
|
|
6189
6190
|
if (verified === undefined)
|
|
6190
6191
|
return;
|
|
6191
6192
|
const { payload } = verified;
|
|
6193
|
+
if (accessToken !== undefined) {
|
|
6194
|
+
const expectedAth = Buffer.from(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(accessToken))).toString("base64url");
|
|
6195
|
+
if (payload.ath !== expectedAth)
|
|
6196
|
+
return;
|
|
6197
|
+
}
|
|
6192
6198
|
const iatMs = typeof payload.iat === "number" ? payload.iat * SECONDS_TO_MS : 0;
|
|
6193
6199
|
if (payload.htm !== htm || normalizeHtu(payload.htu) !== normalizeHtu(htu) || iatMs === 0 || Math.abs(now - iatMs) > maxAgeMs) {
|
|
6194
6200
|
return;
|
|
@@ -6791,8 +6797,9 @@ var oidcProviderRoutes = (config) => {
|
|
|
6791
6797
|
const userinfoRoute = `${oidcRoute}/userinfo`;
|
|
6792
6798
|
const registrationBaseUrl = `${issuer}${registrationRoute}`;
|
|
6793
6799
|
const tokenUrl = `${issuer}${oidcRoute}/token`;
|
|
6800
|
+
const resolveClient = async (clientId) => await clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
|
|
6794
6801
|
const authenticateClient = async (clientId, clientSecret) => {
|
|
6795
|
-
const client = await
|
|
6802
|
+
const client = await resolveClient(clientId);
|
|
6796
6803
|
if (client === undefined)
|
|
6797
6804
|
return;
|
|
6798
6805
|
if (client.hashedSecret === undefined)
|
|
@@ -6835,14 +6842,14 @@ var oidcProviderRoutes = (config) => {
|
|
|
6835
6842
|
assertion: bodyClientAssertion,
|
|
6836
6843
|
expectedAudience: tokenUrl,
|
|
6837
6844
|
jtiStore: config.clientAssertionJtiStore,
|
|
6838
|
-
resolveClient
|
|
6845
|
+
resolveClient
|
|
6839
6846
|
});
|
|
6840
6847
|
return client2 === undefined ? undefined : { client: client2, clientCertThumbprint: undefined };
|
|
6841
6848
|
}
|
|
6842
6849
|
const clientId = bodyClientId ?? basicClientId;
|
|
6843
6850
|
if (clientId === undefined)
|
|
6844
6851
|
return;
|
|
6845
|
-
const candidate = await
|
|
6852
|
+
const candidate = await resolveClient(clientId);
|
|
6846
6853
|
const mtlsResult = await tryMtlsAuth({
|
|
6847
6854
|
candidate,
|
|
6848
6855
|
extract: config.extractTlsClientCert,
|
|
@@ -7058,6 +7065,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
7058
7065
|
backchannel_logout_session_supported: false,
|
|
7059
7066
|
backchannel_logout_supported: true,
|
|
7060
7067
|
code_challenge_methods_supported: ["S256"],
|
|
7068
|
+
client_id_metadata_document_supported: config.resolveClientIdMetadata !== undefined,
|
|
7061
7069
|
dpop_signing_alg_values_supported: ["ES256"],
|
|
7062
7070
|
end_session_endpoint: `${issuer}${endSessionRoute}`,
|
|
7063
7071
|
grant_types_supported: grantTypes,
|
|
@@ -7115,7 +7123,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
7115
7123
|
idTokenHint: query.id_token_hint
|
|
7116
7124
|
});
|
|
7117
7125
|
const clientId = hint?.audClientId ?? query.client_id;
|
|
7118
|
-
const client = clientId === undefined ? undefined : await
|
|
7126
|
+
const client = clientId === undefined ? undefined : await resolveClient(clientId);
|
|
7119
7127
|
const userSession = await loadSessionFromSource({
|
|
7120
7128
|
authSessionStore,
|
|
7121
7129
|
session: inMemorySession,
|
|
@@ -7167,7 +7175,7 @@ var oidcProviderRoutes = (config) => {
|
|
|
7167
7175
|
return jsonResponse({ error: "invalid_request_uri" }, HTTP_BAD_REQUEST2);
|
|
7168
7176
|
}
|
|
7169
7177
|
const initialClientId = effectiveQuery.client_id;
|
|
7170
|
-
const initialClient = initialClientId === undefined ? undefined : await
|
|
7178
|
+
const initialClient = initialClientId === undefined ? undefined : await resolveClient(initialClientId);
|
|
7171
7179
|
if (effectiveQuery.request !== undefined && initialClient !== undefined) {
|
|
7172
7180
|
const parsed = await parseSignedRequestObject({
|
|
7173
7181
|
client: initialClient,
|
|
@@ -24971,15 +24979,18 @@ var readAudience = (audience) => {
|
|
|
24971
24979
|
};
|
|
24972
24980
|
var createOidcAgentCredentialVerifier = ({
|
|
24973
24981
|
issuer,
|
|
24982
|
+
isUsedDpopJti,
|
|
24983
|
+
maxDpopAgeMs,
|
|
24974
24984
|
publicJwk,
|
|
24985
|
+
requireDpop = false,
|
|
24975
24986
|
resource
|
|
24976
24987
|
}) => {
|
|
24977
24988
|
const verifier = async (request) => {
|
|
24978
24989
|
const authorization = request.headers.get("authorization");
|
|
24979
|
-
if (authorization === null || !authorization.startsWith(BEARER_PREFIX6)) {
|
|
24990
|
+
if (authorization === null || !authorization.startsWith(BEARER_PREFIX6) && !authorization.startsWith("DPoP ")) {
|
|
24980
24991
|
return;
|
|
24981
24992
|
}
|
|
24982
|
-
const token = authorization.slice(
|
|
24993
|
+
const token = authorization.slice(authorization.indexOf(" ") + 1).trim();
|
|
24983
24994
|
if (token.length === 0)
|
|
24984
24995
|
return;
|
|
24985
24996
|
const verified = await verifyJwt(token, publicJwk);
|
|
@@ -24987,6 +24998,22 @@ var createOidcAgentCredentialVerifier = ({
|
|
|
24987
24998
|
if (payload === undefined || payload.iss !== issuer || typeof payload.exp !== "number" || payload.exp <= Math.floor(Date.now() / MS_PER_SECOND7) || !readAudience(payload.aud).includes(resource) || typeof payload.client_id !== "string") {
|
|
24988
24999
|
return;
|
|
24989
25000
|
}
|
|
25001
|
+
const confirmation = payload.cnf;
|
|
25002
|
+
const boundJkt = typeof confirmation === "object" && confirmation !== null && typeof confirmation.jkt === "string" ? confirmation.jkt : undefined;
|
|
25003
|
+
if (boundJkt !== undefined || requireDpop) {
|
|
25004
|
+
if (!authorization.startsWith("DPoP "))
|
|
25005
|
+
return;
|
|
25006
|
+
const proof = await verifyDpopProof({
|
|
25007
|
+
accessToken: token,
|
|
25008
|
+
htm: request.method,
|
|
25009
|
+
htu: request.url,
|
|
25010
|
+
isUsedJti: isUsedDpopJti,
|
|
25011
|
+
...maxDpopAgeMs === undefined ? {} : { maxAgeMs: maxDpopAgeMs },
|
|
25012
|
+
proof: request.headers.get("dpop") ?? undefined
|
|
25013
|
+
});
|
|
25014
|
+
if (proof === undefined || boundJkt === undefined || proof.jkt !== boundJkt)
|
|
25015
|
+
return;
|
|
25016
|
+
}
|
|
24990
25017
|
return {
|
|
24991
25018
|
agentId: payload.client_id,
|
|
24992
25019
|
claims: payload,
|
|
@@ -25374,6 +25401,91 @@ var createPostgresApiKeyStore = (db) => ({
|
|
|
25374
25401
|
await db.update(apiKeysTable).set({ last_used_at_ms: lastUsedAt }).where(eq(apiKeysTable.key_id, keyId));
|
|
25375
25402
|
}
|
|
25376
25403
|
});
|
|
25404
|
+
// src/oidc/clientIdMetadata.ts
|
|
25405
|
+
var secureUrl = (value) => {
|
|
25406
|
+
try {
|
|
25407
|
+
return new URL(value).protocol === "https:";
|
|
25408
|
+
} catch {
|
|
25409
|
+
return false;
|
|
25410
|
+
}
|
|
25411
|
+
};
|
|
25412
|
+
var validateClientIdMetadataDocument = (document, expectedClientId) => {
|
|
25413
|
+
const errors = [];
|
|
25414
|
+
if (document.client_id !== expectedClientId)
|
|
25415
|
+
errors.push("client_id does not match the metadata document URL");
|
|
25416
|
+
if (!secureUrl(document.client_id))
|
|
25417
|
+
errors.push("client_id must use HTTPS");
|
|
25418
|
+
if (!Array.isArray(document.redirect_uris) || document.redirect_uris.length === 0)
|
|
25419
|
+
errors.push("redirect_uris is required");
|
|
25420
|
+
for (const uri of document.redirect_uris ?? []) {
|
|
25421
|
+
try {
|
|
25422
|
+
const parsed = new URL(uri);
|
|
25423
|
+
if (parsed.protocol !== "https:" && parsed.hostname !== "localhost")
|
|
25424
|
+
errors.push("redirect URIs must use HTTPS or localhost");
|
|
25425
|
+
} catch {
|
|
25426
|
+
errors.push("redirect URI is invalid");
|
|
25427
|
+
}
|
|
25428
|
+
}
|
|
25429
|
+
for (const [name2, value] of [
|
|
25430
|
+
["client_uri", document.client_uri],
|
|
25431
|
+
["logo_uri", document.logo_uri],
|
|
25432
|
+
["policy_uri", document.policy_uri],
|
|
25433
|
+
["tos_uri", document.tos_uri],
|
|
25434
|
+
["jwks_uri", document.jwks_uri]
|
|
25435
|
+
]) {
|
|
25436
|
+
if (value !== undefined && !secureUrl(value))
|
|
25437
|
+
errors.push(`${name2} must use HTTPS`);
|
|
25438
|
+
}
|
|
25439
|
+
return errors;
|
|
25440
|
+
};
|
|
25441
|
+
var clientIdMetadataToOAuthClient = (document) => ({
|
|
25442
|
+
clientId: document.client_id,
|
|
25443
|
+
grantTypes: document.grant_types ?? ["authorization_code", "refresh_token"],
|
|
25444
|
+
...document.jwks === undefined ? {} : { jwks: document.jwks.keys },
|
|
25445
|
+
...document.jwks_uri === undefined ? {} : { jwksUri: document.jwks_uri },
|
|
25446
|
+
name: document.client_name ?? new URL(document.client_id).hostname,
|
|
25447
|
+
redirectUris: [...document.redirect_uris],
|
|
25448
|
+
scopes: document.scope?.split(" ").filter(Boolean) ?? []
|
|
25449
|
+
});
|
|
25450
|
+
var createClientIdMetadataResolver = ({
|
|
25451
|
+
fetch: fetcher,
|
|
25452
|
+
allow = async () => true,
|
|
25453
|
+
cacheTtlMs = 5 * 60 * 1000,
|
|
25454
|
+
maxBytes = 5 * 1024,
|
|
25455
|
+
now = Date.now
|
|
25456
|
+
}) => {
|
|
25457
|
+
const cache = new Map;
|
|
25458
|
+
return async (clientId) => {
|
|
25459
|
+
if (!secureUrl(clientId) || !await allow(clientId))
|
|
25460
|
+
return;
|
|
25461
|
+
const cached = cache.get(clientId);
|
|
25462
|
+
if (cached !== undefined && cached.expiresAt > now())
|
|
25463
|
+
return cached.client;
|
|
25464
|
+
const response = await fetcher(clientId, {
|
|
25465
|
+
headers: { accept: "application/json" },
|
|
25466
|
+
redirect: "error"
|
|
25467
|
+
});
|
|
25468
|
+
if (!response.ok)
|
|
25469
|
+
return;
|
|
25470
|
+
const declaredLength = Number(response.headers.get("content-length") ?? "0");
|
|
25471
|
+
if (declaredLength > maxBytes)
|
|
25472
|
+
return;
|
|
25473
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
25474
|
+
if (bytes.byteLength > maxBytes)
|
|
25475
|
+
return;
|
|
25476
|
+
let document;
|
|
25477
|
+
try {
|
|
25478
|
+
document = JSON.parse(new TextDecoder().decode(bytes));
|
|
25479
|
+
} catch {
|
|
25480
|
+
return;
|
|
25481
|
+
}
|
|
25482
|
+
if (validateClientIdMetadataDocument(document, clientId).length > 0)
|
|
25483
|
+
return;
|
|
25484
|
+
const client = clientIdMetadataToOAuthClient(document);
|
|
25485
|
+
cache.set(clientId, { client, expiresAt: now() + cacheTtlMs });
|
|
25486
|
+
return client;
|
|
25487
|
+
};
|
|
25488
|
+
};
|
|
25377
25489
|
// src/oidc/inMemoryStores.ts
|
|
25378
25490
|
var DEFAULT_LIST_LIMIT = 100;
|
|
25379
25491
|
var createInMemoryAuthorizationCodeStore = () => {
|
|
@@ -27995,6 +28107,7 @@ export {
|
|
|
27995
28107
|
vaultEntriesTable,
|
|
27996
28108
|
validateSession,
|
|
27997
28109
|
validateEmailDeliverability,
|
|
28110
|
+
validateClientIdMetadataDocument,
|
|
27998
28111
|
userSessionIdTypebox,
|
|
27999
28112
|
userInfoChallengeHeader,
|
|
28000
28113
|
updateRegisteredClient,
|
|
@@ -28326,6 +28439,7 @@ export {
|
|
|
28326
28439
|
createFederatedTokenStore,
|
|
28327
28440
|
createCustomOAuth2Client,
|
|
28328
28441
|
createCredentialOffer,
|
|
28442
|
+
createClientIdMetadataResolver,
|
|
28329
28443
|
createAuthHtmxRoutes,
|
|
28330
28444
|
createAuditRedactor,
|
|
28331
28445
|
createAuditEmitter,
|
|
@@ -28339,6 +28453,7 @@ export {
|
|
|
28339
28453
|
constantTimeEqual,
|
|
28340
28454
|
computeCertThumbprint,
|
|
28341
28455
|
complianceRoutes,
|
|
28456
|
+
clientIdMetadataToOAuthClient,
|
|
28342
28457
|
check2 as check,
|
|
28343
28458
|
buildStatusClaim,
|
|
28344
28459
|
buildIssuerMetadata,
|
|
@@ -28414,5 +28529,5 @@ export {
|
|
|
28414
28529
|
AuthIdentityConflictError
|
|
28415
28530
|
};
|
|
28416
28531
|
|
|
28417
|
-
//# debugId=
|
|
28532
|
+
//# debugId=D8A79A996B677FF164756E2164756E21
|
|
28418
28533
|
//# sourceMappingURL=index.js.map
|