22 * Copyright (c) 2022-2026 Digital Bazaar, Inc.
33 */
44import {
5- createAuthorizationDetailsFromOffer , createCredentialRequestsFromOffer
5+ createAuthorizationDetailsFromOffer , createConfiguredRequestsFromOffer
66} from './oid4vci/credentialOffer.js' ;
77import { generateDIDProofDIVP , generateDIDProofJWT } from './oid4vci/proofs.js' ;
88import { createNamedError } from './util.js' ;
@@ -82,24 +82,16 @@ export class OID4Client {
8282 async requestCredential ( {
8383 credentialDefinition, did, didProofSigner, nonce, agent, format = 'ldp_vc'
8484 } = { } ) {
85- const { authorizationDetails, issuerConfig, offer, oid4vciVersion} = this ;
8685 let requests ;
87- if ( credentialDefinition === undefined ) {
88- if ( ! offer ) {
89- throw new TypeError ( '"offer" must be an object.' ) ;
90- }
91- requests = createCredentialRequestsFromOffer ( {
92- issuerConfig, offer, format, authorizationDetails, oid4vciVersion
93- } ) ;
94- } else {
95- // OID4VCI Draft 13 only
86+ if ( credentialDefinition !== undefined ) {
87+ // OID4VCI Draft 13 only...
9688 requests = [ {
9789 format,
9890 credential_definition : credentialDefinition
9991 } ] ;
10092 }
10193 return this . requestCredentials ( {
102- requests, did, didProofSigner, nonce, agent
94+ requests, did, didProofSigner, nonce, agent, format
10395 } ) ;
10496 }
10597
@@ -116,24 +108,51 @@ export class OID4Client {
116108 } ) ;
117109 }
118110
111+ // generate configured requests...
112+ let configuredRequests ;
113+
114+ // if `requests` given (deprecated style), validate them
115+ // note: `offer` is preferred instead of `requests`
119116 const { authorizationDetails, issuerConfig, offer, oid4vciVersion} = this ;
120- if ( requests === undefined && offer ) {
121- requests = createCredentialRequestsFromOffer ( {
117+ if ( requests ) {
118+ // OID4VCI Draft 13 only...
119+ if ( ! ( Array . isArray ( requests ) && requests . length > 0 ) ) {
120+ throw new TypeError ( '"requests" must be an array of length >= 1.' ) ;
121+ }
122+ requests . forEach ( _assertRequest ) ;
123+ // map requests to `configuredRequests`; use `undefined` credential
124+ // configuration unless only a single configuration is mentioned; a
125+ // future revision might try to match `credential_definition`, but this
126+ // is probably not worth the effort since this is for draft 13 only
127+ let configuration ;
128+ const supportedConfigIds = Object . keys (
129+ issuerConfig . credential_configurations_supported ?? { } ) ;
130+ if ( supportedConfigIds . length > 0 ) {
131+ configuration = issuerConfig
132+ . credential_configurations_supported [ supportedConfigIds [ 0 ] ] ;
133+ }
134+ configuredRequests = requests . map (
135+ request => ( { configuration, request} ) ) ;
136+ } else if ( offer ) {
137+ configuredRequests = createConfiguredRequestsFromOffer ( {
122138 issuerConfig, offer, format, authorizationDetails, oid4vciVersion
123139 } ) ;
124- } else if ( ! ( Array . isArray ( requests ) && requests . length > 0 ) ) {
125- throw new TypeError ( '"requests " must be an array of length >= 1 .' ) ;
140+ } else {
141+ throw new TypeError ( '"offer " must be an object .' ) ;
126142 }
127- requests . forEach ( _assertRequest ) ;
128143
129144 // determine if OID4VCI 1.0+ is to be used
130- const version1Plus = requests . some ( r => r . credential_identifier ) ;
131-
145+ const version1Plus = configuredRequests . some (
146+ ( { request } ) => request . credential_identifier ) ;
132147 if ( ! version1Plus ) {
133148 // set default `format` for requests with `credential_definition`
134149 // (OID4VCI Draft 13 only)
135- requests = requests . map (
136- r => r . credential_definition ? { format, ...r } : r ) ;
150+ configuredRequests = configuredRequests . map (
151+ ( { configuration, request} ) => ( {
152+ configuration,
153+ request : request . credential_definition ?
154+ { format, ...request } : request
155+ } ) ) ;
137156 }
138157
139158 try {
@@ -144,13 +163,15 @@ export class OID4Client {
144163 // adding a DID proof, if requested and N-many nonces might be required
145164 // FIXME: use p-queue to manage work
146165 const { credential_endpoint : url } = issuerConfig ;
147- const results = await Promise . all ( requests . map ( async request => {
148- const json = { ...request } ;
149- return _requestCredential ( {
150- accessToken, issuerConfig,
151- url, json, nonce, did, didProofSigner, agent
152- } ) ;
153- } ) ) ;
166+ const results = await Promise . all ( configuredRequests . map (
167+ async ( { configuration, request} ) => {
168+ const json = { ...request } ;
169+ return _requestCredential ( {
170+ accessToken, issuerConfig,
171+ credentialConfiguration : configuration ,
172+ url, json, nonce, did, didProofSigner, agent
173+ } ) ;
174+ } ) ) ;
154175 // for backwards compatibility, return all results independently,
155176 // combined, and singular (if applicable)
156177 const credentials = results
@@ -168,6 +189,9 @@ export class OID4Client {
168189 // draft 13...
169190 let url ;
170191 let json ;
192+ requests = configuredRequests . map ( ( { request} ) => request ) ;
193+ // same configuration has to be used for all requested credentials
194+ const configuration = configuredRequests [ 0 ] ?. configuration ;
171195 if ( requests . length > 1 || alwaysUseBatchEndpoint ) {
172196 ( { batch_credential_endpoint : url } = issuerConfig ) ;
173197 json = { credential_requests : requests } ;
@@ -177,6 +201,7 @@ export class OID4Client {
177201 }
178202 result = await _requestCredential ( {
179203 accessToken, issuerConfig,
204+ credentialConfiguration : configuration ,
180205 url, json, nonce, did, didProofSigner, agent
181206 } ) ;
182207 }
@@ -232,26 +257,27 @@ export class OID4Client {
232257}
233258
234259async function _addDIDProof ( {
235- issuerConfig, json, nonce, did, didProofSigner
260+ issuerConfig, credentialConfiguration , json, nonce, did, didProofSigner
236261} ) {
237- // FIXME: allow these to combine; and choose just one based on
238- // `proof_types_supported`, defaulting to `jwt` if nothing is specified
239- await _addDIDProofDIVP ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
240- // add DID proof `jwt` to json
241- await _addDIDProofJWT ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
262+ if ( credentialConfiguration ?. proof_types_supported ?. di_vp ) {
263+ return _addDIDProofDIVP ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
264+ }
265+
266+ // default to JWT
267+ return _addDIDProofJWT ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
242268}
243269
244270async function _addDIDProofDIVP ( {
245271 issuerConfig, json, nonce, did, didProofSigner
246272} ) {
247273 // generate a DID proof DI VP
248274 const { issuer : domain } = issuerConfig ;
249- const di_vp = [ await generateDIDProofDIVP ( {
275+ const di_vp = await generateDIDProofDIVP ( {
250276 did,
251277 signer : didProofSigner ,
252278 domain,
253279 challenge : nonce
254- } ) ] ;
280+ } ) ;
255281
256282 // add proof to body to be posted and loop to retry
257283 const proof = { proof_type : 'di_vp' , di_vp} ;
@@ -304,7 +330,8 @@ async function _addDIDProofJWT({
304330}
305331
306332async function _requestCredential ( {
307- accessToken, issuerConfig, url, json, nonce, did, didProofSigner, agent
333+ accessToken, issuerConfig, credentialConfiguration,
334+ url, json, nonce, did, didProofSigner, agent
308335} ) {
309336 /* First send credential request(s) to DS without DID proof JWT (unless
310337 `nonce` is given) e.g.:
@@ -373,7 +400,9 @@ async function _requestCredential({
373400 */
374401 if ( nonce !== undefined ) {
375402 // add DID proof to json
376- await _addDIDProof ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
403+ await _addDIDProof ( {
404+ issuerConfig, credentialConfiguration, json, nonce, did, didProofSigner
405+ } ) ;
377406 }
378407
379408 let result ;
@@ -426,7 +455,9 @@ async function _requestCredential({
426455 }
427456
428457 // add DID proof to json
429- await _addDIDProof ( { issuerConfig, json, nonce, did, didProofSigner} ) ;
458+ await _addDIDProof ( {
459+ issuerConfig, credentialConfiguration, json, nonce, did, didProofSigner
460+ } ) ;
430461 }
431462 }
432463
0 commit comments