@credo-ts/openid4vc 0.6.0-pr-2195-20250322195244 → 0.6.0-pr-2324-20250625125220

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.
Files changed (56) hide show
  1. package/build/openid4vc-holder/OpenId4VcHolderApi.d.ts +10 -6
  2. package/build/openid4vc-holder/OpenId4VcHolderApi.js +2 -4
  3. package/build/openid4vc-holder/OpenId4VcHolderApi.js.map +1 -1
  4. package/build/openid4vc-holder/OpenId4VciHolderService.d.ts +14 -19
  5. package/build/openid4vc-holder/OpenId4VciHolderService.js +425 -203
  6. package/build/openid4vc-holder/OpenId4VciHolderService.js.map +1 -1
  7. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.d.ts +117 -37
  8. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.js +1 -0
  9. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.js.map +1 -1
  10. package/build/openid4vc-holder/OpenId4vpHolderService.js +24 -15
  11. package/build/openid4vc-holder/OpenId4vpHolderService.js.map +1 -1
  12. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.d.ts +21 -0
  13. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.js +11 -0
  14. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.js.map +1 -1
  15. package/build/openid4vc-issuer/OpenId4VcIssuerService.d.ts +7 -3
  16. package/build/openid4vc-issuer/OpenId4VcIssuerService.js +387 -167
  17. package/build/openid4vc-issuer/OpenId4VcIssuerService.js.map +1 -1
  18. package/build/openid4vc-issuer/OpenId4VcIssuerServiceOptions.d.ts +67 -27
  19. package/build/openid4vc-issuer/index.d.ts +1 -1
  20. package/build/openid4vc-issuer/index.js +2 -1
  21. package/build/openid4vc-issuer/index.js.map +1 -1
  22. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.d.ts +29 -5
  23. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.js +2 -0
  24. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.js.map +1 -1
  25. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.d.ts +12 -7
  26. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.js +15 -3
  27. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.js.map +1 -1
  28. package/build/openid4vc-issuer/router/accessTokenEndpoint.js +41 -13
  29. package/build/openid4vc-issuer/router/accessTokenEndpoint.js.map +1 -1
  30. package/build/openid4vc-issuer/router/authorizationChallengeEndpoint.js +102 -33
  31. package/build/openid4vc-issuer/router/authorizationChallengeEndpoint.js.map +1 -1
  32. package/build/openid4vc-issuer/router/credentialEndpoint.js +42 -10
  33. package/build/openid4vc-issuer/router/credentialEndpoint.js.map +1 -1
  34. package/build/openid4vc-issuer/router/jwksEndpoint.js +2 -2
  35. package/build/openid4vc-issuer/router/jwksEndpoint.js.map +1 -1
  36. package/build/openid4vc-issuer/util/txCode.d.ts +1 -1
  37. package/build/openid4vc-issuer/util/txCode.js +3 -1
  38. package/build/openid4vc-issuer/util/txCode.js.map +1 -1
  39. package/build/openid4vc-verifier/OpenId4VpVerifierService.d.ts +1 -1
  40. package/build/openid4vc-verifier/OpenId4VpVerifierService.js +70 -65
  41. package/build/openid4vc-verifier/OpenId4VpVerifierService.js.map +1 -1
  42. package/build/openid4vc-verifier/OpenId4VpVerifierServiceOptions.d.ts +7 -1
  43. package/build/shared/callbacks.d.ts +6 -4
  44. package/build/shared/callbacks.js +212 -69
  45. package/build/shared/callbacks.js.map +1 -1
  46. package/build/shared/models/CredentialHolderBinding.d.ts +65 -11
  47. package/build/shared/models/OpenId4VcJwtIssuer.d.ts +10 -5
  48. package/build/shared/models/OpenId4VciCredentialFormatProfile.d.ts +1 -0
  49. package/build/shared/models/OpenId4VciCredentialFormatProfile.js +1 -0
  50. package/build/shared/models/OpenId4VciCredentialFormatProfile.js.map +1 -1
  51. package/build/shared/router/tenants.js +2 -2
  52. package/build/shared/router/tenants.js.map +1 -1
  53. package/build/shared/utils.d.ts +4 -9
  54. package/build/shared/utils.js +27 -44
  55. package/build/shared/utils.js.map +1 -1
  56. package/package.json +14 -14
@@ -35,17 +35,17 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
35
35
  this.openId4VcIssuanceSessionRepository = openId4VcIssuanceSessionRepository;
36
36
  }
