diff --git a/packages/core/src/container-impl.ts b/packages/core/src/container-impl.ts index 307de4e43b..7231671515 100644 --- a/packages/core/src/container-impl.ts +++ b/packages/core/src/container-impl.ts @@ -92,6 +92,7 @@ export const defaultConfig: Config = { }, showGenericErrors: false, enableFullScreenErrorModal: false, + trustedCertificatesUrl: ReactConfig.TRUSTED_CERTIFICATES_URL, } export const defaultHistoryEventsLogger: HistoryEventsLoggerConfig = { diff --git a/packages/core/src/hooks/useBifoldAgentSetup.ts b/packages/core/src/hooks/useBifoldAgentSetup.ts index 03ac09e4af..ea1da10a2b 100644 --- a/packages/core/src/hooks/useBifoldAgentSetup.ts +++ b/packages/core/src/hooks/useBifoldAgentSetup.ts @@ -9,7 +9,7 @@ import { TOKENS, useServices } from '../container-api' import { DispatchAction } from '../contexts/reducers/store' import { useStore } from '../contexts/store' import { WalletSecret } from '../types/security' -import { createLinkSecretIfRequired, getAgentModules } from '../utils/agent' +import { createLinkSecretIfRequired, getAgentModulesWithCertificates } from '../utils/agent' import { migrateToAskar } from '../utils/migration' export type AgentSetupReturnType = { @@ -22,13 +22,13 @@ const useBifoldAgentSetup = (): AgentSetupReturnType => { const [agent, setAgent] = useState(null) const agentInstanceRef = useRef(null) const [store, dispatch] = useStore() - const [cacheSchemas, cacheCredDefs, logger, indyLedgers, bridge] = useServices([ + const [cacheSchemas, cacheCredDefs, logger, indyLedgers, bridge, config] = useServices([ TOKENS.CACHE_SCHEMAS, TOKENS.CACHE_CRED_DEFS, TOKENS.UTIL_LOGGER, TOKENS.UTIL_LEDGERS, TOKENS.UTIL_AGENT_BRIDGE, - TOKENS.UTIL_REFRESH_ORCHESTRATOR, + TOKENS.CONFIG, ]) const restartExistingAgent = useCallback( @@ -64,15 +64,18 @@ const useBifoldAgentSetup = (): AgentSetupReturnType => { autoUpdateStorageOnStartup: true, }, dependencies: agentDependencies, - modules: getAgentModules({ - indyNetworks: indyLedgers, - mediatorInvitationUrl: mediatorUrl, - txnCache: { - capacity: 1000, - expiryOffsetMs: 1000 * 60 * 60 * 24 * 7, - path: CachesDirectoryPath + '/txn-cache', + modules: await getAgentModulesWithCertificates( + { + indyNetworks: indyLedgers, + mediatorInvitationUrl: mediatorUrl, + txnCache: { + capacity: 1000, + expiryOffsetMs: 1000 * 60 * 60 * 24 * 7, + path: CachesDirectoryPath + '/txn-cache', + }, }, - }), + config.trustedCertificatesUrl + ), }) const wsTransport = new WsOutboundTransport() const httpTransport = new HttpOutboundTransport() @@ -82,7 +85,7 @@ const useBifoldAgentSetup = (): AgentSetupReturnType => { return newAgent }, - [store.preferences.walletName, logger, indyLedgers] + [store.preferences.walletName, logger, indyLedgers, config.trustedCertificatesUrl] ) const migrateIfRequired = useCallback( diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c113ff0ef5..442c1cf327 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -103,7 +103,7 @@ export * from './types/attestation' export { BifoldError } from './types/error' export { Screens, Stacks, TabStacks } from './types/navigators' export * from './types/version-check' -export { createLinkSecretIfRequired, getAgentModules } from './utils/agent' +export { createLinkSecretIfRequired, getAgentModules, getAgentModulesWithCertificates } from './utils/agent' export { getCredentialIdentifiers, isValidAnonCredsCredential } from './utils/credential' export { connectFromScanOrDeepLink, diff --git a/packages/core/src/types/config.ts b/packages/core/src/types/config.ts index 2c9f2f479d..63f376d861 100644 --- a/packages/core/src/types/config.ts +++ b/packages/core/src/types/config.ts @@ -51,6 +51,7 @@ export interface Config { PINScreensConfig: PINScreensConfig showGenericErrors?: boolean enableFullScreenErrorModal?: boolean + trustedCertificatesUrl?: string } export interface PINScreensConfig { diff --git a/packages/core/src/utils/agent.ts b/packages/core/src/utils/agent.ts index 9c5b155616..4d1b96e202 100644 --- a/packages/core/src/utils/agent.ts +++ b/packages/core/src/utils/agent.ts @@ -26,6 +26,7 @@ import { V2CredentialProtocol, V2ProofProtocol, WebDidResolver, + X509Module, } from '@credo-ts/core' import { IndyVdrAnonCredsRegistry, IndyVdrModule, IndyVdrPoolConfig } from '@credo-ts/indy-vdr' import { OpenId4VcHolderModule } from '@credo-ts/openid4vc' @@ -40,18 +41,46 @@ interface GetAgentModulesOptions { indyNetworks: IndyVdrPoolConfig[] mediatorInvitationUrl?: string txnCache?: { capacity: number; expiryOffsetMs: number; path?: string } + trustedCertificates?: string[] } export type BifoldAgent = Agent> +/** + * Fetches trusted certificates from a remote API + * @param url The API endpoint URL + * @returns Array of certificate strings + */ +async function fetchTrustedCertificates(url: string): Promise { + try { + const response = await fetch(url) + if (!response.ok) { + return [] + } + const certificates = await response.json() + if (!Array.isArray(certificates)) { + return [] + } + return certificates.filter((cert) => typeof cert === 'string' && cert.trim().length > 0) + } catch (error) { + return [] + } +} + /** * Constructs the modules to be used in the agent setup * @param indyNetworks * @param mediatorInvitationUrl determine which mediator to use * @param txnCache optional local cache config for indyvdr + * @param trustedCertificates optional array of trusted certificates for X509 module * @returns modules to be used in agent setup */ -export function getAgentModules({ indyNetworks, mediatorInvitationUrl, txnCache }: GetAgentModulesOptions) { +export function getAgentModules({ + indyNetworks, + mediatorInvitationUrl, + txnCache, + trustedCertificates = [], +}: GetAgentModulesOptions) { const indyCredentialFormat = new LegacyIndyCredentialFormatService() const indyProofFormat = new LegacyIndyProofFormatService() @@ -120,9 +149,34 @@ export function getAgentModules({ indyNetworks, mediatorInvitationUrl, txnCache new PeerDidResolver(), ], }), + ...(trustedCertificates.length > 0 + ? { + x509: new X509Module({ + trustedCertificates: trustedCertificates as [string, ...string[]], + }), + } + : {}), } } +/** + * Fetches and prepares agent modules with trusted certificates from remote API + * @param options Agent module options including indyNetworks, mediatorInvitationUrl, txnCache + * @param trustedCertificatesUrl Optional URL to fetch trusted certificates from + * @returns Promise resolving to agent modules + */ +export async function getAgentModulesWithCertificates( + options: Omit, + trustedCertificatesUrl?: string +) { + const trustedCertificates = trustedCertificatesUrl ? await fetchTrustedCertificates(trustedCertificatesUrl) : [] + + return getAgentModules({ + ...options, + trustedCertificates, + }) +} + interface MyAgentContextInterface { loading: boolean agent: BifoldAgent