Skip to content
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@credo-ts/anoncreds": "0.6.1",
"@credo-ts/askar": "0.6.1",
"@credo-ts/core": "0.6.1",
"@credo-ts/didcomm": "0.6.1",
"@credo-ts/indy-vdr": "0.6.1",
"@credo-ts/node": "0.6.1",
"@credo-ts/openid4vc": "0.6.1",
Expand Down
136 changes: 89 additions & 47 deletions src/cliAgent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Note: For now we need to import askar-nodejs at the top to handle the undefined askar issue
// Refer from: https://github.com/credebl/mobile-sdk/blob/main/packages/ssi/src/wallet/wallet.ts
import '@openwallet-foundation/askar-nodejs'
import type { AskarModuleConfigStoreOptions } from '@credo-ts/askar'
import type { InitConfig } from '@credo-ts/core'
import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr'

Expand All @@ -14,7 +15,7 @@ import {
DidCommCredentialV1Protocol,
DidCommProofV1Protocol,
} from '@credo-ts/anoncreds'
import { AskarModule, AskarModuleConfigStoreOptions, AskarMultiWalletDatabaseScheme } from '@credo-ts/askar'
import { AskarModule, AskarMultiWalletDatabaseScheme } from '@credo-ts/askar'
import {
DidsModule,
W3cCredentialsModule,
Expand All @@ -26,6 +27,8 @@ import {
LogLevel,
Agent,
X509Module,
JwkDidRegistrar,
JwkDidResolver,
} from '@credo-ts/core'
import {
DidCommHttpOutboundTransport,
Expand All @@ -46,29 +49,22 @@ import {
IndyVdrIndyDidRegistrar,
} from '@credo-ts/indy-vdr'
import { agentDependencies, DidCommHttpInboundTransport, DidCommWsInboundTransport } from '@credo-ts/node'
import { OpenId4VcHolderModule, OpenId4VcModule } from '@credo-ts/openid4vc'
import { QuestionAnswerModule } from '@credo-ts/question-answer'
import { TenantsModule } from '@credo-ts/tenants'
import { anoncreds } from '@hyperledger/anoncreds-nodejs'
import { askar } from '@openwallet-foundation/askar-nodejs'
import { indyVdr } from '@hyperledger/indy-vdr-nodejs'
import { askar } from '@openwallet-foundation/askar-nodejs'
import axios from 'axios'
import bodyParser from 'body-parser'
import express from 'express'
import { readFile } from 'fs/promises'

import { IndicioAcceptanceMechanism, IndicioTransactionAuthorAgreement, Network, NetworkName } from './enums'
import { setupServer } from './server'
import { generateSecretKey } from './utils/helpers'
import { TsLogger } from './utils/logger'
import { OpenId4VcHolderModule, OpenId4VcIssuerModule, OpenId4VcVerifierModule } from '@credo-ts/openid4vc'
import {
getCredentialRequestToCredentialMapper,
getTrustedCerts,
} from './utils/oid4vc-agent'
import bodyParser from 'body-parser'

import express from 'express'

const openId4VpApp = express()
const openId4VcApp = express()
import { getMixedCredentialRequestToCredentialMapper, getTrustedCerts } from './utils/oid4vc-agent'

export type Transports = 'ws' | 'http'
export type InboundTransport = {
Expand Down Expand Up @@ -128,7 +124,15 @@ export async function readRestConfig(path: string) {
export type RestMultiTenantAgentModules = Awaited<ReturnType<typeof getWithTenantModules>>

export type RestAgentModules = Awaited<ReturnType<typeof getModules>>

function requireEnv(name: string): string {
const value = process.env[name]
if (!value) {
throw new Error(`Missing environment variable: ${name}`)
}
return value
}
const expressApp = express()
expressApp.disable('x-powered-by')
// TODO: add object
const getModules = (
networkConfig: [IndyVdrPoolConfig, ...IndyVdrPoolConfig[]],
Expand All @@ -141,34 +145,41 @@ const getModules = (
autoAcceptCredentials: DidCommAutoAcceptCredential,
autoAcceptProofs: DidCommAutoAcceptProof,
walletScheme: AskarMultiWalletDatabaseScheme,
storeOptions: AskarModuleConfigStoreOptions
storeOptions: AskarModuleConfigStoreOptions,
) => {
const legacyIndyCredentialFormat = new LegacyIndyDidCommCredentialFormatService()
const legacyIndyProofFormat = new LegacyIndyDidCommProofFormatService()
const jsonLdCredentialFormatService = new DidCommJsonLdCredentialFormatService()
const anonCredsCredentialFormatService = new AnonCredsDidCommCredentialFormatService()
const anonCredsProofFormatService = new AnonCredsDidCommProofFormatService()
const presentationExchangeProofFormatService = new DidCommDifPresentationExchangeProofFormatService()

return {
askar: new AskarModule({
askar,
store: {
...storeOptions
...storeOptions,
},
multiWalletDatabaseScheme: walletScheme || AskarMultiWalletDatabaseScheme.ProfilePerWallet,

}),

indyVdr: new IndyVdrModule({
indyVdr,
networks: networkConfig,
}),

dids: new DidsModule({
registrars: [new IndyVdrIndyDidRegistrar(), new KeyDidRegistrar()
registrars: [
new IndyVdrIndyDidRegistrar(),
new KeyDidRegistrar(),
new JwkDidRegistrar(),
// , new PolygonDidRegistrar()
],
resolvers: [new IndyVdrIndyDidResolver(), new KeyDidResolver(), new WebDidResolver()
resolvers: [
new IndyVdrIndyDidResolver(),
new KeyDidResolver(),
new WebDidResolver(),
new JwkDidResolver(),
new KeyDidResolver(),
// , new PolygonDidResolver()
],
Comment on lines +177 to 184
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate KeyDidResolver in resolvers array.

KeyDidResolver is instantiated twice (lines 179 and 182). Remove the duplicate.

Proposed fix
     resolvers: [
       new IndyVdrIndyDidResolver(),
       new KeyDidResolver(),
       new WebDidResolver(),
       new JwkDidResolver(),
-      new KeyDidResolver(),
       // , new PolygonDidResolver()
     ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resolvers: [
new IndyVdrIndyDidResolver(),
new KeyDidResolver(),
new WebDidResolver(),
new JwkDidResolver(),
new KeyDidResolver(),
// , new PolygonDidResolver()
],
resolvers: [
new IndyVdrIndyDidResolver(),
new KeyDidResolver(),
new WebDidResolver(),
new JwkDidResolver(),
// , new PolygonDidResolver()
],
🤖 Prompt for AI Agents
In @src/cliAgent.ts around lines 177 - 184, The resolvers array contains a
duplicate instantiation of KeyDidResolver; remove the redundant entry so the
array only includes one new KeyDidResolver() (leave IndyVdrIndyDidResolver,
WebDidResolver, JwkDidResolver and any commented entries as-is) to avoid
registering the same DID resolver twice.

}),
Expand Down Expand Up @@ -210,7 +221,11 @@ const getModules = (
indyCredentialFormat: legacyIndyCredentialFormat,
}),
new DidCommCredentialV2Protocol({
credentialFormats: [legacyIndyCredentialFormat, jsonLdCredentialFormatService, anonCredsCredentialFormatService],
credentialFormats: [
legacyIndyCredentialFormat,
jsonLdCredentialFormatService,
anonCredsCredentialFormatService,
],
}),
],
},
Expand All @@ -220,6 +235,8 @@ const getModules = (
}),

questionAnswer: new QuestionAnswerModule(),
// openid4vc: new OpenId4VcModule({}),
// Todo: We can remove this polygon module for time being
// polygon: new PolygonModule({
// didContractAddress: didRegistryContractAddress
// ? didRegistryContractAddress
Expand All @@ -230,27 +247,51 @@ const getModules = (
// rpcUrl: rpcUrl ? rpcUrl : (process.env.RPC_URL as string),
// serverUrl: fileServerUrl ? fileServerUrl : (process.env.SERVER_URL as string),
// }),
openId4VcVerifier: new OpenId4VcVerifierModule({
baseUrl:
process.env.NODE_ENV === 'PROD'
? `https://${process.env.APP_URL}/oid4vp`
: `${process.env.AGENT_HTTP_URL}/oid4vp`,
app: openId4VpApp,
authorizationRequestExpirationInSeconds: Number(process.env.OID4VP_AUTH_REQUEST_PROOF_REQUEST_EXPIRY) || 3600,
}),
openId4VcIssuer: new OpenId4VcIssuerModule({
baseUrl:
process.env.NODE_ENV === 'PROD'
? `https://${process.env.APP_URL}/oid4vci`
: `${process.env.AGENT_HTTP_URL}/oid4vci`,
app: openId4VcApp,
statefulCredentialOfferExpirationInSeconds: Number(process.env.OID4VCI_CRED_OFFER_EXPIRY) || 3600,
accessTokenExpiresInSeconds: Number(process.env.OID4VCI_ACCESS_TOKEN_EXPIRY) || 3600,
authorizationCodeExpiresInSeconds: Number(process.env.OID4VCI_AUTH_CODE_EXPIRY) || 3600,
cNonceExpiresInSeconds: Number(process.env.OID4VCI_CNONCE_EXPIRY) || 3600,
dpopRequired: false,
credentialRequestToCredentialMapper: (...args) => getCredentialRequestToCredentialMapper()(...args),
openid4vc: new OpenId4VcModule({
app: expressApp,
issuer: {
baseUrl:
process.env.NODE_ENV === 'PROD'
? `https://${requireEnv('APP_URL')}/oid4vci`
: `${requireEnv('AGENT_HTTP_URL')}/oid4vci`,
app: expressApp,
statefulCredentialOfferExpirationInSeconds: Number(process.env.OID4VCI_CRED_OFFER_EXPIRY) || 3600,
accessTokenExpiresInSeconds: Number(process.env.OID4VCI_ACCESS_TOKEN_EXPIRY) || 3600,
authorizationCodeExpiresInSeconds: Number(process.env.OID4VCI_AUTH_CODE_EXPIRY) || 3600,
cNonceExpiresInSeconds: Number(process.env.OID4VCI_CNONCE_EXPIRY) || 3600,
dpopRequired: false,
credentialRequestToCredentialMapper: (...args) => getMixedCredentialRequestToCredentialMapper()(...args),
},
verifier: {
baseUrl:
process.env.NODE_ENV === 'PROD'
? `https://${requireEnv('APP_URL')}/oid4vp`
: `${requireEnv('AGENT_HTTP_URL')}/oid4vp`,
// app: openId4VpApp,
authorizationRequestExpirationInSeconds: Number(process.env.OID4VP_AUTH_REQUEST_PROOF_REQUEST_EXPIRY) || 3600,
},
}),
// openId4VcVerifier: new OpenId4VcVerifierModule({
// baseUrl:
// process.env.NODE_ENV === 'PROD'
// ? `https://${process.env.APP_URL}/oid4vp`
// : `${process.env.AGENT_HTTP_URL}/oid4vp`,
// app: openId4VpApp,
// authorizationRequestExpirationInSeconds: Number(process.env.OID4VP_AUTH_REQUEST_PROOF_REQUEST_EXPIRY) || 3600,
// }),
// openId4VcIssuer: new OpenId4VcIssuerModule({
// baseUrl:
// process.env.NODE_ENV === 'PROD'
// ? `https://${process.env.APP_URL}/oid4vci`
// : `${process.env.AGENT_HTTP_URL}/oid4vci`,
// app: openId4VcApp,
// statefulCredentialOfferExpirationInSeconds: Number(process.env.OID4VCI_CRED_OFFER_EXPIRY) || 3600,
// accessTokenExpiresInSeconds: Number(process.env.OID4VCI_ACCESS_TOKEN_EXPIRY) || 3600,
// authorizationCodeExpiresInSeconds: Number(process.env.OID4VCI_AUTH_CODE_EXPIRY) || 3600,
// cNonceExpiresInSeconds: Number(process.env.OID4VCI_CNONCE_EXPIRY) || 3600,
// dpopRequired: false,
// credentialRequestToCredentialMapper: (...args) => getCredentialRequestToCredentialMapper()(...args),
// }),
openId4VcHolderModule: new OpenId4VcHolderModule(),
x509: new X509Module({
getTrustedCertificatesForVerification: async (_agentContext, { certificateChain, verification }) => {
Expand All @@ -275,7 +316,7 @@ const getWithTenantModules = (
autoAcceptCredentials: DidCommAutoAcceptCredential,
autoAcceptProofs: DidCommAutoAcceptProof,
walletScheme: AskarMultiWalletDatabaseScheme,
walletConfig: AskarModuleConfigStoreOptions
walletConfig: AskarModuleConfigStoreOptions,
) => {
const modules = getModules(
networkConfig,
Expand All @@ -288,7 +329,7 @@ const getWithTenantModules = (
autoAcceptCredentials,
autoAcceptProofs,
walletScheme,
walletConfig
walletConfig,
)
return {
tenants: new TenantsModule<typeof modules>({
Expand Down Expand Up @@ -418,7 +459,7 @@ export async function runRestAgent(restConfig: AriesRestConfig) {
autoAcceptCredentials || DidCommAutoAcceptCredential.Always,
autoAcceptProofs || DidCommAutoAcceptProof.ContentApproved,
walletScheme || AskarMultiWalletDatabaseScheme.ProfilePerWallet,
walletConfig
walletConfig,
)
const modules = getModules(
networkConfig,
Expand All @@ -431,7 +472,7 @@ export async function runRestAgent(restConfig: AriesRestConfig) {
autoAcceptCredentials || DidCommAutoAcceptCredential.Always,
autoAcceptProofs || DidCommAutoAcceptProof.ContentApproved,
walletScheme || AskarMultiWalletDatabaseScheme.ProfilePerWallet,
walletConfig
walletConfig,
)
const agent = new Agent({
config: agentConfig,
Expand Down Expand Up @@ -468,8 +509,8 @@ export async function runRestAgent(restConfig: AriesRestConfig) {
)
transport.app.use(bodyParser.json({ limit: process.env.APP_JSON_BODY_SIZE ?? '5mb' }))

transport.app.use('/oid4vci', modules.openId4VcIssuer.config.app)
transport.app.use('/oid4vp', modules.openId4VcVerifier.config.app)
// transport.app.use('/oid4vci', modules.openid4vc.issuer?.config.app.routes ?? express.Router())
// transport.app.use('/oid4vp', modules.openid4vc.verifier?.config.app.routes ?? express.Router())
Comment on lines +512 to +513
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read src/cliAgent.ts around lines 512-513
if [ -f "src/cliAgent.ts" ]; then
  echo "=== Lines 505-520 of src/cliAgent.ts ==="
  sed -n '505,520p' src/cliAgent.ts
else
  echo "File not found at src/cliAgent.ts, searching for it..."
  find . -name "cliAgent.ts" -type f 2>/dev/null | head -5
fi

Repository: credebl/credo-controller

Length of output: 733


🏁 Script executed:

# Search for openid4vc route mounting patterns in the codebase
echo "=== Searching for oid4vci and oid4vp route usage ==="
rg -n "oid4vci|oid4vp" --type ts -B 2 -A 2 | head -100

Repository: credebl/credo-controller

Length of output: 2298


🏁 Script executed:

# Check if there's a pattern of mounting routes from modules
echo "=== Searching for module.*.config.app.routes pattern ==="
rg -n "modules\.\w+\..*config.*app.*routes" --type ts | head -50

Repository: credebl/credo-controller

Length of output: 373


🏁 Script executed:

# Check the OpenId4VcModule initialization more thoroughly
echo "=== Context around openid4vc module setup ==="
sed -n '240,290p' src/cliAgent.ts

Repository: credebl/credo-controller

Length of output: 2817


🏁 Script executed:

# Search for where OpenId4VcModule routes might be mounted or if there's a different pattern
echo "=== Search for all openid4vc module usage ==="
rg -n "openid4vc|OpenId4VcModule" --type ts -B 1 -A 1 | head -80

Repository: credebl/credo-controller

Length of output: 5821


🏁 Script executed:

# Check if there's initialization code that mounts these routes
echo "=== Search for transport.app.use patterns ==="
rg -n "transport\.app\.use" --type ts | head -30

Repository: credebl/credo-controller

Length of output: 520


🏁 Script executed:

# Check if there's any documentation or comments about route registration
echo "=== Search for route comments or TODOs ==="
rg -n "route|Route|TODO|FIXME" src/cliAgent.ts | grep -i "oid4v\|route.*oid\|TODO.*route" | head -20

Repository: credebl/credo-controller

Length of output: 397


🏁 Script executed:

# Check if there's a main server/app file that might set up these routes
echo "=== Search for main app/server setup ==="
rg -n "express\(\)" --type ts | head -20

Repository: credebl/credo-controller

Length of output: 312


🏁 Script executed:

# Look at what happens after agent.initialize()
echo "=== Context after agent initialization ==="
sed -n '515,530p' src/cliAgent.ts

Repository: credebl/credo-controller

Length of output: 651


🏁 Script executed:

# Check if there's an indication of whether routes are auto-mounted or need manual mounting
echo "=== Search for .routes property usage ==="
rg -n "\.routes|\.router" --type ts | grep -E "openid4vc|oid4v" | head -20

Repository: credebl/credo-controller

Length of output: 358


🏁 Script executed:

# Look for any web server setup that might handle these routes differently
echo "=== Check if there's a separate app/server file ==="
find . -type f -name "*.ts" | grep -E "app|server|main" | grep -v node_modules | head -20

Repository: credebl/credo-controller

Length of output: 135


🏁 Script executed:

# Read src/server.ts to see how the app is configured
echo "=== Full src/server.ts ===" 
wc -l src/server.ts

Repository: credebl/credo-controller

Length of output: 112


🏁 Script executed:

# Read server.ts with line numbers
cat -n src/server.ts

Repository: credebl/credo-controller

Length of output: 6442


🏁 Script executed:

# Check if there's any documentation or comments about the commented-out lines
echo "=== Full context around lines 500-520 in cliAgent.ts ==="
sed -n '495,525p' src/cliAgent.ts

Repository: credebl/credo-controller

Length of output: 1477


Uncomment or complete the route registration for OpenID4VC endpoints.

The commented-out lines 512-513 prevent the issuer and verifier routes from being mounted on the transport. Additionally, the verifier configuration (line 270) has its app property commented out, meaning verifier routes won't be accessible via any mechanism. Either uncomment these routes or ensure the OpenId4VcModule auto-mounts them when configured with an app property. The presence of baseUrl configuration for both endpoints suggests they should be functional.

}
}

Expand Down Expand Up @@ -505,6 +546,7 @@ export async function runRestAgent(restConfig: AriesRestConfig) {
webhookUrl,
port: adminPort,
schemaFileServerURL,
app: expressApp,
},
apiKey,
)
Expand Down
Loading