37
37
  async createStatelessCredentialOffer(agentContext, options) {
38
- const { authorizationCodeFlowConfig, issuer, offeredCredentials } = options;
38
+ const { authorizationCodeFlowConfig, issuer, credentialConfigurationIds } = options;
39
39
  const vcIssuer = this.getIssuer(agentContext);
40
40
  const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
41
- const uniqueOfferedCredentials = Array.from(new Set(options.offeredCredentials));
42
- if (uniqueOfferedCredentials.length !== offeredCredentials.length) {
41
+ const uniqueOfferedCredentials = Array.from(new Set(options.credentialConfigurationIds));
42
+ if (uniqueOfferedCredentials.length !== credentialConfigurationIds.length) {
43
43
  throw new core_1.CredoError('All offered credentials must have unique ids.');
44
44
  }
45
45
  // Check if all the offered credential configuration ids have a scope value. If not, it won't be possible to actually request
46
46
  // issuance of the credential later on
47
47
  (0, openid4vci_1.extractScopesForCredentialConfigurationIds)({
48
- credentialConfigurationIds: options.offeredCredentials,
48
+ credentialConfigurationIds: options.credentialConfigurationIds,
49
49
  issuerMetadata,
50
50
  throwOnConfigurationWithoutScope: true,
51
51
  });
@@ -53,7 +53,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
53
53
  throw new core_1.CredoError('Stateless offers can only be created for external authorization servers. Make sure to configure an external authorization server on the issuer record, and provide the authoriation server url.');
54
54
  }
55
55
  const { credentialOffer, credentialOfferObject } = await vcIssuer.createCredentialOffer({
56
- credentialConfigurationIds: options.offeredCredentials,
56
+ credentialConfigurationIds: options.credentialConfigurationIds,
57
57
  grants: {
58
58
  authorization_code: {
59
59
  authorization_server: authorizationCodeFlowConfig.authorizationServerUrl,
@@ -68,14 +68,14 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
68
68
  };
69
69
  }
70
70
  async createCredentialOffer(agentContext, options) {
71
- const { preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig, issuer, offeredCredentials, version = 'v1.draft11-13', } = options;
71
+ const { preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig, issuer, credentialConfigurationIds, version = 'v1.draft11-15', authorization, } = options;
72
72
  if (!preAuthorizedCodeFlowConfig && !authorizationCodeFlowConfig) {
73
73
  throw new core_1.CredoError('Authorization Config or Pre-Authorized Config must be provided.');
74
74
  }
75
75
  const vcIssuer = this.getIssuer(agentContext);
76
76
  const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
77
- const uniqueOfferedCredentials = Array.from(new Set(options.offeredCredentials));
78
- if (uniqueOfferedCredentials.length !== offeredCredentials.length) {
77
+ const uniqueOfferedCredentials = Array.from(new Set(options.credentialConfigurationIds));
78
+ if (uniqueOfferedCredentials.length !== credentialConfigurationIds.length) {
79
79
  throw new core_1.CredoError('All offered credentials must have unique ids.');
80
80
  }
81
81
  if (uniqueOfferedCredentials.length === 0) {
@@ -91,7 +91,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
91
91
  // issuance of the credential later on. For pre-auth it's not needed to add a scope.
92
92
  if (options.authorizationCodeFlowConfig) {
93
93
  (0, openid4vci_1.extractScopesForCredentialConfigurationIds)({
94
- credentialConfigurationIds: options.offeredCredentials,
94
+ credentialConfigurationIds: options.credentialConfigurationIds,
95
95
  issuerMetadata,
96
96
  throwOnConfigurationWithoutScope: true,
97
97
  });
@@ -102,12 +102,12 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
102
102
  authorizationCodeFlowConfig,
103
103
  });
104
104
  const { credentialOffer, credentialOfferObject } = await vcIssuer.createCredentialOffer({
105
- credentialConfigurationIds: options.offeredCredentials,
105
+ credentialConfigurationIds: options.credentialConfigurationIds,
106
106
  grants,
107
107
  credentialOfferUri: hostedCredentialOfferUri,
108
108
  credentialOfferScheme: options.baseUri,
109
109
  issuerMetadata: {
110
- originalDraftVersion: version === 'v1.draft11-13' ? openid4vci_1.Openid4vciDraftVersion.Draft11 : openid4vci_1.Openid4vciDraftVersion.Draft14,
110
+ originalDraftVersion: version === 'v1.draft11-15' ? openid4vci_1.Openid4vciDraftVersion.Draft11 : openid4vci_1.Openid4vciDraftVersion.Draft15,
111
111
  ...issuerMetadata,
112
112
  },
113
113
  });
@@ -128,6 +128,16 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
128
128
  required: true,
129
129
  }
130
130
  : undefined,
131
+ dpop: authorization?.requireDpop
132
+ ? {
133
+ required: true,
134
+ }
135
+ : undefined,
136
+ walletAttestation: authorization?.requireWalletAttestation
137
+ ? {
138
+ required: true,
139
+ }
140
+ : undefined,
131
141
  // TODO: how to mix pre-auth and auth? Need to do state checks
132
142
  preAuthorizedCode: credentialOfferObject.grants?.[oauth2_1.preAuthorizedCodeGrantIdentifier]?.['pre-authorized_code'],
133
143
  userPin: preAuthorizedCodeFlowConfig?.txCode
@@ -153,25 +163,95 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
153
163
  ]);
154
164
  const { issuanceSession } = options;
155
165
  const issuer = await this.getIssuerByIssuerId(agentContext, options.issuanceSession.issuerId);
156
- const vcIssuer = this.getIssuer(agentContext);
166
+ const vcIssuer = this.getIssuer(agentContext, { issuanceSessionId: issuanceSession.id });
157
167
  const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
158
168
  const parsedCredentialRequest = vcIssuer.parseCredentialRequest({
169
+ issuerMetadata,
159
170
  credentialRequest: options.credentialRequest,
160
171
  });
161
- const { credentialRequest, credentialIdentifier, format, proofs } = parsedCredentialRequest;
172
+ const { credentialRequest, credentialIdentifier, format, } = parsedCredentialRequest;
162
173
  if (credentialIdentifier) {
163
174
  throw new oauth2_1.Oauth2ServerErrorResponseError({
164
175
  error: oauth2_1.Oauth2ErrorCodes.InvalidCredentialRequest,
165
176
  error_description: `Using unsupported 'credential_identifier'`,
166
177
  });
167
178
  }
168
- if (!format) {
179
+ if (credentialRequest.format && !format) {
180
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
181
+ error: oauth2_1.Oauth2ErrorCodes.UnsupportedCredentialFormat,
182
+ error_description: `Unsupported credential request based on format '${credentialRequest.format}'`,
183
+ });
184
+ }
185
+ if (parsedCredentialRequest.credentialConfigurationId && !parsedCredentialRequest.credentialConfiguration) {
169
186
  throw new oauth2_1.Oauth2ServerErrorResponseError({
170
187
  error: oauth2_1.Oauth2ErrorCodes.UnsupportedCredentialFormat,
171
- error_description: `Unsupported credential format '${credentialRequest.format}'`,
188
+ error_description: `Unsupported credential request based on credential configuration id ${credentialRequest.credential_configuration_id}`,
172
189
  });
173
190
  }
174
- if (!proofs?.jwt || proofs.jwt.length === 0) {
191
+ const { credentialConfiguration, credentialConfigurationId } = this.getCredentialConfigurationsForRequest({
192
+ issuanceSession,
193
+ issuerMetadata,
194
+ requestFormat: format,
195
+ credentialConfigurations: parsedCredentialRequest.credentialConfiguration && parsedCredentialRequest.credentialConfigurationId
196
+ ? {
197
+ [parsedCredentialRequest.credentialConfigurationId]: parsedCredentialRequest.credentialConfiguration,
198
+ }
199
+ : undefined,
200
+ authorization: options.authorization,
201
+ });
202
+ const verifiedCredentialRequestProofs = await this.verifyCredentialRequestProofs(agentContext, {
203
+ issuanceSession,
204
+ issuer,
205
+ parsedCredentialRequest,
206
+ credentialConfiguration,
207
+ credentialConfigurationId,
208
+ });
209
+ const signedCredentials = await this.getSignedCredentials(agentContext, {
210
+ credentialRequest,
211
+ issuanceSession,
212
+ issuer,
213
+ credentialConfiguration,
214
+ credentialConfigurationId,
215
+ requestFormat: format,
216
+ authorization: options.authorization,
217
+ credentialRequestToCredentialMapper: options.credentialRequestToCredentialMapper,
218
+ credentialRequestProofs: verifiedCredentialRequestProofs,
219
+ });
220
+ // NOTE: nonce in credential response is deprecated in newer drafts, but for now we keep it in
221
+ const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
222
+ const credentialResponse = vcIssuer.createCredentialResponse({
223
+ credential: credentialRequest.proof ? signedCredentials.credentials[0] : undefined,
224
+ credentials: credentialRequest.proofs ? signedCredentials.credentials : undefined,
225
+ cNonce,
226
+ cNonceExpiresInSeconds,
227
+ credentialRequest: parsedCredentialRequest,
228
+ });
229
+ issuanceSession.issuedCredentials.push(credentialConfigurationId);
230
+ const newState = issuanceSession.issuedCredentials.length >=
231
+ issuanceSession.credentialOfferPayload.credential_configuration_ids.length
232
+ ? OpenId4VcIssuanceSessionState_1.OpenId4VcIssuanceSessionState.Completed
233
+ : OpenId4VcIssuanceSessionState_1.OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued;
234
+ await this.updateState(agentContext, issuanceSession, newState);
235
+ return {
236
+ credentialResponse,
237
+ issuanceSession,
238
+ };
239
+ }
240
+ async verifyCredentialRequestProofs(agentContext, options) {
241
+ const { parsedCredentialRequest, issuer, issuanceSession, credentialConfiguration, credentialConfigurationId } = options;
242
+ const { proofs } = parsedCredentialRequest;
243
+ const vcIssuer = this.getIssuer(agentContext, { issuanceSessionId: issuanceSession.id });
244
+ const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
245
+ // FIXME: verify request against the configuration
246
+ // - key attestations required
247
+ // - proof types supported
248
+ // - signing alg values supported
249
+ // - key attestation level met.
250
+ const allowedProofTypes = credentialConfiguration.proof_types_supported ?? {
251
+ jwt: { proof_signing_alg_values_supported: (0, utils_1.getSupportedJwaSignatureAlgorithms)(agentContext) },
252
+ };
253
+ const [proofType, proofValue] = Object.entries(proofs ?? {})[0] ?? [];
254
+ if (!proofType || !proofValue || proofValue.length === 0) {
175
255
  const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
176
256
  throw new oauth2_1.Oauth2ServerErrorResponseError({
177
257
  error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
@@ -180,38 +260,66 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
180
260
  c_nonce_expires_in: cNonceExpiresInSeconds,
181
261
  });
182
262
  }
263
+ if (proofType !== 'jwt' && proofType !== 'attestation') {
264
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
265
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
266
+ error_description: `Proof type '${proofType}' is not supported `,
267
+ });
268
+ }
269
+ const supportedProofType = allowedProofTypes[proofType];
270
+ if (!supportedProofType) {
271
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
272
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
273
+ error_description: `Proof type '${proofType}' is not supported for credential configuration '${credentialConfigurationId}'`,
274
+ });
275
+ }
276
+ if (proofType === 'attestation' && proofValue.length !== 1) {
277
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
278
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
279
+ error_description: "Only a single proofs entry is supported for proof type 'attestation'",
280
+ });
281
+ }
183
282
  await this.updateState(agentContext, issuanceSession, OpenId4VcIssuanceSessionState_1.OpenId4VcIssuanceSessionState.CredentialRequestReceived);
