From 53cd247c6f3c9c07310e8b9dcd14c8655d9168fc Mon Sep 17 00:00:00 2001 From: Ajay Jadhav Date: Tue, 20 Oct 2026 18:45:25 +0530 Subject: [PATCH 1/2] feat(x509): fallback to agent trusted certificates when trust list lookup fails When client authentication is not attestation-based, getTrustedCertificatesForVerification currently only fetches certificates from the static TRUST_LIST_URL. In local/dev and e2e environments that URL is often unreachable or misconfigured, which causes verification/issuance flows to fail even if certificates were explicitly trusted. Add a try/catch around getX509CertsByUrl(): on failure, resolve X509ModuleConfig from the agent context and return any certificates already added via POST /x509/trusted (addTrustedCertificate). If none exist, rethrow the original error so remote trust list failures are still surfaced in non-test environments. Signed-off-by: Ajay Jadhav --- src/cliAgent.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cliAgent.ts b/src/cliAgent.ts index 02e74c2c..93cec759 100644 --- a/src/cliAgent.ts +++ b/src/cliAgent.ts @@ -29,6 +29,7 @@ import { LogLevel, Agent, X509Module, + X509ModuleConfig, JwkDidRegistrar, JwkDidResolver, SdJwtVcModule, @@ -309,8 +310,19 @@ const getModules = ( return await getX509CertsByClientToken(tenantId, certificateChain) } - // NoAuth: return all certs from the static trust list URL - return await getX509CertsByUrl() + // NoAuth: return all certs from the static trust list URL. + // Locally the trust list service is often unreachable (placeholder URL), + // so fall back to the certificates explicitly marked as trusted on this + // agent (e.g. added through POST /x509/trusted). + try { + return await getX509CertsByUrl() + } catch (error) { + const agentTrustedCertificates = agentContext.dependencyManager.resolve(X509ModuleConfig).trustedCertificates + if (agentTrustedCertificates && agentTrustedCertificates.length > 0) { + return agentTrustedCertificates + } + throw error + } }, }), } From 76e89e8094977ec2abb5441bea320ad6d0754215 Mon Sep 17 00:00:00 2001 From: Ajay Jadhav Date: Tue, 20 Oct 2026 19:37:11 +0530 Subject: [PATCH 2/2] fix(x509): add 5s timeout and AbortSignal support to trust list fetch; preserve existing fallback behavior Signed-off-by: Ajay Jadhav --- src/utils/oid4vc-agent.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/utils/oid4vc-agent.ts b/src/utils/oid4vc-agent.ts index 73b9951d..49737731 100644 --- a/src/utils/oid4vc-agent.ts +++ b/src/utils/oid4vc-agent.ts @@ -262,27 +262,31 @@ export async function getX509CertsByClientToken( return certificateChain.map((cert) => cert.toString('pem')) } -export async function getX509CertsByUrl(): Promise { +export async function getX509CertsByUrl(signal?: AbortSignal): Promise { const trustListUrl = process.env.TRUST_LIST_URL if (!trustListUrl) throw new Error('[getX509CertsByUrl] TRUST_LIST_URL is not configured') - logger.info(`[getX509CertsByUrl] fetching trust list from: ${trustListUrl}`) + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), 5000) + try { + logger.info(`[getX509CertsByUrl] fetching trust list from: ${trustListUrl}`) + const response = await fetch(trustListUrl, { signal: signal ?? controller.signal }) - const response = await fetch(trustListUrl) + if (!response.ok) { + throw new Error(`[getX509CertsByUrl] failed to fetch trust list: HTTP ${response.status}`) + } - if (!response.ok) { - throw new Error(`[getX509CertsByUrl] failed to fetch trust list: HTTP ${response.status}`) - } + const data = await response.json() - const data = await response.json() + if (!Array.isArray(data) || data.length === 0) { + throw new Error('[getX509CertsByUrl] trust list is empty or invalid') + } - if (!Array.isArray(data) || data.length === 0) { - throw new Error('[getX509CertsByUrl] trust list is empty or invalid') + logger.info(`[getX509CertsByUrl] fetched certificates count: ${data.length}`) + return data as string[] + } finally { + clearTimeout(timeout) } - - logger.info(`[getX509CertsByUrl] fetched certificates count: ${data.length}`) - - return data as string[] } function handlePresentationAuthorization(