@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
@@ -14,8 +14,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.OpenId4VciHolderService = void 0;
16
16
  const core_1 = require("@credo-ts/core");
17
+ const core_2 = require("@credo-ts/core");
17
18
  const oauth2_1 = require("@openid4vc/oauth2");
18
19
  const openid4vci_1 = require("@openid4vc/openid4vci");
20
+ const openid4vci_2 = require("@openid4vc/openid4vci");
19
21
  const shared_1 = require("../shared");
20
22
  const callbacks_1 = require("../shared/callbacks");
21
23
  const issuerMetadataUtils_1 = require("../shared/issuerMetadataUtils");
@@ -49,28 +51,63 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
49
51
  async resolveAuthorizationRequest(agentContext, resolvedCredentialOffer, authCodeFlowOptions) {
50
52
  const { clientId, redirectUri } = authCodeFlowOptions;
51
53
  const { metadata, credentialOfferPayload, offeredCredentialConfigurations } = resolvedCredentialOffer;
52
- const client = this.getClient(agentContext);
54
+ const oauth2Client = this.getOauth2Client(agentContext);
55
+ const client = this.getClient(agentContext, {
56
+ clientId: authCodeFlowOptions.clientId,
57
+ clientAttestation: authCodeFlowOptions.walletAttestationJwt,
58
+ });
53
59
  // If scope is not provided, we request scope for all offered credentials
54
60
  const scope = authCodeFlowOptions.scope ?? (0, issuerMetadataUtils_1.getScopesFromCredentialConfigurationsSupported)(offeredCredentialConfigurations);
61
+ if (!credentialOfferPayload.grants?.[oauth2_1.authorizationCodeGrantIdentifier]) {
62
+ throw new core_2.CredoError(`Provided credential offer does not include the 'authorization_code' grant.`);
63
+ }
64
+ const authorizationCodeGrant = credentialOfferPayload.grants[oauth2_1.authorizationCodeGrantIdentifier];
65
+ const authorizationServer = (0, openid4vci_1.determineAuthorizationServerForCredentialOffer)({
66
+ issuerMetadata: metadata,
67
+ grantAuthorizationServer: authorizationCodeGrant.authorization_server,
68
+ });
69
+ const authorizationServerMetadata = (0, oauth2_1.getAuthorizationServerMetadataFromList)(metadata.authorizationServers, authorizationServer);
70
+ // TODO: should we allow key reuse between dpop and wallet attestation?
71
+ const isDpopSupported = oauth2Client.isDpopSupported({ authorizationServerMetadata });
72
+ const dpop = isDpopSupported.supported
73
+ ? await this.getDpopOptions(agentContext, {
74
+ dpopSigningAlgValuesSupported: isDpopSupported.dpopSigningAlgValuesSupported,
75
+ })
76
+ : undefined;
55
77
  const authorizationResult = await client.initiateAuthorization({
56
78
  clientId,
57
79
  issuerMetadata: metadata,
58
80
  credentialOffer: credentialOfferPayload,
59
81
  scope: scope.join(' '),
60
82
  redirectUri,
83
+ dpop,
61
84
  });
62
- if (authorizationResult.authorizationFlow === openid4vci_1.AuthorizationFlow.PresentationDuringIssuance) {
85
+ if (authorizationResult.authorizationFlow === openid4vci_2.AuthorizationFlow.PresentationDuringIssuance) {
63
86
  return {
64
- authorizationFlow: openid4vci_1.AuthorizationFlow.PresentationDuringIssuance,
87
+ authorizationFlow: openid4vci_2.AuthorizationFlow.PresentationDuringIssuance,
65
88
  openid4vpRequestUrl: authorizationResult.openid4vpRequestUrl,
66
89
  authSession: authorizationResult.authSession,
90
+ // FIXME: return dpop result from this endpoint (dpop nonce)
91
+ dpop: dpop
92
+ ? {
93
+ alg: dpop.signer.alg,
94
+ jwk: core_2.Kms.PublicJwk.fromUnknown(dpop.signer.publicJwk),
95
+ }
96
+ : undefined,
67
97
  };
68
98
  }
69
99
  // Normal Oauth2Redirect flow
70
100
  return {
71
- authorizationFlow: openid4vci_1.AuthorizationFlow.Oauth2Redirect,
101
+ authorizationFlow: openid4vci_2.AuthorizationFlow.Oauth2Redirect,
72
102
  codeVerifier: authorizationResult.pkce?.codeVerifier,
73
103
  authorizationRequestUrl: authorizationResult.authorizationRequestUrl,
104
+ // FIXME: return dpop result from this endpoint (dpop nonce)
105
+ dpop: dpop
106
+ ? {
107
+ alg: dpop.signer.alg,
108
+ jwk: core_2.Kms.PublicJwk.fromUnknown(dpop.signer.publicJwk),
109
+ }
110
+ : undefined,
74
111
  };
75
112
  }
76
113
  async sendNotification(agentContext, options) {
@@ -91,10 +128,11 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
91
128
  });
92
129
  }