184
- let previousNonce = undefined;
185
- const proofSigners = [];
186
- for (const jwt of proofs.jwt) {
187
- const { signer, payload } = await vcIssuer.verifyCredentialRequestJwtProof({
283
+ if (proofType === 'attestation') {
284
+ const keyAttestationJwt = proofValue[0];
285
+ const keyAttestation = await vcIssuer.verifyCredentialRequestAttestationProof({
188
286
  issuerMetadata,
189
- jwt,
190
- clientId: options.issuanceSession.clientId,
287
+ keyAttestationJwt,
191
288
  });
192
- if (!payload.nonce) {
193
- const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
289
+ if (!supportedProofType.proof_signing_alg_values_supported.includes(keyAttestation.header.alg)) {
194
290
  throw new oauth2_1.Oauth2ServerErrorResponseError({
195
291
  error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
196
- error_description: 'Missing nonce in proof(s) in credential request',
197
- c_nonce: cNonce,
198
- c_nonce_expires_in: cNonceExpiresInSeconds,
292
+ error_description: `Proof signing alg value '${keyAttestation.header.alg}' is not supported for proof type 'attestation' in credentail configuration '${credentialConfigurationId}'`,
199
293
  });
200
294
  }
201
- // Set previous nonce if not yet set (first iteration)
202
- if (!previousNonce)
203
- previousNonce = payload.nonce;
204
- if (previousNonce !== payload.nonce) {
295
+ if (!keyAttestation.payload.nonce) {
205
296
  const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
206
297
  throw new oauth2_1.Oauth2ServerErrorResponseError({
207
298
  error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
208
- error_description: 'Not all nonce values in proofs are equal',
299
+ error_description: 'Missing nonce in attestation proof in credential request. If no nonce is present in the attestation, use the jwt proof type instead',
209
300
  c_nonce: cNonce,
210
301
  c_nonce_expires_in: cNonceExpiresInSeconds,
211
302
  });
212
303
  }
213
- // Verify the nonce
214
- await this.verifyNonce(agentContext, issuer, payload.nonce).catch(async (error) => {
304
+ if (supportedProofType.key_attestations_required && keyAttestation) {
305
+ const expectedKeyStorage = supportedProofType.key_attestations_required.key_storage;
306
+ const expectedUserAuthentication = supportedProofType.key_attestations_required.user_authentication;
307
+ if (expectedKeyStorage &&
308
+ !expectedKeyStorage.some((keyStorage) => keyAttestation.payload.key_storage?.includes(keyStorage))) {
309
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
310
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
311
+ error_description: `Insufficent key_storage for key attestation. Proof type 'attestation' for credential configuration '${credentialConfigurationId}', expects one of key_storage values ${expectedKeyStorage.join(', ')}`,
312
+ });
313
+ }
314
+ if (expectedUserAuthentication &&
315
+ !expectedUserAuthentication.some((userAuthentication) => keyAttestation.payload.user_authentication?.includes(userAuthentication))) {
316
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
317
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
318
+ error_description: `Insufficent user_authentication for key attestation. Proof type 'attestation' for credential configuration '${credentialConfigurationId}', expects one of user_authentication values ${expectedUserAuthentication.join(', ')}`,
319
+ });
320
+ }
321
+ }
322
+ await this.verifyNonce(agentContext, issuer, keyAttestation.payload.nonce).catch(async (error) => {
215
323
  const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
216
324
  throw new oauth2_1.Oauth2ServerErrorResponseError({
217
325
  error: oauth2_1.Oauth2ErrorCodes.InvalidNonce,
@@ -222,36 +330,167 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
222
330
  cause: error,
223
331
  });
224
332
  });
225
- proofSigners.push(signer);
333
+ return {
334
+ bindingMethod: 'jwk',
335
+ keys: keyAttestation.payload.attested_keys.map((attestedKey) => {
336
+ return {
337
+ method: 'jwk',
338
+ jwk: core_1.Kms.PublicJwk.fromUnknown(attestedKey),
339
+ };
340
+ }),
341
+ proofType: 'attestation',
342
+ // It's up to the credential request mapper to ensure we trust the key attestation signer
343
+ // For x5c it's kinda covered already.
344
+ keyAttestation,
345
+ };
226
346
  }
227
- const signedCredentials = await this.getSignedCredentials(agentContext, {
228
- credentialRequest,
229
- issuanceSession,
230
- issuer,
231
- requestFormat: format,
232
- authorization: options.authorization,
233
- credentialRequestToCredentialMapper: options.credentialRequestToCredentialMapper,
234
- proofSigners,
235
- });
236
- // NOTE: nonce in credential response is deprecated in newer drafts, but for now we keep it in
237
- const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
238
- const credentialResponse = vcIssuer.createCredentialResponse({
239
- credential: credentialRequest.proof ? signedCredentials.credentials[0] : undefined,
240
- credentials: credentialRequest.proofs ? signedCredentials.credentials : undefined,
241
- cNonce,
242
- cNonceExpiresInSeconds,
243
- credentialRequest: parsedCredentialRequest,
347
+ if (proofType === 'jwt') {
348
+ let firstNonce = undefined;
349
+ const proofSigners = [];
350
+ for (const jwt of proofValue) {
351
+ const { signer, payload, header, keyAttestation } = await vcIssuer.verifyCredentialRequestJwtProof({
352
+ issuerMetadata,
353
+ jwt,
354
+ clientId: options.issuanceSession.clientId,
355
+ });
356
+ // TOOD: we should probably do this check before signature verification, but we then we
357
+ // first need to decode the jwt
358
+ if (!supportedProofType.proof_signing_alg_values_supported.includes(header.alg)) {
359
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
360
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
361
+ error_description: `Proof signing alg value '${header.alg}' is not supported for proof type 'jwt' in credentail configuration '${credentialConfigurationId}'`,
362
+ });
363
+ }
364
+ if (signer.method !== 'jwk' && signer.method !== 'did') {
365
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
366
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
367
+ error_description: "Only 'jwk' and 'did' binding methods supported for jwt proof",
368
+ });
369
+ }
370
+ if (proofSigners[0] && signer.method !== proofSigners[0].method) {
371
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
372
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
373
+ error_description: "All proofs must be signed using the same binding method. Found a mix of 'did' and 'jwk'",
374
+ });
375
+ }
376
+ if (proofSigners[0] && signer.alg !== proofSigners[0].alg) {
377
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
378
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
379
+ error_description: "All proofs must be signed using the same alg value. Found a mix of different 'alg' values.",
380
+ });
381
+ }
382
+ if (keyAttestation && signer.method === 'did') {
383
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
384
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
385
+ error_description: "Binding method 'did' is not supported when a key attestation is provided.",
386
+ });
387
+ }
388
+ if (supportedProofType.key_attestations_required && !keyAttestation) {
389
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
390
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
391
+ error_description: `Missing required key attestation. Key attestations are required for proof type 'jwt' in credentail configuration '${credentialConfigurationId}'`,
392
+ });
393
+ }
394
+ if (supportedProofType.key_attestations_required && keyAttestation) {
395
+ const expectedKeyStorage = supportedProofType.key_attestations_required.key_storage;
396
+ const expectedUserAuthentication = supportedProofType.key_attestations_required.user_authentication;
397
+ if (expectedKeyStorage &&
398
+ !expectedKeyStorage.some((keyStorage) => keyAttestation.payload.key_storage?.includes(keyStorage))) {
399
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
400
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
401
+ error_description: `Insufficent key_storage for key attestation. Proof type 'jwt' for credential configuration '${credentialConfigurationId}', expects one of key_storage values ${expectedKeyStorage.join(', ')}`,
402
+ });
403
+ }
404
+ if (expectedUserAuthentication &&
405
+ !expectedUserAuthentication.some((userAuthentication) => keyAttestation.payload.user_authentication?.includes(userAuthentication))) {
406
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
407
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
408
+ error_description: `Insufficent user_authentication for key attestation. Proof type 'jwt' for credential configuration '${credentialConfigurationId}', expects one of user_authentication values ${expectedUserAuthentication.join(', ')}`,
409
+ });
410
+ }
411
+ }
412
+ if (keyAttestation && proofValue.length > 1) {
413
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
414
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
415
+ error_description: "Only a single proofs entry is supported when jwt proof header contains 'key_attestation'",
416
+ });
417
+ }
418
+ if (!payload.nonce) {
419
+ const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
420
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
421
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
422
+ error_description: 'Missing nonce in proof(s) in credential request',
423
+ c_nonce: cNonce,
424
+ c_nonce_expires_in: cNonceExpiresInSeconds,
425
+ });
426
+ }
427
+ // Set previous nonce if not yet set (first iteration)
428
+ if (!firstNonce)
429
+ firstNonce = payload.nonce;
430
+ if (firstNonce !== payload.nonce) {
431
+ const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
432
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
433
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
434
+ error_description: 'Not all nonce values in proofs are equal',
435
+ c_nonce: cNonce,
436
+ c_nonce_expires_in: cNonceExpiresInSeconds,
437
+ });
438
+ }
439
+ // Verify the nonce
440
+ await this.verifyNonce(agentContext, issuer, payload.nonce).catch(async (error) => {
441
+ const { cNonce, cNonceExpiresInSeconds } = await this.createNonce(agentContext, issuer);
442
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
443
+ error: oauth2_1.Oauth2ErrorCodes.InvalidNonce,
444
+ error_description: 'Invalid nonce in credential request',
445
+ c_nonce: cNonce,
446
+ c_nonce_expires_in: cNonceExpiresInSeconds,
447
+ }, {
448
+ cause: error,
449
+ });
450
+ });
451
+ if (keyAttestation) {
452
+ return {
453
+ proofType: 'jwt',
454
+ bindingMethod: 'jwk',
455
+ keys: keyAttestation.payload.attested_keys.map((attestedKey) => {
456
+ return {
457
+ method: 'jwk',
458
+ jwk: core_1.Kms.PublicJwk.fromUnknown(attestedKey),
459
+ };
460
+ }),
461
+ keyAttestation,
462
+ };
463
+ }
464
+ proofSigners.push(signer);
465
+ }
466
+ if (proofSigners[0].method === 'did') {
467
+ const signers = proofSigners;
468
+ return {
469
+ proofType: 'jwt',
470
+ bindingMethod: 'did',
471
+ keys: signers.map((signer) => ({
472
+ didUrl: signer.didUrl,
473
+ method: 'did',
474
+ jwk: core_1.Kms.PublicJwk.fromUnknown(signer.publicJwk),
475
+ })),
476
+ };
477
+ }
478
+ return {
479
+ proofType: 'jwt',
480
+ bindingMethod: 'jwk',
481
+ keys: proofSigners.map((signer) => {
482
+ return {
483
+ method: 'jwk',
484
+ jwk: core_1.Kms.PublicJwk.fromUnknown(signer.publicJwk),
485
+ };
486
+ }),
487
+ };
488
+ }
489
+ // This will not happen, but to make TS happy
490
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
491
+ error: oauth2_1.Oauth2ErrorCodes.InvalidProof,
492
+ error_description: 'Missing required proof(s) in credential request',
244
493
  });
245
- issuanceSession.issuedCredentials.push(signedCredentials.credentialConfigurationId);
246
- const newState = issuanceSession.issuedCredentials.length >=
247
- issuanceSession.credentialOfferPayload.credential_configuration_ids.length
248
- ? OpenId4VcIssuanceSessionState_1.OpenId4VcIssuanceSessionState.Completed
249
- : OpenId4VcIssuanceSessionState_1.OpenId4VcIssuanceSessionState.CredentialsPartiallyIssued;
250
- await this.updateState(agentContext, issuanceSession, newState);
251
- return {
252
- credentialResponse,
253
- issuanceSession,
254
- };
255
494
  }
256
495
  async findIssuanceSessionsByQuery(agentContext, query, queryOptions) {
257
496
  return this.openId4VcIssuanceSessionRepository.findByQuery(agentContext, query, queryOptions);
@@ -272,17 +511,18 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
272
511
  return this.openId4VcIssuerRepository.update(agentContext, issuer);
273
512
  }
274
513
  async createIssuer(agentContext, options) {
514
+ const kms = agentContext.resolve(core_1.Kms.KeyManagementApi);
275
515
  // TODO: ideally we can store additional data with a key, such as:
276
516
  // - createdAt
277
517
  // - purpose
278
- const accessTokenSignerKey = await agentContext.wallet.createKey({
279
- keyType: options.accessTokenSignerKeyType ?? core_1.KeyType.Ed25519,
518
+ const accessTokenSignerKey = await kms.createKey({
519
+ type: options.accessTokenSignerKeyType ?? { kty: 'OKP', crv: 'Ed25519' },
280
520
  });
281
521
  const openId4VcIssuer = new repository_1.OpenId4VcIssuerRecord({
282
522
  issuerId: options.issuerId ?? core_1.utils.uuid(),
283
523
  display: options.display,
284
524
  dpopSigningAlgValuesSupported: options.dpopSigningAlgValuesSupported,
285
- accessTokenPublicKeyFingerprint: accessTokenSignerKey.fingerprint,
525
+ accessTokenPublicJwk: accessTokenSignerKey.publicJwk,
286
526
  authorizationServerConfigs: options.authorizationServerConfigs,
287
527
  credentialConfigurationsSupported: options.credentialConfigurationsSupported,
288
528
  batchCredentialIssuance: options.batchCredentialIssuance,
@@ -292,12 +532,17 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
292
532
  return openId4VcIssuer;
293
533
  }
294
534
  async rotateAccessTokenSigningKey(agentContext, issuer, options) {
295
- const accessTokenSignerKey = await agentContext.wallet.createKey({
296
- keyType: options?.accessTokenSignerKeyType ?? core_1.KeyType.Ed25519,
535
+ const kms = agentContext.resolve(core_1.Kms.KeyManagementApi);
536
+ const previousKey = issuer.resolvedAccessTokenPublicJwk;
537
+ const accessTokenSignerKey = await kms.createKey({
538
+ type: options?.accessTokenSignerKeyType ?? { kty: 'OKP', crv: 'Ed25519' },
297
539
  });
298
- // TODO: ideally we can remove the previous key
299
- issuer.accessTokenPublicKeyFingerprint = accessTokenSignerKey.fingerprint;
540
+ issuer.accessTokenPublicJwk = accessTokenSignerKey.publicJwk;
300
541
  await this.openId4VcIssuerRepository.update(agentContext, issuer);
542
+ // Remove previous key
543
+ await kms.deleteKey({
544
+ keyId: previousKey.keyId,
545
+ });
301
546
  }
302
547
  /**
303
548
  * @param fetchExternalAuthorizationServerMetadata defaults to false
@@ -356,18 +601,17 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
356
601
  const jwsService = agentContext.dependencyManager.resolve(core_1.JwsService);
357
602
  const cNonceExpiresInSeconds = this.openId4VcIssuerConfig.cNonceExpiresInSeconds;
358
603
  const cNonceExpiresAt = (0, utils_1.addSecondsToDate)(new Date(), cNonceExpiresInSeconds);
359
- const key = core_1.Key.fromFingerprint(issuer.accessTokenPublicKeyFingerprint);
360
- const jwk = (0, core_1.getJwkFromKey)(key);
604
+ const key = issuer.resolvedAccessTokenPublicJwk;
361
605
  const cNonce = await jwsService.createJwsCompact(agentContext, {
362
- key,
606
+ keyId: key.keyId,
363
607
  payload: core_1.JwtPayload.fromJson({
364
608
  iss: issuerMetadata.credentialIssuer.credential_issuer,
365
609
  exp: (0, utils_1.dateToSeconds)(cNonceExpiresAt),
366
610
  }),
367
611
  protectedHeaderOptions: {
368
612
  typ: 'credo+cnonce',
369
- kid: issuer.accessTokenPublicKeyFingerprint,
370
- alg: jwk.supportedSignatureAlgorithms[0],
613
+ kid: key.keyId,
614
+ alg: key.signatureAlgorithm,
371
615
  },
372
616
  });
373
617
  return {
@@ -384,8 +628,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
384
628
  async verifyNonce(agentContext, issuer, cNonce) {
385
629
  const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
386
630
  const jwsService = agentContext.dependencyManager.resolve(core_1.JwsService);
387
- const key = core_1.Key.fromFingerprint(issuer.accessTokenPublicKeyFingerprint);
388
- const jwk = (0, core_1.getJwkFromKey)(key);
631
+ const key = issuer.resolvedAccessTokenPublicJwk;
389
632
  const jwt = core_1.Jwt.fromSerializedJwt(cNonce);
390
633
  jwt.payload.validate();
391
634
  if (jwt.payload.iss !== issuerMetadata.credentialIssuer.credential_issuer) {
@@ -396,17 +639,18 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
396
639
  }
397
640
  const verification = await jwsService.verifyJws(agentContext, {
398
641
  jws: cNonce,
399
- jwkResolver: () => jwk,
642
+ jwsSigner: {
643
+ method: 'jwk',
644
+ jwk: key,
645
+ },
400
646
  });
401
- if (!verification.signerKeys
402
- .map((singerKey) => singerKey.fingerprint)
403
- .includes(issuer.accessTokenPublicKeyFingerprint)) {
647
+ if (!verification.isValid) {
404
648
  throw new core_1.CredoError('Invalid nonce');
405
649
  }
406
650
  }
407
- getIssuer(agentContext) {
651
+ getIssuer(agentContext, options = {}) {
408
652
  return new openid4vci_1.Openid4vciIssuer({
409
- callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext),
653
+ callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext, options),
410
654
  });
411
655
  }
412
656
  getOauth2Client(agentContext) {
@@ -414,9 +658,9 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
414
658
  callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext),
415
659
  });
416
660
  }
417
- getOauth2AuthorizationServer(agentContext) {
661
+ getOauth2AuthorizationServer(agentContext, options = {}) {
418
662
  return new oauth2_1.Oauth2AuthorizationServer({
419
- callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext),
663
+ callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext, options),
420
664
  });
421
665
  }
422
666
  getResourceServer(agentContext, issuerRecord) {
@@ -449,6 +693,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
449
693
  });
450
694
  }
451
695
  async getGrantsFromConfig(agentContext, config) {
696
+ const kms = agentContext.resolve(core_1.Kms.KeyManagementApi);
452
697
  const { preAuthorizedCodeFlowConfig, authorizationCodeFlowConfig, issuerMetadata } = config;
453
698
  // TOOD: export type
454
699
  const grants = {};
@@ -456,7 +701,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
456
701
  if (preAuthorizedCodeFlowConfig) {
457
702
  const { txCode, authorizationServerUrl, preAuthorizedCode } = preAuthorizedCodeFlowConfig;
458
703
  grants[oauth2_1.preAuthorizedCodeGrantIdentifier] = {
459
- 'pre-authorized_code': preAuthorizedCode ?? (await agentContext.wallet.generateNonce()),
704
+ 'pre-authorized_code': preAuthorizedCode ?? core_1.TypedArrayEncoder.toBase64URL(kms.randomBytes({ length: 32 })),
460
705
  tx_code: txCode,
461
706
  authorization_server: config.issuerMetadata.credentialIssuer.authorization_servers
462
707
  ? authorizationServerUrl
@@ -477,8 +722,7 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
477
722
  issuer_state:
478
723
  // TODO: the issuer_state should not be guessable, so it's best if we generate it and now allow the user to provide it?
479
724
  // but same is true for the pre-auth code and users of credo can also provide that value. We can't easily do unique constraint with askat
480
- authorizationCodeFlowConfig.issuerState ??
481
- core_1.TypedArrayEncoder.toBase64URL(agentContext.wallet.getRandomValues(32)),
725
+ authorizationCodeFlowConfig.issuerState ?? core_1.TypedArrayEncoder.toBase64URL(kms.randomBytes({ length: 32 })),
482
726
  authorization_server: config.issuerMetadata.credentialIssuer.authorization_servers
483
727
  ? authorizationServerUrl
484
728
  : undefined,
@@ -486,38 +730,23 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
486
730
  }
487
731
  return grants;
488
732
  }
489
- async getHolderBindingFromRequestProofs(agentContext, proofSigners) {
490
- const credentialHolderBindings = [];
491
- for (const signer of proofSigners) {
492
- if (signer.method === 'custom' || signer.method === 'x5c') {
493
- throw new core_1.CredoError(`Only 'jwk' and 'did' based holder binding is supported`);
494
- }
495
- if (signer.method === 'jwk') {
496
- const jwk = (0, core_1.getJwkFromJson)(signer.publicJwk);
497
- credentialHolderBindings.push({
498
- method: 'jwk',
499
- jwk,
500
- key: jwk.key,
501
- });
502
- }
503
- if (signer.method === 'did') {
504
- const key = await (0, utils_1.getKeyFromDid)(agentContext, signer.didUrl);
505
- credentialHolderBindings.push({
506
- method: 'did',
507
- didUrl: signer.didUrl,
508
- key,
509
- });
510
- }
511
- }
512
- return credentialHolderBindings;
513
- }
514
733
  getCredentialConfigurationsForRequest(options) {
515
- const { requestFormat, issuanceSession, issuerMetadata, authorization } = options;
734
+ const { requestFormat, issuanceSession, issuerMetadata, authorization, credentialConfigurations } = options;
516
735
  // Check against all credential configurations
517
- const configurationsMatchingRequest = (0, openid4vci_1.getCredentialConfigurationsMatchingRequestFormat)({
518
- requestFormat,
519
- credentialConfigurations: issuerMetadata.credentialIssuer.credential_configurations_supported,
520
- });
736
+ const configurationsMatchingRequest = credentialConfigurations
737
+ ? credentialConfigurations
738
+ : requestFormat
739
+ ? (0, openid4vci_1.getCredentialConfigurationsMatchingRequestFormat)({
740
+ requestFormat,
741
+ credentialConfigurations: issuerMetadata.credentialIssuer.credential_configurations_supported,
742
+ })
743
+ : undefined;
744
+ if (!configurationsMatchingRequest) {
745
+ throw new oauth2_1.Oauth2ServerErrorResponseError({
746
+ error: oauth2_1.Oauth2ErrorCodes.InvalidCredentialRequest,
747
+ error_description: `Either 'credential_configuration_id' or 'format' needs to be defined'`,
748
+ });
749
+ }
521
750
  if (Object.keys(configurationsMatchingRequest).length === 0) {
522
751
  throw new oauth2_1.Oauth2ServerErrorResponseError({
523
752
  error: oauth2_1.Oauth2ErrorCodes.InvalidCredentialRequest,
@@ -542,9 +771,12 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
542
771
  }
543
772
  // For pre-auth we allow all ids from the offer
544
773
  if (authorization.accessToken.payload['pre-authorized_code']) {
774
+ // We return the first one that matches all checks. Pre draft 15 it could be multiple entries, but only if you offer
775
+ // multiple credentials of the same type. We need to do checks on this, so we pick the first one
776
+ const [credentialConfigurationId, credentialConfiguration] = Object.entries(configurationsMatchingRequestAndOfferNotIssued)[0];
545
777
  return {
546
- credentialConfigurations: configurationsMatchingRequestAndOfferNotIssued,
547
- credentialConfigurationIds: Object.keys(configurationsMatchingRequestAndOfferNotIssued),
778
+ credentialConfigurationId,
779
+ credentialConfiguration,
548
780
  };
549
781
  }
550
782
  // Limit to scopes from the token
@@ -558,20 +790,16 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
558
790
  status: 403,
559
791
  });
560
792
  }
793
+ // We return the first one that matches all checks. Pre draft 15 it could be multiple entries, but only if you offer
794
+ // multiple credentials of the same type. We need to do checks on this, so we pick the first one
795
+ const [credentialConfigurationId, credentialConfiguration] = Object.entries(configurationsMatchingRequestOfferScope)[0];
561
796
  return {
562
- credentialConfigurations: configurationsMatchingRequestOfferScope,
563
- credentialConfigurationIds: Object.keys(configurationsMatchingRequestOfferScope),
797
+ credentialConfigurationId,
798
+ credentialConfiguration: credentialConfiguration,
564
799
  };
565
800
  }
566
801
  async getSignedCredentials(agentContext, options) {
567
- const { issuanceSession, issuer, requestFormat, authorization } = options;
568
- const issuerMetadata = await this.getIssuerMetadata(agentContext, issuer);
569
- const { credentialConfigurations, credentialConfigurationIds } = this.getCredentialConfigurationsForRequest({
570
- issuanceSession,
571
- issuerMetadata,
572
- requestFormat,
573
- authorization,
574
- });
802
+ const { issuanceSession, credentialConfiguration, credentialConfigurationId, credentialRequestProofs } = options;
575
803
  const mapper = options.credentialRequestToCredentialMapper ?? this.openId4VcIssuerConfig.credentialRequestToCredentialMapper;
576
804
  let verification = undefined;
577
805
  // NOTE: this will throw an error if the verifier module is not registered and there is a
@@ -596,28 +824,24 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
596
824
  throw new core_1.CredoError(`Verified authorization response for verification session with id '${session.id}' does not have presenationExchange or dcql defined.`);
597
825
  }
598
826
  }
599
- const holderBindings = await this.getHolderBindingFromRequestProofs(agentContext, options.proofSigners);
600
827
  const signOptions = await mapper({
601
828
  agentContext,
602
829
  issuanceSession,
603
- holderBindings,
830
+ holderBinding: credentialRequestProofs,
604
831
  credentialOffer: issuanceSession.credentialOfferPayload,
605
832
  verification,
606
833
  credentialRequest: options.credentialRequest,
607
834
  credentialRequestFormat: options.requestFormat,
608
- // Macthing credential configuration ids
609
- credentialConfigurationsSupported: credentialConfigurations,
610
- credentialConfigurationIds,
835
+ // Macthing credential configuration
836
+ credentialConfiguration,
837
+ credentialConfigurationId,
611
838
  // Authorization
612
839
  authorization: options.authorization,
613
840
  });
614
- if (!credentialConfigurationIds.includes(signOptions.credentialConfigurationId)) {
615
- throw new core_1.CredoError(`Credential request to credential mapper returned credential configuration id '${signOptions.credentialConfigurationId}' but is not part of provided input credential configuration ids. Allowed values are '${credentialConfigurationIds.join(', ')}'.`);
616
- }
617
- // NOTE: we may want to allow a mismatch between this (as with new attestations not every key
618
- // needs a separate proof), but for now it needs to match
619
- if (signOptions.credentials.length !== holderBindings.length) {
620
- throw new core_1.CredoError(`Credential request to credential mapper returned '${signOptions.credentials.length}' to be signed, while only '${holderBindings.length}' holder binding entries were provided. Make sure to return one credential for each holder binding entry`);
841
+ const expectedLength = credentialRequestProofs.keys.length;
842
+ // NOTE: we may want to allow a mismatch between this (as there is a match batch length), but for now it needs to match
843
+ if (signOptions.credentials.length !== expectedLength) {
844
+ throw new core_1.CredoError(`Credential request to credential mapper returned '${signOptions.credentials.length}' to be signed, while '${expectedLength}' holder binding entries were provided. Make sure to return one credential for each holder binding entry`);
621
845
  }
622
846
  if (signOptions.format === core_1.ClaimFormat.JwtVc || signOptions.format === core_1.ClaimFormat.LdpVc) {
623
847
  const oid4vciFormatMap = {
@@ -625,40 +849,44 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
625
849
  [shared_1.OpenId4VciCredentialFormatProfile.JwtVcJsonLd]: core_1.ClaimFormat.JwtVc,
626
850
  [shared_1.OpenId4VciCredentialFormatProfile.LdpVc]: core_1.ClaimFormat.LdpVc,
627
851
  };
628
- const expectedClaimFormat = oid4vciFormatMap[options.requestFormat.format];
852
+ const expectedClaimFormat = oid4vciFormatMap[credentialConfiguration.format];
629
853
  if (signOptions.format !== expectedClaimFormat) {
630
854
  throw new core_1.CredoError(`Invalid credential format returned by sign options. Expected '${expectedClaimFormat}', received '${signOptions.format}'.`);
631
855
  }
632
856
  return {
633
- credentialConfigurationId: signOptions.credentialConfigurationId,
634
- format: requestFormat.format,
857
+ format: credentialConfiguration.format,
635
858
  credentials: (await Promise.all(signOptions.credentials.map((credential) => this.signW3cCredential(agentContext, signOptions.format, credential).then((signed) => signed.encoded)))),
636
859
  };
637
860
  }
638
861
  if (signOptions.format === core_1.ClaimFormat.SdJwtVc) {
639
- if (signOptions.format !== requestFormat.format) {
640
- throw new core_1.CredoError(`Invalid credential format returned by sign options. Expected '${requestFormat.format}', received '${signOptions.format}'.`);
862
+ if (credentialConfiguration.format !== shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc &&
863
+ credentialConfiguration.format !== shared_1.OpenId4VciCredentialFormatProfile.SdJwtDc) {
864
+ throw new core_1.CredoError(`Invalid credential format returned by sign options. Expected '${core_1.ClaimFormat.SdJwtVc}', received '${signOptions.format}'.`);
641
865
  }
642
- if (!signOptions.credentials.every((c) => c.payload.vct === requestFormat.vct)) {
643
- throw new core_1.CredoError(`One or more vct values of the offered credential(s) do not match the vct of the requested credential. Offered ${Array.from(new Set(signOptions.credentials.map((c) => `'${c.payload.vct}'`))).join(', ')} Requested '${requestFormat.vct}'.`);
866
+ if (!signOptions.credentials.every((c) => c.payload.vct === credentialConfiguration.vct)) {
867
+ throw new core_1.CredoError(`One or more vct values of the offered credential(s) do not match the vct of the requested credential. Offered ${Array.from(new Set(signOptions.credentials.map((c) => `'${c.payload.vct}'`))).join(', ')} Requested '${credentialConfiguration.vct}'.`);
644
868
  }
645
869
  const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
646
870
  return {
647
- credentialConfigurationId: signOptions.credentialConfigurationId,
648
- format: shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc,
649
- credentials: await Promise.all(signOptions.credentials.map((credential) => sdJwtVcApi.sign(credential).then((signed) => signed.compact))),
871
+ format: credentialConfiguration.format,
872
+ credentials: await Promise.all(signOptions.credentials.map((credential) => sdJwtVcApi
873
+ .sign({
874
+ ...credential,
875
+ // Set header type based on the oid4vci format
876
+ headerType: credentialConfiguration.format,
877
+ })
878
+ .then((signed) => signed.compact))),
650
879
  };
651
880
  }
652
881
  if (signOptions.format === core_1.ClaimFormat.MsoMdoc) {
653
- if (signOptions.format !== requestFormat.format) {
654
- throw new core_1.CredoError(`Invalid credential format returned by sign options. Expected '${requestFormat.format}', received '${signOptions.format}'.`);
882
+ if (signOptions.format !== credentialConfiguration.format) {
883
+ throw new core_1.CredoError(`Invalid credential format returned by sign options. Expected '${credentialConfiguration.format}', received '${signOptions.format}'.`);
655
884
  }
656
- if (!signOptions.credentials.every((c) => c.docType === requestFormat.doctype)) {
657
- throw new core_1.CredoError(`One or more doctype values of the offered credential(s) do not match the doctype of the requested credential. Offered ${Array.from(new Set(signOptions.credentials.map((c) => `'${c.docType}'`))).join(', ')} Requested '${requestFormat.doctype}'.`);
885
+ if (!signOptions.credentials.every((c) => c.docType === credentialConfiguration.doctype)) {
886
+ throw new core_1.CredoError(`One or more doctype values of the offered credential(s) do not match the doctype of the requested credential. Offered ${Array.from(new Set(signOptions.credentials.map((c) => `'${c.docType}'`))).join(', ')} Requested '${credentialConfiguration.doctype}'.`);
658
887
  }
659
888
  const mdocApi = agentContext.dependencyManager.resolve(core_1.MdocApi);
660
889
  return {
661
- credentialConfigurationId: signOptions.credentialConfigurationId,
662
890
  format: shared_1.OpenId4VciCredentialFormatProfile.MsoMdoc,
663
891
  credentials: await Promise.all(signOptions.credentials.map((credential) => mdocApi.sign(credential).then((signed) => signed.base64Url))),
664
892
  };
@@ -666,24 +894,16 @@ let OpenId4VcIssuerService = class OpenId4VcIssuerService {
666
894
  throw new core_1.CredoError(`Unsupported credential format ${signOptions.format}`);
667
895
  }
668
896
  async signW3cCredential(agentContext, format, options) {
669
- const key = await (0, utils_1.getKeyFromDid)(agentContext, options.verificationMethod);
897
+ const publicJwk = await (0, utils_1.getPublicJwkFromDid)(agentContext, options.verificationMethod);
670
898
  if (format === core_1.ClaimFormat.JwtVc) {
671
- const supportedSignatureAlgorithms = (0, core_1.getJwkFromKey)(key).supportedSignatureAlgorithms;
672
- if (supportedSignatureAlgorithms.length === 0) {
673
- throw new core_1.CredoError(`No supported JWA signature algorithms found for key with keyType ${key.keyType}`);
674
- }
675
- const alg = supportedSignatureAlgorithms[0];
676
- if (!alg) {
677
- throw new core_1.CredoError(`No supported JWA signature algorithms for key type ${key.keyType}`);
678
- }
679
899
  return await this.w3cCredentialService.signCredential(agentContext, {
680
900
  format: core_1.ClaimFormat.JwtVc,
681
901
  credential: options.credential,
682
902
  verificationMethod: options.verificationMethod,
683
- alg,
903
+ alg: publicJwk.signatureAlgorithm,
684
904
  });
685
905
  }
686
- const proofType = (0, utils_1.getProofTypeFromKey)(agentContext, key);
906
+ const proofType = (0, utils_1.getProofTypeFromPublicJwk)(agentContext, publicJwk);
687
907
  return await this.w3cCredentialService.signCredential(agentContext, {
688
908
  format: core_1.ClaimFormat.LdpVc,
689
909
  credential: options.credential,