93
130
  async getDpopOptions(agentContext, { jwk, dpopSigningAlgValuesSupported, nonce, }) {
131
+ const kms = agentContext.resolve(core_2.Kms.KeyManagementApi);
94
132
  if (jwk) {
95
133
  const alg = dpopSigningAlgValuesSupported.find((alg) => jwk.supportedSignatureAlgorithms.includes(alg));
96
134
  if (!alg) {
97
- throw new core_1.CredoError(`No supported dpop signature algorithms found in dpop_signing_alg_values_supported '${dpopSigningAlgValuesSupported.join(', ')}' matching key type ${jwk.keyType}`);
135
+ throw new core_2.CredoError(`No supported dpop signature algorithms found in dpop_signing_alg_values_supported '${dpopSigningAlgValuesSupported.join(', ')}' matching jwk ${jwk.jwkTypehumanDescription}`);
98
136
  }
99
137
  return {
100
138
  signer: {
@@ -105,59 +143,83 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
105
143
  nonce,
106
144
  };
107
145
  }
108
- const alg = dpopSigningAlgValuesSupported.find((alg) => (0, core_1.getJwkClassFromJwaSignatureAlgorithm)(alg));
109
- const JwkClass = alg ? (0, core_1.getJwkClassFromJwaSignatureAlgorithm)(alg) : undefined;
110
- if (!alg || !JwkClass) {
111
- throw new core_1.CredoError(`No supported dpop signature algorithms found in dpop_signing_alg_values_supported '${dpopSigningAlgValuesSupported.join(', ')}'`);
146
+ const alg = dpopSigningAlgValuesSupported.find((alg) => {
147
+ try {
148
+ core_2.Kms.PublicJwk.supportedPublicJwkClassForSignatureAlgorithm(alg);
149
+ return true;
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ });
155
+ if (!alg) {
156
+ throw new core_2.CredoError(`No supported dpop signature algorithms found in dpop_signing_alg_values_supported '${dpopSigningAlgValuesSupported.join(', ')}'`);
112
157
  }
113
- const key = await agentContext.wallet.createKey({ keyType: JwkClass.keyType });
158
+ const key = await kms.createKeyForSignatureAlgorithm({ algorithm: alg });
114
159
  return {
115
160
  signer: {
116
161
  method: 'jwk',
117
162
  alg,
118
- publicJwk: (0, core_1.getJwkFromKey)(key).toJson(),
163
+ publicJwk: key.publicJwk,
119
164
  },
120
165
  nonce,
121
166
  };
122
167
  }
123
168
  async retrieveAuthorizationCodeUsingPresentation(agentContext, options) {
124
- const client = this.getClient(agentContext);
125
- // TODO: support dpop on this endpoint as well
126
- // const dpop = options.dpop
127
- // ? await this.getDpopOptions(agentContext, {
128
- // ...options.dpop,
129
- // dpopSigningAlgValuesSupported: [options.dpop.alg],
130
- // })
131
- // : undefined
132
- // TODO: we should support DPoP in this request as well
133
- const { authorizationChallengeResponse } = await client.retrieveAuthorizationCodeUsingPresentation({
169
+ const client = this.getClient(agentContext, {
170
+ clientAttestation: options.walletAttestationJwt,
171
+ });
172
+ const dpop = options.dpop
173
+ ? await this.getDpopOptions(agentContext, {
174
+ ...options.dpop,
175
+ dpopSigningAlgValuesSupported: [options.dpop.alg],
176
+ })
177
+ : undefined;
178
+ const { authorizationChallengeResponse, dpop: dpopResult } = await client.retrieveAuthorizationCodeUsingPresentation({
134
179
  authSession: options.authSession,
135
180
  presentationDuringIssuanceSession: options.presentationDuringIssuanceSession,
136
181
  credentialOffer: options.resolvedCredentialOffer.credentialOfferPayload,
137
182
  issuerMetadata: options.resolvedCredentialOffer.metadata,
138
- // dpop
183
+ dpop,
139
184
  });
140
185
  return {
141
186
  authorizationCode: authorizationChallengeResponse.authorization_code,
187
+ dpop: dpop
188
+ ? {
189
+ ...dpopResult,
190
+ alg: dpop.signer.alg,
191
+ jwk: core_2.Kms.PublicJwk.fromUnknown(dpop.signer.publicJwk),
192
+ }
193
+ : undefined,
142
194
  };
143
195
  }
144
196
  async requestAccessToken(agentContext, options) {
145
197
  const { metadata, credentialOfferPayload } = options.resolvedCredentialOffer;
146
- const client = this.getClient(agentContext);
198
+ const client = this.getClient(agentContext, {
199
+ clientAttestation: options.walletAttestationJwt,
200
+ clientId: 'clientId' in options ? options.clientId : undefined,
201
+ });
147
202
  const oauth2Client = this.getOauth2Client(agentContext);
148
203
  const authorizationServer = options.code
149
204
  ? credentialOfferPayload.grants?.authorization_code?.authorization_server
150
205
  : credentialOfferPayload.grants?.[oauth2_1.preAuthorizedCodeGrantIdentifier]?.authorization_server;
151
206
  const authorizationServerMetadata = (0, oauth2_1.getAuthorizationServerMetadataFromList)(metadata.authorizationServers, authorizationServer ?? metadata.authorizationServers[0].issuer);
152
- // TODO: should allow dpop input parameter for if it was already bound earlier
153
207
  const isDpopSupported = oauth2Client.isDpopSupported({
154
208
  authorizationServerMetadata,
155
209
  });
156
- const dpop = isDpopSupported.supported
210
+ const dpop = options.dpop
157
211
  ? await this.getDpopOptions(agentContext, {
158
- dpopSigningAlgValuesSupported: isDpopSupported.dpopSigningAlgValuesSupported,
212
+ ...options.dpop,
213
+ dpopSigningAlgValuesSupported: [options.dpop.alg],
159
214
  })
160
- : undefined;
215
+ : // We should be careful about this case. It could just be the user didn't correctly
216
+ // provide the DPoP from the auth response. In whic case different DPoP will be used
217
+ // However it might be that they only use DPoP for the token request (esp in pre-auth case)
218
+ isDpopSupported.supported
219
+ ? await this.getDpopOptions(agentContext, {
220
+ dpopSigningAlgValuesSupported: isDpopSupported.dpopSigningAlgValuesSupported,
221
+ })
222
+ : undefined;
161
223
  const result = options.code
162
224
  ? await client.retrieveAuthorizationCodeAccessTokenFromOffer({
163
225
  issuerMetadata: metadata,
@@ -166,11 +228,6 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
166
228
  dpop,
167
229
  pkceCodeVerifier: options.codeVerifier,
168
230
  redirectUri: options.redirectUri,
169
- additionalRequestPayload: {
170
- // TODO: handle it as part of client auth once we support
171
- // assertion based client authentication
172
- client_id: options.clientId,
173
- },
174
231
  })
175
232
  : await client.retrievePreAuthorizedCodeAccessTokenFromOffer({
176
233
  credentialOffer: credentialOfferPayload,
@@ -184,7 +241,7 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
184
241
  ? {
185
242
  ...result.dpop,
186
243
  alg: dpop.signer.alg,
187
- jwk: (0, core_1.getJwkFromJson)(dpop.signer.publicJwk),
244
+ jwk: core_2.Kms.PublicJwk.fromUnknown(dpop.signer.publicJwk),
188
245
  }
189
246
  : undefined,
190
247
  };
@@ -192,22 +249,10 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
192
249
  async acceptCredentialOffer(agentContext, options) {
193
250
  const { resolvedCredentialOffer, acceptCredentialOfferOptions } = options;
194
251
  const { metadata, offeredCredentialConfigurations } = resolvedCredentialOffer;
195
- const { credentialConfigurationIds, credentialBindingResolver, verifyCredentialStatus, requestBatch } = acceptCredentialOfferOptions;
252
+ const { credentialConfigurationIds, credentialBindingResolver, verifyCredentialStatus, allowedProofOfPossessionSignatureAlgorithms, } = acceptCredentialOfferOptions;
196
253
  const client = this.getClient(agentContext);
197
254
  if (credentialConfigurationIds?.length === 0) {
198
- throw new core_1.CredoError(`'credentialConfigurationIds' may not be empty`);
199
- }
200
- const supportedJwaSignatureAlgorithms = (0, utils_1.getSupportedJwaSignatureAlgorithms)(agentContext);
201
- const allowedProofOfPossessionSigAlgs = acceptCredentialOfferOptions.allowedProofOfPossessionSignatureAlgorithms;
202
- const possibleProofOfPossessionSigAlgs = allowedProofOfPossessionSigAlgs
203
- ? allowedProofOfPossessionSigAlgs.filter((algorithm) => supportedJwaSignatureAlgorithms.includes(algorithm))
204
- : supportedJwaSignatureAlgorithms;
205
- if (possibleProofOfPossessionSigAlgs.length === 0) {
206
- throw new core_1.CredoError([
207
- 'No possible proof of possession signature algorithm found.',
208
- `Signature algorithms supported by the Agent '${supportedJwaSignatureAlgorithms.join(', ')}'`,
209
- `Allowed Signature algorithms '${allowedProofOfPossessionSigAlgs?.join(', ')}'`,
210
- ].join('\n'));
255
+ throw new core_2.CredoError(`'credentialConfigurationIds' may not be empty`);
211
256
  }
212
257
  const receivedCredentials = [];
213
258
  let cNonce = options.cNonce;
@@ -215,7 +260,7 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
215
260
  const credentialConfigurationsToRequest = credentialConfigurationIds?.map((id) => {
216
261
  if (!offeredCredentialConfigurations[id]) {
217
262
  const offeredCredentialIds = Object.keys(offeredCredentialConfigurations).join(', ');
218
- throw new core_1.CredoError(`Credential to request '${id}' is not present in offered credentials. Offered credentials are ${offeredCredentialIds}`);
263
+ throw new core_2.CredoError(`Credential to request '${id}' is not present in offered credentials. Offered credentials are ${offeredCredentialIds}`);
219
264
  }
220
265
  return [id, offeredCredentialConfigurations[id]];
221
266
  }) ?? Object.entries(offeredCredentialConfigurations);
@@ -242,46 +287,41 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
242
287
  : undefined,
243
288
  })
244
289
  .catch((e) => {
245
- if (e instanceof openid4vci_1.Openid4vciRetrieveCredentialsError && e.response.credentialErrorResponseResult?.success) {
290
+ if (e instanceof openid4vci_2.Openid4vciRetrieveCredentialsError && e.response.credentialErrorResponseResult?.success) {
246
291
  cNonce = e.response.credentialErrorResponseResult.data.c_nonce;
247
292
  }
248
293
  });
249
294
  }
250
295
  }
251
296
  if (!cNonce) {
252
- throw new core_1.CredoError('No cNonce provided and unable to acquire cNonce from the credential issuer');
253
- }
254
- // If true: use max from issuer or otherwise 1
255
- // If number not 0: use the number
256
- // Else: use 1
257
- const batchSize = requestBatch === true ? (metadata.credentialIssuer.batch_credential_issuance?.batch_size ?? 1) : requestBatch || 1;
258
- if (typeof requestBatch === 'number' && requestBatch > 1 && !metadata.credentialIssuer.batch_credential_issuance) {
259
- throw new core_1.CredoError(`Credential issuer '${metadata.credentialIssuer.credential_issuer}' does not support batch credential issuance using the 'proofs' request property. Onlt 'proof' supported.`);
297
+ throw new core_2.CredoError('No cNonce provided and unable to acquire cNonce from the credential issuer');
260
298
  }
261
299
  for (const [offeredCredentialId, offeredCredentialConfiguration] of credentialConfigurationsToRequest) {
262
- const jwts = [];
263
- for (let i = 0; i < batchSize; i++) {
264
- // TODO: we should call this method once with a keyLength. Gives more control to the user and better aligns with key attestations
265
- // Get a key instance for each entry in the batch.
266
- // Get all options for the credential request (such as which kid to use, the signature algorithm, etc)
267
- const { jwtSigner } = await this.getCredentialRequestOptions(agentContext, {
268
- possibleProofOfPossessionSignatureAlgorithms: possibleProofOfPossessionSigAlgs,
269
- offeredCredential: {
270
- id: offeredCredentialId,
271
- configuration: offeredCredentialConfiguration,
272
- },
273
- credentialBindingResolver,
274
- });
275
- const { jwt } = await client.createCredentialRequestJwtProof({
276
- credentialConfigurationId: offeredCredentialId,
277
- issuerMetadata: resolvedCredentialOffer.metadata,
278
- signer: jwtSigner,
279
- clientId: options.clientId,
280
- nonce: cNonce,
281
- });
282
- this.logger.debug('Generated credential request proof of possesion jwt', { jwt });
283
- jwts.push(jwt);
284
- }
300
+ const proofs = await this.getCredentialRequestOptions(agentContext, {
301
+ allowedProofOfPossesionAlgorithms: allowedProofOfPossessionSignatureAlgorithms ?? (0, utils_1.getSupportedJwaSignatureAlgorithms)(agentContext),
302
+ metadata,
303
+ offeredCredential: {
304
+ id: offeredCredentialId,
305
+ configuration: offeredCredentialConfiguration,
306
+ },
307
+ clientId: options.clientId,
308
+ // We already checked whether nonce exists above
309
+ cNonce: cNonce,
310
+ credentialBindingResolver,
311
+ });
312
+ this.logger.debug('Generated credential request proof of possesion', { proofs });
313
+ const proof =
314
+ // Draft 11 ALWAYS uses proof
315
+ (metadata.originalDraftVersion === openid4vci_2.Openid4vciDraftVersion.Draft11 ||
316
+ // Draft 14 allows both proof and proofs. Try to use proof when it makes to improve interoperability
317
+ (metadata.originalDraftVersion === openid4vci_2.Openid4vciDraftVersion.Draft14 &&
318
+ metadata.credentialIssuer.batch_credential_issuance === undefined)) &&
319
+ proofs.jwt?.length === 1
320
+ ? {
321
+ proof_type: 'jwt',
322
+ jwt: proofs.jwt[0],
323
+ }
324
+ : undefined;
285
325
  const { credentialResponse, dpop } = await client.retrieveCredentials({
286
326
  issuerMetadata: metadata,
287
327
  accessToken: options.accessToken,
@@ -293,13 +333,9 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
293
333
  dpopSigningAlgValuesSupported: [options.dpop.alg],
294
334
  })
295
335
  : undefined,
296
- proofs: batchSize > 1 ? { jwt: jwts } : undefined,
297
- proof: batchSize === 1
298
- ? {
299
- proof_type: 'jwt',
300
- jwt: jwts[0],
301
- }
302
- : undefined,
336
+ // Only include proofs if we don't add proof
337
+ proofs: !proof ? proofs : undefined,
338
+ proof,
303
339
  });
304
340
  // Set new nonce values
305
341
  cNonce = credentialResponse.c_nonce;
@@ -310,9 +346,10 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
310
346
  credentialIssuerMetadata: metadata.credentialIssuer,
311
347
  format: offeredCredentialConfiguration.format,
312
348
  credentialConfigurationId: offeredCredentialId,
349
+ credentialConfiguration: offeredCredentialConfiguration,
313
350
  });
314
- this.logger.debug('received credential', credential.credentials.map((c) => c instanceof core_1.Mdoc ? { issuerSignedNamespaces: c.issuerSignedNamespaces, base64Url: c.base64Url } : c));
315
- receivedCredentials.push({ ...credential, credentialConfigurationId: offeredCredentialId });
351
+ this.logger.debug('received credential', credential.credentials.map((c) => c instanceof core_2.Mdoc ? { issuerSignedNamespaces: c.issuerSignedNamespaces, base64Url: c.base64Url } : c));
352
+ receivedCredentials.push(credential);
316
353
  }
317
354
  return {
318
355
  credentials: receivedCredentials,
@@ -332,82 +369,178 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
332
369
  * of possession.
333
370
  */
334
371
  async getCredentialRequestOptions(agentContext, options) {
335
- const { signatureAlgorithms, supportedDidMethods, supportsAllDidMethods, supportsJwk } = this.getProofOfPossessionRequirements(agentContext, {
372
+ const dids = agentContext.resolve(core_1.DidsApi);
373
+ const { allowedProofOfPossesionAlgorithms, offeredCredential } = options;
374
+ const { configuration, id: configurationId } = offeredCredential;
375
+ const supportedJwaSignatureAlgorithms = (0, utils_1.getSupportedJwaSignatureAlgorithms)(agentContext);
376
+ const possibleProofOfPossessionSignatureAlgorithms = allowedProofOfPossesionAlgorithms
377
+ ? allowedProofOfPossesionAlgorithms.filter((algorithm) => supportedJwaSignatureAlgorithms.includes(algorithm))
378
+ : supportedJwaSignatureAlgorithms;
379
+ if (possibleProofOfPossessionSignatureAlgorithms.length === 0) {
380
+ throw new core_2.CredoError([
381
+ 'No possible proof of possession signature algorithm found.',
382
+ `Signature algorithms supported by the Agent '${supportedJwaSignatureAlgorithms.join(', ')}'`,
383
+ `Allowed Signature algorithms '${allowedProofOfPossesionAlgorithms?.join(', ')}'`,
384
+ ].join('\n'));
385
+ }
386
+ const { proofTypes, supportedDidMethods, supportsAllDidMethods, supportsJwk } = this.getProofOfPossessionRequirements(agentContext, {
336
387
  credentialToRequest: options.offeredCredential,
337
- possibleProofOfPossessionSignatureAlgorithms: options.possibleProofOfPossessionSignatureAlgorithms,
338
- });
339
- const JwkClasses = signatureAlgorithms.map((signatureAlgorithm) => {
340
- const JwkClass = (0, core_1.getJwkClassFromJwaSignatureAlgorithm)(signatureAlgorithm);
341
- if (!JwkClass) {
342
- throw new core_1.CredoError(`Could not determine JWK key type of the JWA signature algorithm '${signatureAlgorithm}'`);
343
- }
344
- return JwkClass;
388
+ metadata: options.metadata,
389
+ possibleProofOfPossessionSignatureAlgorithms,
345
390
  });
346
- const keyTypes = JwkClasses.map((JwkClass) => JwkClass.keyType);
347
- const supportedVerificationMethods = keyTypes.flatMap((keyType) => (0, core_1.getSupportedVerificationMethodTypesFromKeyType)(keyType));
348
- const format = options.offeredCredential.configuration.format;
391
+ const format = configuration.format;
349
392
  const supportsAnyMethod = supportedDidMethods !== undefined || supportsAllDidMethods || supportsJwk;
393
+ const issuerMaxBatchSize = options.metadata.credentialIssuer.batch_credential_issuance?.batch_size ?? 1;
350
394
  // Now we need to determine how the credential will be bound to us
351
395
  const credentialBinding = await options.credentialBindingResolver({
352
396
  agentContext,
353
397
  credentialFormat: format,
354
- signatureAlgorithms,
355
- supportedVerificationMethods,
356
- keyTypes: JwkClasses.map((JwkClass) => JwkClass.keyType),
357
- credentialConfigurationId: options.offeredCredential.id,
398
+ credentialConfigurationId: configurationId,
399
+ credentialConfiguration: configuration,
400
+ metadata: options.metadata,
401
+ issuerMaxBatchSize,
402
+ proofTypes,
358
403
  supportsAllDidMethods,
359
404
  supportedDidMethods,
360
405
  supportsJwk,
361
406
  });
362
- let jwk;
407
+ const client = this.getClient(agentContext);
363
408
  // Make sure the issuer of proof of possession is valid according to openid issuer metadata
364
409
  if (credentialBinding.method === 'did') {
365
- // Test binding method
410
+ if (!proofTypes.jwt) {
411
+ throw new core_2.CredoError(`JWT proof type is not supported for configuration '${configurationId}', which is required for did based credential binding.`);
412
+ }
413
+ if (proofTypes.jwt.keyAttestationsRequired) {
414
+ throw new core_2.CredoError(`Credential binding returned list of DID urls, but credential configuration '${configurationId}' requires key attestations. Key attestations and DIDs are not compatible.`);
415
+ }
416
+ if (credentialBinding.didUrls.length > issuerMaxBatchSize) {
417
+ throw new core_2.CredoError(`Issuer supports issuing a batch of maximum ${issuerMaxBatchSize} credential(s). Binding resolver returned ${credentialBinding.didUrls.length} DID urls. Make sure the returned value does not exceed the max batch issuance.`);
418
+ }
419
+ if (credentialBinding.didUrls.length === 0) {
420
+ throw new core_2.CredoError('Credential binding with method did returned empty didUrls list');
421
+ }
422
+ const firstDid = (0, core_2.parseDid)(credentialBinding.didUrls[0]);
423
+ if (!credentialBinding.didUrls.every((didUrl) => (0, core_2.parseDid)(didUrl).method === firstDid.method)) {
424
+ throw new core_2.CredoError('Expected all did urls for binding method did to use the same did method');
425
+ }
366
426
  if (!supportsAllDidMethods &&
367
427
  // If supportedDidMethods is undefined, it means the issuer didn't include the binding methods in the metadata
368
428
  // The user can still select a verification method, but we can't validate it
369
429
  supportedDidMethods !== undefined &&
370
- !supportedDidMethods.find((supportedDidMethod) => credentialBinding.didUrl.startsWith(supportedDidMethod) && supportsAnyMethod)) {
371
- const { method } = (0, core_1.parseDid)(credentialBinding.didUrl);
430
+ !supportedDidMethods.find((supportedDidMethod) => firstDid.did.startsWith(supportedDidMethod) && supportsAnyMethod)) {
431
+ // Test binding method
372
432
  const supportedDidMethodsString = supportedDidMethods.join(', ');
373
- throw new core_1.CredoError(`Resolved credential binding for proof of possession uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'`);
433
+ throw new core_2.CredoError(`Resolved credential binding for proof of possession uses did method '${firstDid.method}', but issuer only supports '${supportedDidMethodsString}'`);
374
434
  }
375
- const key = await (0, utils_1.getKeyFromDid)(agentContext, credentialBinding.didUrl);
376
- jwk = (0, core_1.getJwkFromKey)(key);
377
- if (!keyTypes.includes(key.keyType)) {
378
- throw new core_1.CredoError(`Credential binding returned did url that points to key with type '${key.keyType}', but one of '${keyTypes.join(', ')}' was expected`);
435
+ const { publicJwk: firstKey } = await dids.resolveVerificationMethodFromCreatedDidRecord(firstDid.didUrl);
436
+ const algorithm = proofTypes.jwt.supportedSignatureAlgorithms.find((algorithm) => firstKey.supportedSignatureAlgorithms.includes(algorithm));
437
+ if (!algorithm) {
438
+ throw new core_2.CredoError(`Credential binding returned did url that points to key '${firstKey.jwkTypehumanDescription}' that supports signature algorithms ${firstKey.supportedSignatureAlgorithms.join(', ')}, but one of '${proofTypes.jwt.supportedSignatureAlgorithms.join(', ')}' was expected`);
379
439
  }
440
+ // This will/should leverage the caching, so it's ok to resolve the did here
441
+ const keys = await Promise.all(credentialBinding.didUrls.map(async (didUrl, index) => index === 0
442
+ ? // We already fetched the first did
443
+ { jwk: firstKey, didUrl: firstDid.didUrl }
444
+ : { jwk: (await dids.resolveVerificationMethodFromCreatedDidRecord(didUrl)).publicJwk, didUrl }));
445
+ if (!keys.every((key) => core_2.Kms.assymetricJwkKeyTypeMatches(key.jwk.toJson(), firstKey.toJson()))) {
446
+ throw new core_2.CredoError('Expected all did urls to point to the same key type');
447
+ }
448
+ return {
449
+ jwt: await Promise.all(keys.map((key) => client
450
+ .createCredentialRequestJwtProof({
451
+ credentialConfigurationId: configurationId,
452
+ issuerMetadata: options.metadata,
453
+ signer: {
454
+ method: 'did',
455
+ didUrl: key.didUrl,
456
+ alg: algorithm,
457
+ kid: key.jwk.keyId,
458
+ },
459
+ nonce: options.cNonce,
460
+ clientId: options.clientId,
461
+ })
462
+ .then(({ jwt }) => jwt))),
463
+ };
380
464
  }
381
- else if (credentialBinding.method === 'jwk') {
465
+ if (credentialBinding.method === 'jwk') {
382
466
  if (!supportsJwk && supportsAnyMethod) {
383
- throw new core_1.CredoError(`Resolved credential binding for proof of possession uses jwk, but openid issuer does not support 'jwk' or 'cose_key' cryptographic binding method`);
467
+ throw new core_2.CredoError(`Resolved credential binding for proof of possession uses jwk, but openid issuer does not support 'jwk' or 'cose_key' cryptographic binding method`);
384
468
  }
385
- jwk = credentialBinding.jwk;
386
- if (!keyTypes.includes(credentialBinding.jwk.key.keyType)) {
387
- throw new core_1.CredoError(`Credential binding returned jwk with key with type '${credentialBinding.jwk.key.keyType}', but one of '${keyTypes.join(', ')}' was expected`);
469
+ if (!proofTypes.jwt) {
470
+ throw new core_2.CredoError(`JWT proof type is not supported for configuration '${configurationId}', which is required for jwk based credential binding.`);
388
471
  }
389
- }
390
- else {
391
- // @ts-expect-error currently if/else if exhaustive, but once we add new option it will give ts error
392
- throw new core_1.CredoError(`Unsupported credential binding method ${credentialBinding.method}`);
393
- }
394
- const alg = jwk.supportedSignatureAlgorithms.find((alg) => signatureAlgorithms.includes(alg));
395
- if (!alg) {
396
- // Should not happen, to make ts happy
397
- throw new core_1.CredoError(`Unable to determine alg for key type ${jwk.keyType}`);
398
- }
399
- const jwtSigner = credentialBinding.method === 'did'
400
- ? {
401
- method: credentialBinding.method,
402
- didUrl: credentialBinding.didUrl,
403
- alg,
472
+ if (proofTypes.jwt.keyAttestationsRequired) {
473
+ throw new core_2.CredoError(`Credential binding returned list of JWK keys, but credential configuration '${configurationId}' requires key attestations. Return a key attestation with binding method 'attestation'.`);
404
474
  }
405
- : {
406
- method: 'jwk',
407
- publicJwk: credentialBinding.jwk.toJson(),
408
- alg,
475
+ if (credentialBinding.keys.length > issuerMaxBatchSize) {
476
+ throw new core_2.CredoError(`Issuer supports issuing a batch of maximum ${issuerMaxBatchSize} credential(s). Binding resolver returned ${credentialBinding.keys.length} keys. Make sure the returned value does not exceed the max batch issuance.`);
477
+ }
478
+ if (credentialBinding.keys.length === 0) {
479
+ throw new core_2.CredoError('Credential binding with method jwk returned empty keys list');
480
+ }
481
+ const firstJwk = credentialBinding.keys[0];
482
+ if (!credentialBinding.keys.every((key) => core_2.Kms.assymetricJwkKeyTypeMatches(key.toJson(), firstJwk.toJson()))) {
483
+ throw new core_2.CredoError('Expected all keys for binding method jwk to use the same key type');
484
+ }
485
+ const algorithm = proofTypes.jwt.supportedSignatureAlgorithms.find((algorithm) => firstJwk.supportedSignatureAlgorithms.includes(algorithm));
486
+ if (!algorithm) {
487
+ throw new core_2.CredoError(`Credential binding returned jwk that points to key '${firstJwk.jwkTypehumanDescription}' that supports signature algorithms ${firstJwk.supportedSignatureAlgorithms.join(', ')}, but one of '${proofTypes.jwt.supportedSignatureAlgorithms.join(', ')}' was expected`);
488
+ }
489
+ return {
490
+ jwt: await Promise.all(credentialBinding.keys.map((jwk) => client
491
+ .createCredentialRequestJwtProof({
492
+ credentialConfigurationId: configurationId,
493
+ issuerMetadata: options.metadata,
494
+ signer: {
495
+ method: 'jwk',
496
+ publicJwk: jwk.toJson(),
497
+ alg: algorithm,
498
+ },
499
+ nonce: options.cNonce,
500
+ clientId: options.clientId,
501
+ })
502
+ .then(({ jwt }) => jwt))),
409
503
  };
410
- return { credentialBinding, signatureAlgorithm: alg, jwtSigner };
504
+ }
505
+ if (credentialBinding.method === 'attestation') {
506
+ const { payload } = (0, openid4vci_1.parseKeyAttestationJwt)({ keyAttestationJwt: credentialBinding.keyAttestationJwt });
507
+ // TODO: check client_id matches in payload
508
+ if (payload.attested_keys.length > issuerMaxBatchSize) {
509
+ throw new core_2.CredoError(`Issuer supports issuing a batch of maximum ${issuerMaxBatchSize} credential(s). Binding resolver returned key attestation with ${payload.attested_keys.length} attested keys. Make sure the returned value does not exceed the max batch issuance.`);
510
+ }
511
+ // TODO: check nonce matches cNonce
512
+ if (proofTypes.attestation && payload.nonce) {
513
+ // If attestation is supported and the attestation contains a nonce, we can use the attestation directly
514
+ return {
515
+ attestation: [credentialBinding.keyAttestationJwt],
516
+ };
517
+ }
518
+ if (proofTypes.jwt) {
519
+ const jwk = core_2.Kms.PublicJwk.fromUnknown(payload.attested_keys[0]);
520
+ return {
521
+ jwt: [
522
+ await client
523
+ .createCredentialRequestJwtProof({
524
+ credentialConfigurationId: configurationId,
525
+ issuerMetadata: options.metadata,
526
+ signer: {
527
+ method: 'jwk',
528
+ publicJwk: payload.attested_keys[0],
529
+ // TODO: we should probably use the 'alg' from the jwk
530
+ alg: jwk.supportedSignatureAlgorithms[0],
531
+ },
532
+ keyAttestationJwt: credentialBinding.keyAttestationJwt,
533
+ nonce: options.cNonce,
534
+ clientId: options.clientId,
535
+ })
536
+ .then(({ jwt }) => jwt),
537
+ ],
538
+ };
539
+ }
540
+ throw new core_2.CredoError(`Unable to create credential request proofs. Configuration supports 'attestation' proof type, but attestation did not contain a 'nonce' value`);
541
+ }
542
+ // @ts-expect-error currently if/else if exhaustive, but once we add new option it will give ts error
543
+ throw new core_2.CredoError(`Unsupported credential binding method ${credentialBinding.method}`);
411
544
  }
412
545
  /**
413
546
  * Get the requirements for creating the proof of possession. Based on the allowed
@@ -416,53 +549,87 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
416
549
  * algorithm to use, based on the order of preference.
417
550
  */
418
551
  getProofOfPossessionRequirements(agentContext, options) {
419
- const { credentialToRequest } = options;
420
- if (!OpenId4VciHolderServiceOptions_1.openId4VciSupportedCredentialFormats.includes(credentialToRequest.configuration.format)) {
421
- throw new core_1.CredoError([
552
+ const { credentialToRequest, possibleProofOfPossessionSignatureAlgorithms, metadata } = options;
553
+ const { configuration, id: configurationId } = credentialToRequest;
554
+ if (!OpenId4VciHolderServiceOptions_1.openId4VciSupportedCredentialFormats.includes(configuration.format)) {
555
+ throw new core_2.CredoError([
422
556
  `Requested credential with format '${credentialToRequest.configuration.format}',`,
423
557
  `for the credential with id '${credentialToRequest.id},`,
424
558
  `but the wallet only supports the following formats '${OpenId4VciHolderServiceOptions_1.openId4VciSupportedCredentialFormats.join(', ')}'`,
425
559
  ].join('\n'));
426
560
  }
427
561
  // For each of the supported algs, find the key types, then find the proof types
428
- const signatureSuiteRegistry = agentContext.dependencyManager.resolve(core_1.SignatureSuiteRegistry);
429
- let signatureAlgorithms = [];
430
- if (credentialToRequest.configuration.proof_types_supported) {
431
- if (!credentialToRequest.configuration.proof_types_supported.jwt) {
432
- throw new core_1.CredoError(`Unsupported proof type(s) ${Object.keys(credentialToRequest.configuration.proof_types_supported).join(', ')}. Supported proof type(s) are: jwt`);
562
+ const signatureSuiteRegistry = agentContext.dependencyManager.resolve(core_2.SignatureSuiteRegistry);
563
+ let proofTypesSupported = configuration.proof_types_supported;
564
+ if (!proofTypesSupported) {
565
+ // For draft above 11 we do not allow no proof_type (we do not support no key binding for now)
566
+ if (metadata.originalDraftVersion !== openid4vci_2.Openid4vciDraftVersion.Draft11) {
567
+ throw new core_2.CredoError(`Credential configuration '${configurationId}' does not specifcy proof_types_supported. Credentials not bound to keys are not supported at the moment`);
433
568
  }
569
+ // For draft 11 we fall back to jwt proof type
570
+ proofTypesSupported = {
571
+ jwt: {
572
+ proof_signing_alg_values_supported: possibleProofOfPossessionSignatureAlgorithms,
573
+ },
574
+ };
434
575
  }
435
- const proofSigningAlgsSupported = credentialToRequest.configuration.proof_types_supported?.jwt?.proof_signing_alg_values_supported;
436
- // If undefined, it means the issuer didn't include the cryptographic suites in the metadata
437
- // We just guess that the first one is supported
438
- if (proofSigningAlgsSupported === undefined) {
439
- signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms;
440
- }
441
- else {
442
- switch (credentialToRequest.configuration.format) {
443
- case shared_1.OpenId4VciCredentialFormatProfile.JwtVcJson:
444
- case shared_1.OpenId4VciCredentialFormatProfile.JwtVcJsonLd:
445
- case shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc:
446
- case shared_1.OpenId4VciCredentialFormatProfile.MsoMdoc:
447
- signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms.filter((signatureAlgorithm) => proofSigningAlgsSupported.includes(signatureAlgorithm));
448
- break;
449
- case shared_1.OpenId4VciCredentialFormatProfile.LdpVc:
450
- signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms.filter((signatureAlgorithm) => {
451
- const JwkClass = (0, core_1.getJwkClassFromJwaSignatureAlgorithm)(signatureAlgorithm);
452
- if (!JwkClass)
453
- return false;
454
- const matchingSuite = signatureSuiteRegistry.getAllByKeyType(JwkClass.keyType);
455
- if (matchingSuite.length === 0)
456
- return false;
457
- return proofSigningAlgsSupported.includes(matchingSuite[0].proofType);
458
- });
459
- break;
460
- default:
461
- throw new core_1.CredoError('Unsupported credential format.');
576
+ const proofTypes = {
577
+ jwt: undefined,
578
+ attestation: undefined,
579
+ };
580
+ for (const [proofType, proofTypeConfig] of Object.entries(proofTypesSupported)) {
581
+ if (proofType !== 'jwt' && proofType !== 'attestation')
582
+ continue;
583
+ let signatureAlgorithms = [];
584
+ const proofSigningAlgsSupported = proofTypeConfig?.proof_signing_alg_values_supported;
585
+ if (proofSigningAlgsSupported === undefined) {
586
+ // If undefined, it means the issuer didn't include the cryptographic suites in the metadata
587
+ // We just guess that the first one is supported
588
+ signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms;
589
+ }
590
+ else {
591
+ switch (credentialToRequest.configuration.format) {
592
+ case shared_1.OpenId4VciCredentialFormatProfile.JwtVcJson:
593
+ case shared_1.OpenId4VciCredentialFormatProfile.JwtVcJsonLd:
594
+ case shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc:
595
+ case shared_1.OpenId4VciCredentialFormatProfile.SdJwtDc:
596
+ case shared_1.OpenId4VciCredentialFormatProfile.MsoMdoc:
597
+ signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms.filter((signatureAlgorithm) => proofSigningAlgsSupported.includes(signatureAlgorithm));
598
+ break;
599
+ // FIXME: this is wrong, as the proof type is separate from the credential signing alg
600
+ // But there might be some draft 11 logic that depends on this, can be removed soon
601
+ case shared_1.OpenId4VciCredentialFormatProfile.LdpVc:
602
+ signatureAlgorithms = options.possibleProofOfPossessionSignatureAlgorithms.filter((signatureAlgorithm) => {
603
+ try {
604
+ const jwkClass = core_2.Kms.PublicJwk.supportedPublicJwkClassForSignatureAlgorithm(signatureAlgorithm);
605
+ const matchingSuites = signatureSuiteRegistry.getAllByPublicJwkType(jwkClass);
606
+ if (matchingSuites.length === 0)
607
+ return false;
608
+ return proofSigningAlgsSupported.includes(matchingSuites[0].proofType);
609
+ }
610
+ catch {
611
+ return false;
612
+ }
613
+ });
614
+ break;
615
+ default:
616
+ throw new core_2.CredoError('Unsupported credential format.');
617
+ }
462
618
  }
619
+ proofTypes[proofType] = {
620
+ supportedSignatureAlgorithms: signatureAlgorithms,
621
+ keyAttestationsRequired: proofTypeConfig.key_attestations_required
622
+ ? {
623
+ keyStorage: proofTypeConfig.key_attestations_required.key_storage,
624
+ userAuthentication: proofTypeConfig.key_attestations_required.user_authentication,
625
+ }
626
+ : undefined,
627
+ };
463
628
  }
464
- if (signatureAlgorithms.length === 0) {
465
- throw new core_1.CredoError(`Could not establish signature algorithm for format ${credentialToRequest.configuration.format} and id ${credentialToRequest.id}. Server supported signature algorithms are '${proofSigningAlgsSupported?.join(', ') ?? 'Not defined'}', available are '${options.possibleProofOfPossessionSignatureAlgorithms.join(', ')}'`);
629
+ const { jwt, attestation } = proofTypes;
630
+ if (!jwt && !attestation) {
631
+ const supported = Object.keys(proofTypesSupported).join(', ');
632
+ throw new core_2.CredoError(`Unsupported proof type(s) ${supported}. Supported proof type(s) are: jwt, attestation`);
466
633
  }
467
634
  const issuerSupportedBindingMethods = credentialToRequest.configuration.cryptographic_binding_methods_supported;
468
635
  const supportsAllDidMethods = issuerSupportedBindingMethods?.includes('did') ?? false;
@@ -471,26 +638,26 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
471
638
  const supportsCoseKey = issuerSupportedBindingMethods?.includes('cose_key') ?? false;
472
639
  const supportsJwk = issuerSupportedBindingMethods?.includes('jwk') || supportsCoseKey;
473
640
  return {
474
- signatureAlgorithms,
641
+ proofTypes,
475
642
  supportedDidMethods,
476
643
  supportsAllDidMethods,
477
644
  supportsJwk,
478
645
  };
479
646
  }
480
647
  async handleCredentialResponse(agentContext, credentialResponse, options) {
481
- const { verifyCredentialStatus, credentialConfigurationId } = options;
648
+ const { verifyCredentialStatus, credentialConfigurationId, credentialConfiguration } = options;
482
649
  this.logger.debug('Credential response', credentialResponse);
483
650
  const credentials = credentialResponse.credentials ?? (credentialResponse.credential ? [credentialResponse.credential] : undefined);
484
651
  if (!credentials) {
485
- throw new core_1.CredoError(`Credential response returned neither 'credentials' nor 'credential' parameter.`);
652
+ throw new core_2.CredoError(`Credential response returned neither 'credentials' nor 'credential' parameter.`);
486
653
  }
487
654
  const notificationId = credentialResponse.notification_id;
488
655
  const format = options.format;
489
- if (format === shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc) {
656
+ if (format === shared_1.OpenId4VciCredentialFormatProfile.SdJwtVc || format === shared_1.OpenId4VciCredentialFormatProfile.SdJwtDc) {
490
657
  if (!credentials.every((c) => typeof c === 'string')) {
491
- throw new core_1.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
658
+ throw new core_2.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
492
659
  }
493
- const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
660
+ const sdJwtVcApi = agentContext.dependencyManager.resolve(core_2.SdJwtVcApi);
494
661
  const verificationResults = await Promise.all(credentials.map((compactSdJwtVc, index) => sdJwtVcApi.verify({
495
662
  compactSdJwtVc,
496
663
  // Only load and verify it for the first instance
@@ -498,21 +665,22 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
498
665
  })));
499
666
  if (!verificationResults.every((result) => result.isValid)) {
500
667
  agentContext.config.logger.error('Failed to validate credential(s)', { verificationResults });
501
- throw new core_1.CredoError(`Failed to validate sd-jwt-vc credentials. Results = ${JSON.stringify(verificationResults)}`);
668
+ throw new core_2.CredoError(`Failed to validate sd-jwt-vc credentials. Results = ${JSON.stringify(verificationResults)}`);
502
669
  }
503
670
  return {
504
671
  credentials: verificationResults.map((result) => result.sdJwtVc),
505
672
  notificationId,
506
673
  credentialConfigurationId,
674
+ credentialConfiguration,
507
675
  };
508
676
  }
509
677
  if (options.format === shared_1.OpenId4VciCredentialFormatProfile.JwtVcJson ||
510
678
  options.format === shared_1.OpenId4VciCredentialFormatProfile.JwtVcJsonLd) {
511
679
  if (!credentials.every((c) => typeof c === 'string')) {
512
- throw new core_1.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
680
+ throw new core_2.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
513
681
  }
514
682
  const result = await Promise.all(credentials.map(async (c) => {
515
- const credential = core_1.W3cJwtVerifiableCredential.fromSerializedJwt(c);
683
+ const credential = core_2.W3cJwtVerifiableCredential.fromSerializedJwt(c);
516
684
  const result = await this.w3cCredentialService.verifyCredential(agentContext, {
517
685
  credential,
518
686
  verifyCredentialStatus,
@@ -521,19 +689,24 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
521
689
  }));
522
690
  if (!result.every((c) => c.result.isValid)) {
523
691
  agentContext.config.logger.error('Failed to validate credentials', { result });
524
- throw new core_1.CredoError(`Failed to validate credential, error = ${result
692
+ throw new core_2.CredoError(`Failed to validate credential, error = ${result
525
693
  .map((e) => e.result.error?.message)
526
694
  .filter(Boolean)
527
695
  .join(', ')}`);
528
696
  }
529
- return { credentials: result.map((r) => r.credential), notificationId, credentialConfigurationId };
697
+ return {
698
+ credentials: result.map((r) => r.credential),
699
+ notificationId,
700
+ credentialConfigurationId,
701
+ credentialConfiguration,
702
+ };
530
703
  }
531
704
  if (format === shared_1.OpenId4VciCredentialFormatProfile.LdpVc) {
532
705
  if (!credentials.every((c) => typeof c === 'object')) {
533
- throw new core_1.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are an object. ${JSON.stringify(credentials)}`);
706
+ throw new core_2.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are an object. ${JSON.stringify(credentials)}`);
534
707
  }
535
708
  const result = await Promise.all(credentials.map(async (c) => {
536
- const credential = core_1.W3cJsonLdVerifiableCredential.fromJson(c);
709
+ const credential = core_2.W3cJsonLdVerifiableCredential.fromJson(c);
537
710
  const result = await this.w3cCredentialService.verifyCredential(agentContext, {
538
711
  credential,
539
712
  verifyCredentialStatus,
@@ -542,20 +715,25 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
542
715
  }));
543
716
  if (!result.every((c) => c.result.isValid)) {
544
717
  agentContext.config.logger.error('Failed to validate credentials', { result });
545
- throw new core_1.CredoError(`Failed to validate credential, error = ${result
718
+ throw new core_2.CredoError(`Failed to validate credential, error = ${result
546
719
  .map((e) => e.result.error?.message)
547
720
  .filter(Boolean)
548
721
  .join(', ')}`);
549
722
  }
550
- return { credentials: result.map((r) => r.credential), notificationId, credentialConfigurationId };
723
+ return {
724
+ credentials: result.map((r) => r.credential),
725
+ notificationId,
726
+ credentialConfigurationId,
727
+ credentialConfiguration,
728
+ };
551
729
  }
552
730
  if (format === shared_1.OpenId4VciCredentialFormatProfile.MsoMdoc) {
553
731
  if (!credentials.every((c) => typeof c === 'string')) {
554
- throw new core_1.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
732
+ throw new core_2.CredoError(`Received credential(s) of format ${format}, but not all credential(s) are a string. ${JSON.stringify(credentials)}`);
555
733
  }
556
- const mdocApi = agentContext.dependencyManager.resolve(core_1.MdocApi);
734
+ const mdocApi = agentContext.dependencyManager.resolve(core_2.MdocApi);
557
735
  const result = await Promise.all(credentials.map(async (credential) => {
558
- const mdoc = core_1.Mdoc.fromBase64Url(credential);
736
+ const mdoc = core_2.Mdoc.fromBase64Url(credential);
559
737
  const result = await mdocApi.verify(mdoc, {});
560
738
  return {
561
739
  result,
@@ -564,18 +742,62 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
564
742
  }));
565
743
  if (!result.every((r) => r.result.isValid)) {
566
744
  agentContext.config.logger.error('Failed to validate credentials', { result });
567
- throw new core_1.CredoError(`Failed to validate mdoc credential(s). \n - ${result
745
+ throw new core_2.CredoError(`Failed to validate mdoc credential(s). \n - ${result
568
746
  .map((r, i) => (r.result.isValid ? undefined : `(${i}) ${r.result.error}`))
569
747
  .filter(Boolean)
570
748
  .join('\n - ')}`);
571
749
  }
572
- return { credentials: result.map((c) => c.mdoc), notificationId, credentialConfigurationId };
750
+ return {
751
+ credentials: result.map((c) => c.mdoc),
752
+ notificationId,
753
+ credentialConfigurationId,
754
+ credentialConfiguration,
755
+ };
573
756
  }
574
- throw new core_1.CredoError(`Unsupported credential format ${options.format}`);
757
+ throw new core_2.CredoError(`Unsupported credential format ${options.format}`);
575
758
  }
576
- getClient(agentContext) {
577
- return new openid4vci_1.Openid4vciClient({
578
- callbacks: (0, callbacks_1.getOid4vcCallbacks)(agentContext),
759
+ getClient(agentContext, { clientAttestation, clientId } = {}) {
760
+ const callbacks = (0, callbacks_1.getOid4vcCallbacks)(agentContext);
761
+ return new openid4vci_2.Openid4vciClient({
762
+ callbacks: {
763
+ ...callbacks,
764
+ clientAuthentication: (options) => {
765
+ const { authorizationServerMetadata, url, body } = options;
766
+ const oauth2Client = this.getOauth2Client(agentContext);
767
+ const clientAttestationSupported = oauth2Client.isClientAttestationSupported({
768
+ authorizationServerMetadata,
769
+ });
770
+ // Client attestations
771
+ if (clientAttestation && clientAttestationSupported) {
772
+ return (0, oauth2_1.clientAuthenticationClientAttestationJwt)({
773
+ clientAttestationJwt: clientAttestation,
774
+ callbacks,
775
+ })(options);
776
+ }
777
+ // Pre auth flow
778
+ if (url === authorizationServerMetadata.token_endpoint &&
779
+ authorizationServerMetadata['pre-authorized_grant_anonymous_access_supported'] &&
780
+ body.grant_type === oauth2_1.preAuthorizedCodeGrantIdentifier) {
781
+ return (0, oauth2_1.clientAuthenticationAnonymous)()(options);
782
+ }
783
+ // Just a client id (no auth)
784
+ if (clientId) {
785
+ return (0, oauth2_1.clientAuthenticationNone)({ clientId })(options);
786
+ }
787
+ // NOTE: we fall back to anonymous authentication for pre-auth for now, as there's quite some
788
+ // issuers that do not have pre-authorized_grant_anonymous_access_supported defined
789
+ if (url === authorizationServerMetadata.token_endpoint &&
790
+ body.grant_type === oauth2_1.preAuthorizedCodeGrantIdentifier) {
791
+ return (0, oauth2_1.clientAuthenticationAnonymous)()(options);
792
+ }
793
+ // TODO: We should still look at auth_methods_supported
794
+ // If there is an auth session for the auth challenge endpoint, we don't have to include the client_id
795
+ if (url === authorizationServerMetadata.authorization_challenge_endpoint && body.auth_session) {
796
+ return (0, oauth2_1.clientAuthenticationAnonymous)()(options);
797
+ }
798
+ throw new core_2.CredoError('Unable to perform client authentication.');
799
+ },
800
+ },
579
801
  });
580
802
  }
581
803
  getOauth2Client(agentContext) {
@@ -586,8 +808,8 @@ let OpenId4VciHolderService = class OpenId4VciHolderService {
586
808
  };
587
809
  exports.OpenId4VciHolderService = OpenId4VciHolderService;
588
810
  exports.OpenId4VciHolderService = OpenId4VciHolderService = __decorate([
589
- (0, core_1.injectable)(),
590
- __param(0, (0, core_1.inject)(core_1.InjectionSymbols.Logger)),
591
- __metadata("design:paramtypes", [Object, core_1.W3cCredentialService])
811
+ (0, core_2.injectable)(),
812
+ __param(0, (0, core_2.inject)(core_2.InjectionSymbols.Logger)),
813
+ __metadata("design:paramtypes", [Object, core_2.W3cCredentialService])
592
814
  ], OpenId4VciHolderService);
593
815
  //# sourceMappingURL=OpenId4VciHolderService.js.map