@credo-ts/openid4vc 0.6.0-pr-2195-20250226092707 → 0.6.0-pr-2209-20250321171013

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 (99) hide show
  1. package/build/openid4vc-holder/OpenId4VcHolderApi.d.ts +14 -119
  2. package/build/openid4vc-holder/OpenId4VcHolderApi.js +9 -24
  3. package/build/openid4vc-holder/OpenId4VcHolderApi.js.map +1 -1
  4. package/build/openid4vc-holder/OpenId4VcHolderModule.js +1 -1
  5. package/build/openid4vc-holder/OpenId4VcHolderModule.js.map +1 -1
  6. package/build/openid4vc-holder/OpenId4VciHolderService.d.ts +8 -7
  7. package/build/openid4vc-holder/OpenId4VciHolderService.js +21 -19
  8. package/build/openid4vc-holder/OpenId4VciHolderService.js.map +1 -1
  9. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.d.ts +4 -4
  10. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.js +2 -2
  11. package/build/openid4vc-holder/OpenId4VciHolderServiceOptions.js.map +1 -1
  12. package/build/openid4vc-holder/OpenId4vcSiopHolderService.d.ts +15 -116
  13. package/build/openid4vc-holder/OpenId4vcSiopHolderService.js +233 -239
  14. package/build/openid4vc-holder/OpenId4vcSiopHolderService.js.map +1 -1
  15. package/build/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.d.ts +9 -25
  16. package/build/openid4vc-issuer/OpenId4VcIssuerApi.d.ts +44 -194
  17. package/build/openid4vc-issuer/OpenId4VcIssuerEvents.d.ts +1 -1
  18. package/build/openid4vc-issuer/OpenId4VcIssuerModule.d.ts +1 -1
  19. package/build/openid4vc-issuer/OpenId4VcIssuerModule.js +1 -1
  20. package/build/openid4vc-issuer/OpenId4VcIssuerModule.js.map +1 -1
  21. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.d.ts +8 -8
  22. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.js +10 -2
  23. package/build/openid4vc-issuer/OpenId4VcIssuerModuleConfig.js.map +1 -1
  24. package/build/openid4vc-issuer/OpenId4VcIssuerService.d.ts +48 -198
  25. package/build/openid4vc-issuer/OpenId4VcIssuerService.js +27 -39
  26. package/build/openid4vc-issuer/OpenId4VcIssuerService.js.map +1 -1
  27. package/build/openid4vc-issuer/OpenId4VcIssuerServiceOptions.d.ts +6 -11
  28. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.d.ts +2 -2
  29. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.js +1 -0
  30. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRecord.js.map +1 -1
  31. package/build/openid4vc-issuer/repository/OpenId4VcIssuanceSessionRepository.d.ts +1 -1
  32. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.d.ts +1 -1
  33. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.js +3 -3
  34. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRecord.js.map +1 -1
  35. package/build/openid4vc-issuer/repository/OpenId4VcIssuerRepository.d.ts +1 -1
  36. package/build/openid4vc-issuer/router/accessTokenEndpoint.d.ts +2 -2
  37. package/build/openid4vc-issuer/router/accessTokenEndpoint.js +1 -1
  38. package/build/openid4vc-issuer/router/accessTokenEndpoint.js.map +1 -1
  39. package/build/openid4vc-issuer/router/authorizationChallengeEndpoint.js +1 -1
  40. package/build/openid4vc-issuer/router/authorizationChallengeEndpoint.js.map +1 -1
  41. package/build/openid4vc-issuer/router/authorizationServerMetadataEndpoint.js +1 -1
  42. package/build/openid4vc-issuer/router/authorizationServerMetadataEndpoint.js.map +1 -1
  43. package/build/openid4vc-issuer/router/credentialEndpoint.d.ts +1 -1
  44. package/build/openid4vc-issuer/router/credentialEndpoint.js +3 -3
  45. package/build/openid4vc-issuer/router/credentialEndpoint.js.map +1 -1
  46. package/build/openid4vc-issuer/router/credentialOfferEndpoint.d.ts +1 -1
  47. package/build/openid4vc-issuer/router/issuerMetadataEndpoint.js +1 -1
  48. package/build/openid4vc-issuer/router/issuerMetadataEndpoint.js.map +1 -1
  49. package/build/openid4vc-issuer/router/jwksEndpoint.d.ts +1 -1
  50. package/build/openid4vc-issuer/router/nonceEndpoint.d.ts +1 -1
  51. package/build/openid4vc-issuer/util/txCode.d.ts +1 -1
  52. package/build/openid4vc-verifier/OpenId4VcSiopVerifierService.d.ts +12 -20
  53. package/build/openid4vc-verifier/OpenId4VcSiopVerifierService.js +325 -571
  54. package/build/openid4vc-verifier/OpenId4VcSiopVerifierService.js.map +1 -1
  55. package/build/openid4vc-verifier/OpenId4VcSiopVerifierServiceOptions.d.ts +20 -36
  56. package/build/openid4vc-verifier/OpenId4VcVerifierApi.d.ts +2 -2
  57. package/build/openid4vc-verifier/OpenId4VcVerifierApi.js +2 -2
  58. package/build/openid4vc-verifier/OpenId4VcVerifierApi.js.map +1 -1
  59. package/build/openid4vc-verifier/OpenId4VcVerifierEvents.d.ts +1 -1
  60. package/build/openid4vc-verifier/OpenId4VcVerifierModule.d.ts +1 -1
  61. package/build/openid4vc-verifier/OpenId4VcVerifierModule.js +4 -1
  62. package/build/openid4vc-verifier/OpenId4VcVerifierModule.js.map +1 -1
  63. package/build/openid4vc-verifier/OpenId4VcVerifierModuleConfig.d.ts +2 -2
  64. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.d.ts +49 -0
  65. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.js +230 -0
  66. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartyEventEmitter.js.map +1 -0
  67. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.d.ts +19 -0
  68. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.js +144 -0
  69. package/build/openid4vc-verifier/repository/OpenId4VcRelyingPartySessionManager.js.map +1 -0
  70. package/build/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.d.ts +11 -20
  71. package/build/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.js +7 -18
  72. package/build/openid4vc-verifier/repository/OpenId4VcVerificationSessionRecord.js.map +1 -1
  73. package/build/openid4vc-verifier/repository/OpenId4VcVerificationSessionRepository.d.ts +1 -1
  74. package/build/openid4vc-verifier/repository/OpenId4VcVerifierRecord.d.ts +1 -1
  75. package/build/openid4vc-verifier/repository/OpenId4VcVerifierRepository.d.ts +1 -1
  76. package/build/openid4vc-verifier/router/authorizationEndpoint.js +103 -5
  77. package/build/openid4vc-verifier/router/authorizationEndpoint.js.map +1 -1
  78. package/build/openid4vc-verifier/router/authorizationRequestEndpoint.js +16 -3
  79. package/build/openid4vc-verifier/router/authorizationRequestEndpoint.js.map +1 -1
  80. package/build/shared/callbacks.d.ts +6 -14
  81. package/build/shared/callbacks.js +14 -102
  82. package/build/shared/callbacks.js.map +1 -1
  83. package/build/shared/issuerMetadataUtils.d.ts +144 -102
  84. package/build/shared/models/OpenId4VcJwtIssuer.d.ts +3 -2
  85. package/build/shared/models/index.d.ts +10 -10
  86. package/build/shared/models/index.js +5 -5
  87. package/build/shared/models/index.js.map +1 -1
  88. package/build/shared/router/context.d.ts +3 -3
  89. package/build/shared/router/context.js +4 -4
  90. package/build/shared/router/context.js.map +1 -1
  91. package/build/shared/router/express.js +1 -2
  92. package/build/shared/router/express.js.map +1 -1
  93. package/build/shared/transform.d.ts +5 -0
  94. package/build/shared/transform.js +69 -0
  95. package/build/shared/transform.js.map +1 -0
  96. package/build/shared/utils.d.ts +8 -6
  97. package/build/shared/utils.js +105 -34
  98. package/build/shared/utils.js.map +1 -1
  99. package/package.json +8 -6
@@ -14,15 +14,17 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.OpenId4VcSiopVerifierService = void 0;
16
16
  const core_1 = require("@credo-ts/core");
17
- const oauth2_1 = require("@openid4vc/oauth2");
18
- const openid4vp_1 = require("@openid4vc/openid4vp");
19
- const callbacks_1 = require("../shared/callbacks");
17
+ const did_auth_siop_1 = require("@sphereon/did-auth-siop");
18
+ const OpenID4VP_1 = require("@sphereon/did-auth-siop/dist/authorization-response/OpenID4VP");
19
+ const rxjs_1 = require("rxjs");
20
20
  const router_1 = require("../shared/router");
21
+ const transform_1 = require("../shared/transform");
21
22
  const utils_1 = require("../shared/utils");
22
23
  const OpenId4VcVerificationSessionState_1 = require("./OpenId4VcVerificationSessionState");
23
- const OpenId4VcVerifierEvents_1 = require("./OpenId4VcVerifierEvents");
24
24
  const OpenId4VcVerifierModuleConfig_1 = require("./OpenId4VcVerifierModuleConfig");
25
25
  const repository_1 = require("./repository");
26
+ const OpenId4VcRelyingPartyEventEmitter_1 = require("./repository/OpenId4VcRelyingPartyEventEmitter");
27
+ const OpenId4VcRelyingPartySessionManager_1 = require("./repository/OpenId4VcRelyingPartySessionManager");
26
28
  /**
27
29
  * @internal
28
30
  */
@@ -34,39 +36,27 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
34
36
  this.config = config;
35
37
  this.openId4VcVerificationSessionRepository = openId4VcVerificationSessionRepository;
36
38
  }
37
- getOpenid4vpVerifier(agentContext) {
38
- const callbacks = (0, callbacks_1.getOid4vcCallbacks)(agentContext);
39
- const openid4vpClient = new openid4vp_1.Openid4vpVerifier({ callbacks });
40
- return openid4vpClient;
41
- }
42
39
  async createAuthorizationRequest(agentContext, options) {
43
40
  const nonce = await agentContext.wallet.generateNonce();
44
41
  const state = await agentContext.wallet.generateNonce();
45
- const isDcApiRequest = options.responseMode === 'dc_api' || options.responseMode === 'dc_api.jwt';
46
- const responseMode = options.responseMode ?? 'direct_post.jwt';
47
- // No response url for DC API
42
+ // Correlation id will be the id of the verification session record
43
+ const correlationId = core_1.utils.uuid();
48
44
  let authorizationResponseUrl = (0, core_1.joinUriParts)(this.config.baseUrl, [
49
45
  options.verifier.verifierId,
50
46
  this.config.authorizationEndpoint.endpointPath,
51
47
  ]);
52
- const jwtIssuer = options.requestSigner.method === 'none'
53
- ? undefined
54
- : options.requestSigner.method === 'x5c'
55
- ? await (0, utils_1.requestSignerToJwtIssuer)(agentContext, {
56
- ...options.requestSigner,
57
- issuer: authorizationResponseUrl,
58
- })
59
- : await (0, utils_1.requestSignerToJwtIssuer)(agentContext, options.requestSigner);
48
+ const jwtIssuer = options.requestSigner.method === 'x5c'
49
+ ? await (0, utils_1.openIdTokenIssuerToJwtIssuer)(agentContext, {
50
+ ...options.requestSigner,
51
+ issuer: authorizationResponseUrl,
52
+ })
53
+ : await (0, utils_1.openIdTokenIssuerToJwtIssuer)(agentContext, options.requestSigner);
60
54
  let clientIdScheme;
61
55
  let clientId;
62
- if (!jwtIssuer) {
63
- if (!isDcApiRequest) {
64
- throw new Error("requestSigner method 'none' is only supported for response mode 'dc_api' and 'dc_api.jwt'");
56
+ if (jwtIssuer.method === 'x5c') {
57
+ if (jwtIssuer.issuer !== authorizationResponseUrl) {
58
+ throw new core_1.CredoError(`The jwtIssuer's issuer field must match the verifier's authorizationResponseUrl '${authorizationResponseUrl}'.`);
65
59
  }
66
- clientIdScheme = 'web-origin';
67
- clientId = undefined;
68
- }
69
- else if (jwtIssuer?.method === 'x5c') {
70
60
  const leafCertificate = core_1.X509Service.getLeafCertificate(agentContext, { certificateChain: jwtIssuer.x5c });
71
61
  if (leafCertificate.sanDnsNames.includes((0, core_1.getDomainFromUrl)(jwtIssuer.issuer))) {
72
62
  clientIdScheme = 'x509_san_dns';
@@ -82,354 +72,170 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
82
72
  throw new core_1.CredoError(`With jwtIssuer 'method' 'x5c' the jwtIssuer's 'issuer' field must either match the match a sanDnsName (FQDN) or sanUriName in the leaf x509 chain's leaf certificate.`);
83
73
  }
84
74
  }
85
- else if (jwtIssuer?.method === 'did') {
75
+ else if (jwtIssuer.method === 'did') {
86
76
  clientId = jwtIssuer.didUrl.split('#')[0];
87
77
  clientIdScheme = 'did';
88
78
  }
89
79
  else {
90
80
  throw new core_1.CredoError(`Unsupported jwt issuer method '${options.requestSigner.method}'. Only 'did' and 'x5c' are supported.`);
91
81
  }
92
- // We always use shortened URIs currently
93
- const hostedAuthorizationRequestUri = !isDcApiRequest
94
- ? (0, core_1.joinUriParts)(this.config.baseUrl, [
95
- options.verifier.verifierId,
96
- this.config.authorizationRequestEndpoint.endpointPath,
97
- // It doesn't really matter what the url is, as long as it's unique
98
- core_1.utils.uuid(),
99
- ])
100
- : // No hosted request needed when using DC API
101
- undefined;
102
- const client_id = clientIdScheme === 'did' || clientIdScheme === 'https' ? clientId : `${clientIdScheme}:${clientId}`;
103
- const client_metadata = await this.getClientMetadata(agentContext, {
104
- responseMode,
105
- verifier: options.verifier,
82
+ const relyingParty = await this.getRelyingParty(agentContext, options.verifier, {
83
+ presentationDefinition: options.presentationExchange?.definition,
106
84
  authorizationResponseUrl,
85
+ clientId,
86
+ clientIdScheme,
87
+ responseMode: options.responseMode,
107
88
  });
108
- const requestParamsBase = {
89
+ // We always use shortened URIs currently
90
+ const hostedAuthorizationRequestUri = (0, core_1.joinUriParts)(this.config.baseUrl, [
91
+ options.verifier.verifierId,
92
+ this.config.authorizationRequestEndpoint.endpointPath,
93
+ // It doesn't really matter what the url is, as long as it's unique
94
+ core_1.utils.uuid(),
95
+ ]);
96
+ // This is very unfortunate, but storing state in sphereon's SiOP-OID4VP library
97
+ // is done async, so we can't be certain yet that the verification session record
98
+ // is created already when we have created the authorization request. So we need to
99
+ // wait for a short while before we can be certain that the verification session record
100
+ // is created. To not use arbitrary timeouts, we wait for the specific RecordSavedEvent
101
+ // that is emitted when the verification session record is created.
102
+ const eventEmitter = agentContext.dependencyManager.resolve(core_1.EventEmitter);
103
+ const verificationSessionCreatedPromise = (0, rxjs_1.firstValueFrom)(eventEmitter
104
+ .observable(core_1.RepositoryEventTypes.RecordSaved)
105
+ .pipe((0, rxjs_1.filter)((e) => e.metadata.contextCorrelationId === agentContext.contextCorrelationId), (0, rxjs_1.filter)((e) => e.payload.record.id === correlationId && e.payload.record.verifierId === options.verifier.verifierId), (0, rxjs_1.first)(), (0, rxjs_1.timeout)({
106
+ first: 10000,
107
+ meta: 'OpenId4VcSiopVerifierService.createAuthorizationRequest',
108
+ }), (0, rxjs_1.map)((e) => e.payload.record)));
109
+ const authorizationRequest = await relyingParty.createAuthorizationRequest({
110
+ correlationId,
109
111
  nonce,
110
- presentation_definition: options.presentationExchange?.definition,
111
- dcql_query: options.dcql?.query,
112
- transaction_data: options.transactionData?.map((entry) => core_1.JsonEncoder.toBase64URL(entry)),
113
- response_mode: responseMode,
114
- response_type: 'vp_token',
115
- client_metadata,
116
- };
117
- const authorizationRequestPayload = requestParamsBase.response_mode === 'dc_api.jwt' || requestParamsBase.response_mode === 'dc_api'
118
- ? {
119
- ...requestParamsBase,
120
- // No client_id for unsigned requests
121
- client_id: jwtIssuer ? client_id : undefined,
122
- response_mode: requestParamsBase.response_mode,
123
- expected_origins: options.expectedOrigins,
124
- }
125
- : {
126
- ...requestParamsBase,
127
- client_id: client_id,
128
- state,
129
- response_uri: authorizationResponseUrl,
130
- };
131
- const openid4vpVerifier = this.getOpenid4vpVerifier(agentContext);
132
- const authorizationRequest = await openid4vpVerifier.createOpenId4vpAuthorizationRequest({
133
- jar: jwtIssuer
134
- ? {
135
- jwtSigner: jwtIssuer,
136
- // FIXME: cast can be removed when PR 41 is merged in oid4vc-ts
137
- requestUri: hostedAuthorizationRequestUri,
138
- }
139
- : undefined,
140
- requestParams: authorizationRequestPayload,
141
- });
142
- const verificationSession = new repository_1.OpenId4VcVerificationSessionRecord({
143
- // Only store payload for unsiged requests
144
- authorizationRequestPayload: authorizationRequest.jar ? undefined : authorizationRequestPayload,
145
- authorizationRequestJwt: authorizationRequest.jar?.requestObjectJwt,
146
- authorizationRequestUri: authorizationRequest.jar?.requestUri,
147
- state: OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.RequestCreated,
148
- verifierId: options.verifier.verifierId,
112
+ state,
113
+ requestByReferenceURI: hostedAuthorizationRequestUri,
114
+ jwtIssuer,
149
115
  });
150
- await this.openId4VcVerificationSessionRepository.save(agentContext, verificationSession);
151
- this.emitStateChangedEvent(agentContext, verificationSession, null);
152
- return {
153
- authorizationRequest: authorizationRequest.authRequest,
154
- verificationSession,
155
- authorizationRequestObject: authorizationRequest.authRequestObject,
156
- };
157
- }
158
- getDcqlVerifiedResponse(agentContext, _dcqlQuery, presentation) {
159
- const dcqlService = agentContext.dependencyManager.resolve(core_1.DcqlService);
160
- const dcqlQuery = dcqlService.validateDcqlQuery(_dcqlQuery);
161
- const dcqlPresentationEntries = Object.entries(presentation);
162
- const dcqlPresentation = Object.fromEntries(dcqlPresentationEntries.map((presentation) => {
163
- const [credentialId, vpTokenPresentationParseResult] = presentation;
164
- return [credentialId, this.decodePresentation(agentContext, { vpTokenPresentationParseResult })];
165
- }));
166
- const dcqlPresentationResult = dcqlService.assertValidDcqlPresentation(dcqlPresentation, dcqlQuery);
167
- return {
168
- query: dcqlQuery,
169
- presentation: dcqlPresentation,
170
- presentationResult: dcqlPresentationResult,
171
- };
172
- }
173
- async parseAuthorizationResponse(agentContext, options) {
174
- const { verifierId, responsePayload } = options;
175
- let verificationSession;
176
- let parsedAuthResponse;
177
- let rawResponsePayload = responsePayload;
178
- try {
179
- parsedAuthResponse = await (0, openid4vp_1.parseOpenid4vpAuthorizationResponse)({
180
- responsePayload,
181
- callbacks: {
182
- ...(0, callbacks_1.getOid4vcCallbacks)(agentContext),
183
- getOpenid4vpAuthorizationRequest: async (responsePayload) => {
184
- const { state, nonce } = responsePayload;
185
- rawResponsePayload = responsePayload;
186
- const session = await this.findVerificationSessionForAuthorizationResponse(agentContext, {
187
- authorizationResponseParams: { state, nonce },
188
- verifierId,
189
- });
190
- if (!session) {
191
- agentContext.config.logger.warn(`No verification session found for incoming authorization response for verifier ${verifierId}`);
192
- throw new core_1.CredoError(`No state or nonce provided in authorization response for verifier ${verifierId}`);
193
- }
194
- verificationSession = session;
195
- const authorizationRequest = (0, openid4vp_1.parseOpenid4vpAuthorizationRequestPayload)({
196
- authorizationRequest: verificationSession.request,
197
- });
198
- if (authorizationRequest.type !== 'openid4vp' && authorizationRequest.type !== 'openid4vp_dc_api') {
199
- throw new core_1.CredoError(`Invalid authorization request jwt. Expected 'openid4vp' or 'openid4vp_dc_api' request, received '${authorizationRequest.type}'.`);
200
- }
201
- if (authorizationRequest.params.client_id) {
202
- return {
203
- authorizationRequest: {
204
- ...authorizationRequest.params,
205
- client_id: authorizationRequest.params.client_id,
206
- },
207
- };
208
- }
209
- if (!options.origin) {
210
- throw new core_1.CredoError('Origin must be provided when client id is not set.');
211
- }
212
- return {
213
- authorizationRequest: {
214
- ...authorizationRequest.params,
215
- client_id: authorizationRequest.params.client_id ?? `web-origin:${options.origin}`,
216
- },
217
- };
218
- },
219
- },
220
- });
221
- }
222
- catch (error) {
223
- if (verificationSession?.state === OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.RequestUriRetrieved ||
224
- verificationSession?.state === OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.RequestCreated) {
225
- const parsed = openid4vp_1.zOpenid4vpAuthorizationResponse.safeParse(rawResponsePayload);
226
- verificationSession.authorizationResponsePayload = parsed.success ? parsed.data : undefined;
227
- verificationSession.errorMessage = error.message;
228
- await this.updateState(agentContext, verificationSession, OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.Error);
229
- }
230
- throw error;
116
+ // NOTE: it's not possible to set the uri scheme when using the RP to create an auth request, only lower level
117
+ // functions allow this. So we need to replace the uri scheme manually.
118
+ let authorizationRequestUri = (await authorizationRequest.uri()).encodedUri;
119
+ if (options.presentationExchange && !options.idToken) {
120
+ authorizationRequestUri = authorizationRequestUri.replace('openid://', 'openid4vp://');
231
121
  }
232
- if (parsedAuthResponse.authResponsePayload.presentation_submission &&
233
- typeof parsedAuthResponse.authResponsePayload.presentation_submission === 'string') {
234
- const decoded = decodeURIComponent(parsedAuthResponse.authResponsePayload.presentation_submission);
235
- const parsed = JSON.parse(decoded);
236
- parsedAuthResponse.authResponsePayload.presentation_submission = parsed;
237
- if (parsedAuthResponse.type === 'pex') {
238
- parsedAuthResponse.pex.presentationSubmission = parsed;
239
- }
240
- }
241
- if (!verificationSession) {
242
- throw new core_1.CredoError('Missing verification session, cannot verify authorization response.');
243
- }
244
- // FIXME: export JarmMode from openid4vp
245
- if (parsedAuthResponse.jarm && `${parsedAuthResponse.jarm.type}` !== 'Encrypted') {
246
- throw new oauth2_1.Oauth2ServerErrorResponseError({
247
- error: oauth2_1.Oauth2ErrorCodes.InvalidRequest,
248
- error_description: `Only encrypted JARM responses are supported, received '${parsedAuthResponse.jarm.type}'.`,
249
- });
122
+ else {
123
+ authorizationRequestUri = authorizationRequestUri.replace('openid4vp://', 'openid://');
250
124
  }
125
+ const verificationSession = await verificationSessionCreatedPromise;
251
126
  return {
252
- ...parsedAuthResponse,
127
+ authorizationRequest: authorizationRequestUri,
253
128
  verificationSession,
254
129
  };
255
130
  }
256
131
  async verifyAuthorizationResponse(agentContext, options) {
257
- const openid4vpVerifier = this.getOpenid4vpVerifier(agentContext);
258
- const result = await this.parseAuthorizationResponse(agentContext, {
259
- verifierId: options.verifierId,
260
- responsePayload: options.authorizationResponse,
261
- origin: options.origin,
262
- });
263
- result.verificationSession.assertState([
132
+ // Assert state
133
+ options.verificationSession.assertState([
264
134
  OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.RequestUriRetrieved,
265
135
  OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.RequestCreated,
266
136
  ]);
267
- const authorizationRequest = result.authRequestPayload;
268
- let requestClientId = authorizationRequest.client_id;
269
- const responseUri = 'response_uri' in authorizationRequest ? authorizationRequest.response_uri : undefined;
270
- const transactionData = authorizationRequest.transaction_data
271
- ? openid4vpVerifier.parseTransactionData({ transactionData: authorizationRequest.transaction_data })
272
- : undefined;
273
- const isDcApiRequest = authorizationRequest.response_mode === 'dc_api' || authorizationRequest.response_mode === 'dc_api.jwt';
274
- if (isDcApiRequest) {
275
- if (!options.origin)
276
- throw new core_1.CredoError('origin is required for Digital Credentials API');
277
- if (!requestClientId)
278
- requestClientId = `web-origin:${options.origin}`;
279
- }
280
- else if (!requestClientId) {
281
- throw new core_1.CredoError('Missing required client_id');
282
- }
283
- // validating the id token
284
- // verifying the presentations
285
- // validating the presentations against the presentation definition
286
- // checking the revocation status of the presentations
287
- // checking the nonce of the presentations matches the nonce of the request
288
- let presentationVerificationResults = [];
289
- if (result.type === 'dcql') {
290
- const dcqlPresentationEntries = Object.entries(result.dcql.presentation);
291
- const presentationVerificationPromises = dcqlPresentationEntries.map(async (presentation) => {
292
- const [credentialId, vpTokenPresentationParseResult] = presentation;
293
- return await this.verifyPresentations(agentContext, {
294
- correlationId: result.verificationSession.id,
295
- transactionData,
296
- nonce: authorizationRequest.nonce,
297
- audience: requestClientId,
298
- origin: options.origin,
299
- responseUri,
300
- mdocGeneratedNonce: result.jarm?.mdocGeneratedNonce,
301
- verificationSessionRecordId: result.verificationSession.id,
302
- vpTokenPresentationParseResult,
303
- credentialId,
304
- });
305
- });
306
- presentationVerificationResults = await Promise.all(presentationVerificationPromises);
307
- }
308
- if (result.type === 'pex') {
309
- const presentations = result.pex.presentations;
310
- const pex = agentContext.dependencyManager.resolve(core_1.DifPresentationExchangeService);
311
- pex.validatePresentationDefinition(result.pex.presentationDefinition);
312
- pex.validatePresentationSubmission(result.pex.presentationSubmission);
313
- const presentationsArray = Array.isArray(presentations) ? presentations : [presentations];
314
- const presentationVerificationPromises = presentationsArray.map((presentation) => {
315
- const inputDescriptor = result.pex.presentationSubmission.descriptor_map.find((descriptorMapEntry) => descriptorMapEntry.path === presentation.path);
316
- if (!inputDescriptor) {
317
- throw new core_1.CredoError(`Could not map transaction data entry to input descriptor.`);
318
- }
319
- return this.verifyPresentations(agentContext, {
320
- correlationId: result.verificationSession.id,
321
- transactionData,
322
- nonce: authorizationRequest.nonce,
137
+ const authorizationRequest = await did_auth_siop_1.AuthorizationRequest.fromUriOrJwt(options.verificationSession.authorizationRequestJwt);
138
+ const verifier = await this.getVerifierByVerifierId(agentContext, options.verificationSession.verifierId);
139
+ const requestClientId = await authorizationRequest.getMergedProperty('client_id');
140
+ const requestNonce = await authorizationRequest.getMergedProperty('nonce');
141
+ const requestState = await authorizationRequest.getMergedProperty('state');
142
+ const responseUri = await authorizationRequest.getMergedProperty('response_uri');
143
+ const presentationDefinitionsWithLocation = await authorizationRequest.getPresentationDefinitions();
144
+ if (!requestNonce || !requestClientId || !requestState) {
145
+ throw new core_1.CredoError(`Unable to find nonce, state, or client_id in authorization request for verification session '${options.verificationSession.id}'`);
146
+ }
147
+ const authorizationResponseUrl = (0, core_1.joinUriParts)(this.config.baseUrl, [
148
+ options.verificationSession.verifierId,
149
+ this.config.authorizationEndpoint.endpointPath,
150
+ ]);
151
+ const relyingParty = await this.getRelyingParty(agentContext, verifier, {
152
+ presentationDefinition: presentationDefinitionsWithLocation?.[0]?.definition,
153
+ authorizationResponseUrl,
154
+ clientId: requestClientId,
155
+ });
156
+ // This is very unfortunate, but storing state in sphereon's SiOP-OID4VP library
157
+ // is done async, so we can't be certain yet that the verification session record
158
+ // is updated already when we have verified the authorization response. So we need to
159
+ // wait for a short while before we can be certain that the verification session record
160
+ // is updated. To not use arbitrary timeouts, we wait for the specific RecordUpdatedEvent
161
+ // that is emitted when the verification session record is updated.
162
+ const eventEmitter = agentContext.dependencyManager.resolve(core_1.EventEmitter);
163
+ const verificationSessionUpdatedPromise = (0, rxjs_1.firstValueFrom)(eventEmitter
164
+ .observable(core_1.RepositoryEventTypes.RecordUpdated)
165
+ .pipe((0, rxjs_1.filter)((e) => e.metadata.contextCorrelationId === agentContext.contextCorrelationId), (0, rxjs_1.filter)((e) => e.payload.record.id === options.verificationSession.id &&
166
+ e.payload.record.verifierId === options.verificationSession.verifierId &&
167
+ (e.payload.record.state === OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.ResponseVerified ||
168
+ e.payload.record.state === OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.Error)), (0, rxjs_1.first)(), (0, rxjs_1.timeout)({
169
+ first: 10000,
170
+ meta: 'OpenId4VcSiopVerifierService.verifyAuthorizationResponse',
171
+ }), (0, rxjs_1.map)((e) => e.payload.record)));
172
+ await relyingParty.verifyAuthorizationResponse(options.authorizationResponse, {
173
+ audience: requestClientId,
174
+ correlationId: options.verificationSession.id,
175
+ state: requestState,
176
+ presentationDefinitions: presentationDefinitionsWithLocation,
177
+ verification: {
178
+ presentationVerificationCallback: this.getPresentationVerificationCallback(agentContext, {
179
+ correlationId: options.verificationSession.id,
180
+ nonce: requestNonce,
323
181
  audience: requestClientId,
324
182
  responseUri,
325
- mdocGeneratedNonce: result.jarm?.mdocGeneratedNonce,
326
- verificationSessionRecordId: result.verificationSession.id,
327
- vpTokenPresentationParseResult: presentation,
328
- credentialId: inputDescriptor.id,
329
- });
330
- });
331
- presentationVerificationResults = await Promise.all(presentationVerificationPromises);
332
- }
333
- try {
334
- const errorMessages = presentationVerificationResults
335
- .map((result, index) => (!result.verified ? `\t- [${index}]: ${result.reason}` : undefined))
336
- .filter((i) => i !== undefined);
337
- if (errorMessages.length > 0) {
338
- throw new core_1.CredoError(`One or more presentations failed verification. \n\t${errorMessages.join('\n')}`);
339
- }
340
- // Validate the presentations against the query
341
- if (result.type === 'pex') {
342
- const pex = agentContext.dependencyManager.resolve(core_1.DifPresentationExchangeService);
343
- const presentations = presentationVerificationResults
344
- .map((p) => (p.verified ? p.presentation : undefined))
345
- .filter((p) => p !== undefined);
346
- pex.validatePresentation(
347
- // FIXME: type. it should always be an object as we created it
348
- result.pex.presentationDefinition, presentations, result.pex.presentationSubmission);
349
- }
350
- else {
351
- const dcql = agentContext.dependencyManager.resolve(core_1.DcqlService);
352
- const presentations = presentationVerificationResults.reduce((all, p) => (p.verified ? { ...all, [p.credentialId]: p.presentation } : all), {});
353
- // FIXME: type for this parameter
354
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
355
- dcql.assertValidDcqlPresentation(presentations, result.dcql.query);
356
- }
357
- const transactionDataMeta = [];
358
- for (const result of presentationVerificationResults) {
359
- if (result.verified && result.transactionDataMeta) {
360
- transactionDataMeta.push([result.transactionDataMeta.credentialId, result.transactionDataMeta]);
361
- }
362
- }
363
- if (transactionData) {
364
- const inputDescriptorToTransactionDataMeta = Object.fromEntries(transactionDataMeta);
365
- if (!transactionData.every((tdEntry) => {
366
- return tdEntry.credential_ids.some((credentialId) => inputDescriptorToTransactionDataMeta[credentialId]);
367
- })) {
368
- throw new core_1.CredoError('One ore more required transaction data entries were not found in the signed transaction data.');
369
- }
370
- }
371
- }
372
- catch (error) {
373
- result.verificationSession.errorMessage = error.message;
374
- await this.updateState(agentContext, result.verificationSession, OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.Error);
375
- throw error;
376
- }
377
- result.verificationSession.authorizationResponsePayload = result.authResponsePayload;
378
- await this.updateState(agentContext, result.verificationSession, OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.ResponseVerified);
379
- const verifiedAuthorizationResponse = await this.getVerifiedAuthorizationResponse(agentContext, result.verificationSession);
380
- return { ...verifiedAuthorizationResponse, verificationSession: result.verificationSession };
183
+ mdocGeneratedNonce: options.jarmHeader?.apu
184
+ ? core_1.TypedArrayEncoder.toUtf8String(core_1.TypedArrayEncoder.fromBase64(options.jarmHeader.apu))
185
+ : undefined,
186
+ verificationSessionRecordId: options.verificationSession.id,
187
+ }),
188
+ },
189
+ });
190
+ const verificationSession = await verificationSessionUpdatedPromise;
191
+ const verifiedAuthorizationResponse = await this.getVerifiedAuthorizationResponse(verificationSession);
192
+ return {
193
+ ...verifiedAuthorizationResponse,
194
+ verificationSession: await verificationSessionUpdatedPromise,
195
+ };
381
196
  }
382
- async getVerifiedAuthorizationResponse(agentContext, verificationSession) {
197
+ async getVerifiedAuthorizationResponse(verificationSession) {
383
198
  verificationSession.assertState(OpenId4VcVerificationSessionState_1.OpenId4VcVerificationSessionState.ResponseVerified);
384
199
  if (!verificationSession.authorizationResponsePayload) {
385
200
  throw new core_1.CredoError('No authorization response payload found in the verification session.');
386
201
  }
387
- const openid4vpAuthorizationResponsePayload = verificationSession.authorizationResponsePayload;
388
- const openid4vpVerifier = this.getOpenid4vpVerifier(agentContext);
389
- const authorizationRequest = openid4vpVerifier.parseOpenid4vpAuthorizationRequestPayload({
390
- authorizationRequest: verificationSession.request,
391
- });
392
- if (authorizationRequest.provided !== 'jwt' || authorizationRequest.type === 'jar') {
393
- throw new core_1.CredoError('Invalid authorization request jwt');
394
- }
395
- const result = openid4vpVerifier.validateOpenid4vpAuthorizationResponse({
396
- authorizationRequest: authorizationRequest.params,
397
- authorizationResponse: openid4vpAuthorizationResponsePayload,
398
- });
399
- const transactionData = authorizationRequest.params.transaction_data
400
- ? openid4vpVerifier.parseTransactionData({ transactionData: authorizationRequest.params.transaction_data })
202
+ const authorizationResponse = await did_auth_siop_1.AuthorizationResponse.fromPayload(verificationSession.authorizationResponsePayload);
203
+ const authorizationRequest = await did_auth_siop_1.AuthorizationRequest.fromUriOrJwt(verificationSession.authorizationRequestJwt);
204
+ const idToken = authorizationResponse.idToken
205
+ ? { payload: await authorizationResponse.idToken?.payload() }
401
206
  : undefined;
402
207
  let presentationExchange = undefined;
403
- const dcql = result.type === 'dcql'
404
- ? this.getDcqlVerifiedResponse(agentContext, authorizationRequest.params.dcql_query, result.dcql.presentation)
405
- : undefined;
406
- const vpToken = (0, utils_1.parseIfJson)(openid4vpAuthorizationResponsePayload.vp_token);
407
- const presentationDefinition = authorizationRequest.params
408
- .presentation_definition;
409
- if (presentationDefinition) {
410
- if (!vpToken) {
411
- throw new core_1.CredoError('Missing vp_token in the openid4vp authorization response.');
412
- }
413
- const rawPresentations = openid4vpVerifier.parsePresentationsFromVpToken({ vpToken });
414
- const submission = openid4vpAuthorizationResponsePayload.presentation_submission;
208
+ const presentationDefinitions = await authorizationRequest.getPresentationDefinitions();
209
+ if (presentationDefinitions && presentationDefinitions.length > 0) {
210
+ const rawPresentations = authorizationResponse.payload.vp_token
211
+ ? await (0, OpenID4VP_1.extractPresentationsFromVpToken)(authorizationResponse.payload.vp_token, {
212
+ hasher: core_1.Hasher.hash,
213
+ })
214
+ : [];
215
+ // TODO: Probably wise to check against request for the location of the submission_data
216
+ const submission = idToken?.payload?._vp_token?.presentation_submission ?? authorizationResponse.payload.presentation_submission;
415
217
  if (!submission) {
416
218
  throw new core_1.CredoError('Unable to extract submission from the response.');
417
219
  }
418
- const verifiablePresentations = rawPresentations.map((presentation) => this.getPresentationFromVpTokenParseResult(agentContext, presentation));
220
+ // FIXME: should return type be an array? As now it doesn't always match the submission
221
+ const verifiablePresentations = Array.isArray(rawPresentations)
222
+ ? rawPresentations.map(transform_1.getVerifiablePresentationFromSphereonWrapped)
223
+ : (0, transform_1.getVerifiablePresentationFromSphereonWrapped)(rawPresentations);
224
+ const definition = presentationDefinitions[0].definition;
419
225
  presentationExchange = {
420
- definition: presentationDefinition,
226
+ definition,
421
227
  submission,
422
- presentations: verifiablePresentations,
423
- descriptors: (0, core_1.extractPresentationsWithDescriptorsFromSubmission)(verifiablePresentations, submission, presentationDefinition),
228
+ // We always return this as an array
229
+ presentations: Array.isArray(verifiablePresentations) ? verifiablePresentations : [verifiablePresentations],
230
+ descriptors: (0, core_1.extractPresentationsWithDescriptorsFromSubmission)(verifiablePresentations, submission, definition),
424
231
  };
425
232
  }
426
- if (!presentationExchange && !dcql) {
427
- throw new core_1.CredoError('No presentationExchange or dcql found in the response.');
233
+ if (!idToken && !presentationExchange) {
234
+ throw new core_1.CredoError('No idToken or presentationExchange found in the response.');
428
235
  }
429
236
  return {
237
+ idToken,
430
238
  presentationExchange,
431
- dcql,
432
- transactionData,
433
239
  };
434
240
  }
435
241
  /**
@@ -440,8 +246,16 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
440
246
  let nonce;
441
247
  let state;
442
248
  if (authorizationResponse) {
443
- const state = authorizationResponse.state;
444
- if (!state) {
249
+ const authorizationResponseInstance = await did_auth_siop_1.AuthorizationResponse.fromPayload(authorizationResponse).catch(() => {
250
+ throw new core_1.CredoError(`Unable to parse authorization response payload. ${JSON.stringify(authorizationResponse)}`);
251
+ });
252
+ nonce = await authorizationResponseInstance.getMergedProperty('nonce', {
253
+ hasher: core_1.Hasher.hash,
254
+ });
255
+ state = await authorizationResponseInstance.getMergedProperty('state', {
256
+ hasher: core_1.Hasher.hash,
257
+ });
258
+ if (!nonce && !state) {
445
259
  throw new core_1.CredoError('Could not extract nonce or state from authorization response. Unable to find OpenId4VcVerificationSession.');
446
260
  }
447
261
  }
@@ -483,18 +297,37 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
483
297
  async getVerificationSessionById(agentContext, verificationSessionId) {
484
298
  return this.openId4VcVerificationSessionRepository.getById(agentContext, verificationSessionId);
485
299
  }
486
- async getClientMetadata(agentContext, options) {
487
- const { responseMode, verifier } = options;
300
+ async getRelyingParty(agentContext, verifier, { idToken, presentationDefinition, clientId, clientIdScheme, authorizationResponseUrl, responseMode, }) {
488
301
  const signatureSuiteRegistry = agentContext.dependencyManager.resolve(core_1.SignatureSuiteRegistry);
489
302
  const supportedAlgs = (0, utils_1.getSupportedJwaSignatureAlgorithms)(agentContext);
490
303
  const supportedMdocAlgs = supportedAlgs.filter(core_1.isMdocSupportedSignatureAlgorithm);
491
304
  const supportedProofTypes = signatureSuiteRegistry.supportedProofTypes;
305
+ // Check: audience must be set to the issuer with dynamic disc otherwise self-issued.me/v2.
306
+ const builder = did_auth_siop_1.RP.builder();
307
+ const responseTypes = [];
308
+ if (!presentationDefinition && idToken === false) {
309
+ throw new core_1.CredoError('Either `presentationExchange` or `idToken` must be enabled');
310
+ }
311
+ if (presentationDefinition) {
312
+ responseTypes.push(did_auth_siop_1.ResponseType.VP_TOKEN);
313
+ }
314
+ if (idToken === true || !presentationDefinition) {
315
+ responseTypes.push(did_auth_siop_1.ResponseType.ID_TOKEN);
316
+ }
492
317
  // FIXME: we now manually remove did:peer, we should probably allow the user to configure this
493
318
  const supportedDidMethods = agentContext.dependencyManager
494
319
  .resolve(core_1.DidsApi)
495
320
  .supportedResolverMethods.filter((m) => m !== 'peer');
321
+ // The OpenId4VcRelyingPartyEventHandler is a global event handler that makes sure that
322
+ // all the events are handled, and that the correct context is used for the events.
323
+ const sphereonEventEmitter = agentContext.dependencyManager
324
+ .resolve(OpenId4VcRelyingPartyEventEmitter_1.OpenId4VcRelyingPartyEventHandler)
325
+ .getEventEmitterForVerifier(agentContext.contextCorrelationId, verifier.verifierId);
326
+ const mode = !responseMode || responseMode === 'direct_post'
327
+ ? did_auth_siop_1.ResponseMode.DIRECT_POST
328
+ : did_auth_siop_1.ResponseMode.DIRECT_POST_JWT;
496
329
  let jarmEncryptionJwk;
497
- if ((0, openid4vp_1.isJarmResponseMode)(responseMode)) {
330
+ if (mode === did_auth_siop_1.ResponseMode.DIRECT_POST_JWT) {
498
331
  const key = await agentContext.wallet.createKey({ keyType: core_1.KeyType.P256 });
499
332
  jarmEncryptionJwk = { ...(0, core_1.getJwkFromKey)(key).toJson(), kid: key.fingerprint, use: 'enc' };
500
333
  }
@@ -502,20 +335,44 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
502
335
  ? {
503
336
  jwks: { keys: [jarmEncryptionJwk] },
504
337
  authorization_encrypted_response_alg: 'ECDH-ES',
505
- // FIXME: we need to allow setting this, but also to fetch it based on the `request_uri` and
506
- // `request_uri_method`
507
- authorization_encrypted_response_enc: 'A128GCM',
338
+ authorization_encrypted_response_enc: 'A256GCM',
508
339
  }
509
340
  : undefined;
510
- return {
341
+ builder
342
+ .withClientId(clientId)
343
+ .withResponseUri(authorizationResponseUrl)
344
+ .withIssuer(did_auth_siop_1.ResponseIss.SELF_ISSUED_V2)
345
+ .withAudience(did_auth_siop_1.RequestAud.SELF_ISSUED_V2)
346
+ .withIssuer(did_auth_siop_1.ResponseIss.SELF_ISSUED_V2)
347
+ .withSupportedVersions([
348
+ did_auth_siop_1.SupportedVersion.SIOPv2_D11,
349
+ did_auth_siop_1.SupportedVersion.SIOPv2_D12_OID4VP_D18,
350
+ did_auth_siop_1.SupportedVersion.SIOPv2_D12_OID4VP_D20,
351
+ ])
352
+ .withResponseMode(mode)
353
+ .withHasher(core_1.Hasher.hash)
354
+ // FIXME: should allow verification of revocation
355
+ // .withRevocationVerificationCallback()
356
+ .withRevocationVerification(did_auth_siop_1.RevocationVerification.NEVER)
357
+ .withSessionManager(new OpenId4VcRelyingPartySessionManager_1.OpenId4VcRelyingPartySessionManager(agentContext, verifier.verifierId))
358
+ .withEventEmitter(sphereonEventEmitter)
359
+ .withResponseType(responseTypes)
360
+ .withCreateJwtCallback((0, utils_1.getCreateJwtCallback)(agentContext))
361
+ .withVerifyJwtCallback((0, utils_1.getVerifyJwtCallback)(agentContext))
362
+ // TODO: we should probably allow some dynamic values here
363
+ .withClientMetadata({
511
364
  ...jarmClientMetadata,
512
365
  ...verifier.clientMetadata,
513
- response_types_supported: ['vp_token'],
366
+ // FIXME: not passing client_id here means it will not be added
367
+ // to the authorization request url (not the signed payload). Need
368
+ // to fix that in Sphereon lib
369
+ client_id: clientId,
370
+ passBy: did_auth_siop_1.PassBy.VALUE,
371
+ response_types_supported: [did_auth_siop_1.ResponseType.VP_TOKEN],
514
372
  subject_syntax_types_supported: [
515
373
  'urn:ietf:params:oauth:jwk-thumbprint',
516
374
  ...supportedDidMethods.map((m) => `did:${m}`),
517
375
  ],
518
- authorization_signed_response_alg: 'RS256',
519
376
  vp_formats: {
520
377
  mso_mdoc: {
521
378
  alg: supportedMdocAlgs,
@@ -542,249 +399,146 @@ let OpenId4VcSiopVerifierService = class OpenId4VcSiopVerifierService {
542
399
  'kb-jwt_alg_values': supportedAlgs,
543
400
  'sd-jwt_alg_values': supportedAlgs,
544
401
  },
545
- 'dc+sd-jwt': {
546
- 'kb-jwt_alg_values': supportedAlgs,
547
- 'sd-jwt_alg_values': supportedAlgs,
548
- },
549
402
  },
550
- };
551
- }
552
- getPresentationFromVpTokenParseResult(agentContext, vpTokenPresentationParseResult) {
553
- if (vpTokenPresentationParseResult.format === 'dc+sd-jwt') {
554
- const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
555
- return sdJwtVcApi.fromCompact(vpTokenPresentationParseResult.presentation);
556
- }
557
- else if (vpTokenPresentationParseResult.format === 'mso_mdoc') {
558
- return core_1.MdocDeviceResponse.fromBase64Url(vpTokenPresentationParseResult.presentation);
559
- }
560
- else if (vpTokenPresentationParseResult.format === 'jwt_vp_json') {
561
- return core_1.W3cJwtVerifiablePresentation.fromSerializedJwt(vpTokenPresentationParseResult.presentation);
562
- }
563
- else if (vpTokenPresentationParseResult.format === 'ldp_vp') {
564
- return core_1.JsonTransformer.fromJSON(vpTokenPresentationParseResult.presentation, core_1.W3cJsonLdVerifiablePresentation);
565
- }
566
- throw new core_1.CredoError(`Unsupported presentation format. ${vpTokenPresentationParseResult.format}`);
567
- }
568
- getTransactionDataMeta(options) {
569
- const { vpTokenPresentationParseResult, transactionData, transactionDataResult, credentialId } = options;
570
- if (!transactionData) {
571
- throw new core_1.CredoError('Could not map transaction data result to the request');
572
- }
573
- const hashName = transactionDataResult.hashes_alg ?? 'sha-256';
574
- const presentationHashes = transactionDataResult.hashes;
575
- const transactionDataEntriesWithHash = transactionData.map((tdEntry) => {
576
- const hash = core_1.TypedArrayEncoder.toBase64URL(core_1.Hasher.hash(core_1.JsonEncoder.toBase64URL(tdEntry), hashName));
577
- return hash;
578
403
  });
579
- for (const [idx, hash] of transactionDataEntriesWithHash.entries()) {
580
- if (presentationHashes[idx] !== hash) {
581
- throw new core_1.CredoError(`Transaction data entry ${idx} does not match hash ${hash}`);
582
- }
583
- }
584
- return {
585
- credentialId,
586
- transactionData,
587
- transactionDataResult,
588
- path: vpTokenPresentationParseResult.path,
589
- };
590
- }
591
- decodePresentation(agentContext, options) {
592
- const { vpTokenPresentationParseResult } = options;
593
- if (vpTokenPresentationParseResult.format === 'dc+sd-jwt') {
594
- // TODO: it might be better here to look at the presentation submission to know
595
- // If presentation includes a ~, we assume it's an SD-JWT-VC
596
- const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
597
- const sdJwtVc = sdJwtVcApi.fromCompact(vpTokenPresentationParseResult.presentation);
598
- return sdJwtVc;
404
+ if (clientIdScheme) {
405
+ builder.withClientIdScheme(clientIdScheme);
599
406
  }
600
- else if (vpTokenPresentationParseResult.format === 'mso_mdoc') {
601
- const mdocDeviceResponse = core_1.MdocDeviceResponse.fromBase64Url(vpTokenPresentationParseResult.presentation);
602
- return mdocDeviceResponse;
407
+ if (presentationDefinition) {
408
+ builder.withPresentationDefinition({ definition: presentationDefinition }, [did_auth_siop_1.PropertyTarget.REQUEST_OBJECT]);
603
409
  }
604
- else if (vpTokenPresentationParseResult.format === 'jwt_vp_json') {
605
- return core_1.W3cJwtVerifiablePresentation.fromSerializedJwt(vpTokenPresentationParseResult.presentation);
606
- }
607
- else {
608
- return core_1.JsonTransformer.fromJSON(vpTokenPresentationParseResult.presentation, core_1.W3cJsonLdVerifiablePresentation);
410
+ if (responseTypes.includes(did_auth_siop_1.ResponseType.ID_TOKEN)) {
411
+ builder.withScope('openid');
609
412
  }
413
+ return builder.build();
610
414
  }
611
- async verifyPresentations(agentContext, options) {
612
- const { vpTokenPresentationParseResult, transactionData } = options;
613
- let transactionDataMeta = undefined;
614
- try {
615
- this.logger.debug(`Presentation response`, core_1.JsonTransformer.toJSON(vpTokenPresentationParseResult.presentation));
616
- if (!vpTokenPresentationParseResult)
617
- throw new core_1.CredoError('Did not receive a presentation for verification.');
618
- const x509Config = agentContext.dependencyManager.resolve(core_1.X509ModuleConfig);
619
- let isValid;
620
- let reason = undefined;
621
- let verifiablePresentation;
622
- if (vpTokenPresentationParseResult.format === 'dc+sd-jwt') {
623
- // TODO: it might be better here to look at the presentation submission to know
624
- // If presentation includes a ~, we assume it's an SD-JWT-VC
625
- const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
626
- const jwt = core_1.Jwt.fromSerializedJwt(vpTokenPresentationParseResult.presentation.split('~')[0]);
627
- const sdJwtVc = sdJwtVcApi.fromCompact(vpTokenPresentationParseResult.presentation);
628
- const certificateChain = (0, core_1.extractX509CertificatesFromJwt)(jwt);
629
- let trustedCertificates = undefined;
630
- if (certificateChain && x509Config.getTrustedCertificatesForVerification) {
631
- trustedCertificates = await x509Config.getTrustedCertificatesForVerification(agentContext, {
632
- certificateChain,
633
- verification: {
634
- type: 'credential',
635
- credential: sdJwtVc,
636
- openId4VcVerificationSessionId: options.verificationSessionRecordId,
415
+ getPresentationVerificationCallback(agentContext, options) {
416
+ return async (encodedPresentation, presentationSubmission) => {
417
+ try {
418
+ this.logger.debug('Presentation response', core_1.JsonTransformer.toJSON(encodedPresentation));
419
+ this.logger.debug('Presentation submission', presentationSubmission);
420
+ if (!encodedPresentation)
421
+ throw new core_1.CredoError('Did not receive a presentation for verification.');
422
+ const x509Config = agentContext.dependencyManager.resolve(core_1.X509ModuleConfig);
423
+ let isValid;
424
+ let reason = undefined;
425
+ if (typeof encodedPresentation === 'string' && encodedPresentation.includes('~')) {
426
+ // TODO: it might be better here to look at the presentation submission to know
427
+ // If presentation includes a ~, we assume it's an SD-JWT-VC
428
+ const sdJwtVcApi = agentContext.dependencyManager.resolve(core_1.SdJwtVcApi);
429
+ const jwt = core_1.Jwt.fromSerializedJwt(encodedPresentation.split('~')[0]);
430
+ const sdJwtVc = sdJwtVcApi.fromCompact(encodedPresentation);
431
+ const certificateChain = (0, core_1.extractX509CertificatesFromJwt)(jwt);
432
+ let trustedCertificates = undefined;
433
+ if (certificateChain && x509Config.getTrustedCertificatesForVerification) {
434
+ trustedCertificates = await x509Config.getTrustedCertificatesForVerification(agentContext, {
435
+ certificateChain,
436
+ verification: {
437
+ type: 'credential',
438
+ credential: sdJwtVc,
439
+ openId4VcVerificationSessionId: options.verificationSessionRecordId,
440
+ },
441
+ });
442
+ }
443
+ if (!trustedCertificates) {
444
+ // We also take from the config here to avoid the callback being called again
445
+ trustedCertificates = x509Config.trustedCertificates ?? [];
446
+ }
447
+ const verificationResult = await sdJwtVcApi.verify({
448
+ compactSdJwtVc: encodedPresentation,
449
+ keyBinding: {
450
+ audience: options.audience,
451
+ nonce: options.nonce,
637
452
  },
453
+ trustedCertificates,
638
454
  });
455
+ isValid = verificationResult.verification.isValid;
456
+ reason = verificationResult.isValid ? undefined : verificationResult.error.message;
639
457
  }
640
- if (!trustedCertificates) {
641
- // We also take from the config here to avoid the callback being called again
642
- trustedCertificates = x509Config.trustedCertificates ?? [];
643
- }
644
- const verificationResult = await sdJwtVcApi.verify({
645
- compactSdJwtVc: vpTokenPresentationParseResult.presentation,
646
- keyBinding: {
647
- audience: options.audience,
648
- nonce: options.nonce,
649
- },
650
- trustedCertificates,
651
- });
652
- if (verificationResult.sdJwtVc?.transactionData) {
653
- transactionDataMeta = this.getTransactionDataMeta({
654
- vpTokenPresentationParseResult,
655
- transactionData,
656
- transactionDataResult: verificationResult.sdJwtVc.transactionData,
657
- credentialId: options.credentialId,
658
- });
458
+ else if (typeof encodedPresentation === 'string' && !core_1.Jwt.format.test(encodedPresentation)) {
459
+ if (!options.responseUri || !options.mdocGeneratedNonce) {
460
+ isValid = false;
461
+ reason = 'Mdoc device response verification failed. Response uri and the mdocGeneratedNonce are not set';
462
+ }
463
+ else {
464
+ const mdocDeviceResponse = core_1.MdocDeviceResponse.fromBase64Url(encodedPresentation);
465
+ if (mdocDeviceResponse.documents.length !== 1) {
466
+ throw new core_1.CredoError('Only a single mdoc is supported per device response for OpenID4VP verification');
467
+ }
468
+ const document = mdocDeviceResponse.documents[0];
469
+ const certificateChain = document.issuerSignedCertificateChain.map((cert) => core_1.X509Certificate.fromRawCertificate(cert));
470
+ const trustedCertificates = await x509Config.getTrustedCertificatesForVerification?.(agentContext, {
471
+ certificateChain,
472
+ verification: {
473
+ type: 'credential',
474
+ credential: document,
475
+ openId4VcVerificationSessionId: options.verificationSessionRecordId,
476
+ },
477
+ });
478
+ await mdocDeviceResponse.verify(agentContext, {
479
+ sessionTranscriptOptions: {
480
+ clientId: options.audience,
481
+ mdocGeneratedNonce: options.mdocGeneratedNonce,
482
+ responseUri: options.responseUri,
483
+ verifierGeneratedNonce: options.nonce,
484
+ },
485
+ trustedCertificates,
486
+ });
487
+ isValid = true;
488
+ }
659
489
  }
660
- isValid = verificationResult.verification.isValid;
661
- reason = verificationResult.isValid ? undefined : verificationResult.error.message;
662
- verifiablePresentation = sdJwtVc;
663
- }
664
- else if (vpTokenPresentationParseResult.format === 'mso_mdoc') {
665
- const mdocDeviceResponse = core_1.MdocDeviceResponse.fromBase64Url(vpTokenPresentationParseResult.presentation);
666
- const trustedCertificates = (await Promise.all(mdocDeviceResponse.documents.map(async (mdoc) => {
667
- const certificateChain = mdoc.issuerSignedCertificateChain.map((cert) => core_1.X509Certificate.fromRawCertificate(cert));
668
- const trustedCertificates = await x509Config.getTrustedCertificatesForVerification?.(agentContext, {
669
- certificateChain,
670
- verification: {
671
- type: 'credential',
672
- credential: mdoc,
673
- openId4VcVerificationSessionId: options.verificationSessionRecordId,
674
- },
490
+ else if (typeof encodedPresentation === 'string' && core_1.Jwt.format.test(encodedPresentation)) {
491
+ const presentation = core_1.W3cJwtVerifiablePresentation.fromSerializedJwt(encodedPresentation);
492
+ const certificateChain = (0, core_1.extractX509CertificatesFromJwt)(presentation.jwt);
493
+ let trustedCertificates = undefined;
494
+ if (certificateChain && x509Config.getTrustedCertificatesForVerification) {
495
+ trustedCertificates = await x509Config.getTrustedCertificatesForVerification?.(agentContext, {
496
+ certificateChain,
497
+ verification: {
498
+ type: 'credential',
499
+ credential: presentation,
500
+ openId4VcVerificationSessionId: options.verificationSessionRecordId,
501
+ },
502
+ });
503
+ }
504
+ if (!trustedCertificates) {
505
+ trustedCertificates = x509Config.trustedCertificates ?? [];
506
+ }
507
+ const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, {
508
+ presentation: encodedPresentation,
509
+ challenge: options.nonce,
510
+ domain: options.audience,
511
+ trustedCertificates,
675
512
  });
676
- // TODO: could have some duplication but not a big issue
677
- return trustedCertificates ?? x509Config.trustedCertificates;
678
- })))
679
- .filter((c) => c !== undefined)
680
- .flatMap((c) => c);
681
- let sessionTranscriptOptions;
682
- if (options.origin) {
683
- sessionTranscriptOptions = {
684
- type: 'openId4VpDcApi',
685
- clientId: options.audience,
686
- verifierGeneratedNonce: options.nonce,
687
- origin: options.origin,
688
- };
513
+ isValid = verificationResult.isValid;
514
+ reason = verificationResult.error?.message;
689
515
  }
690
516
  else {
691
- if (!options.mdocGeneratedNonce || !options.responseUri) {
692
- throw new core_1.CredoError('mdocGeneratedNonce and responseUri are required for mdoc openid4vp session transcript calculation');
693
- }
694
- sessionTranscriptOptions = {
695
- type: 'openId4Vp',
696
- clientId: options.audience,
697
- mdocGeneratedNonce: options.mdocGeneratedNonce,
698
- responseUri: options.responseUri,
699
- verifierGeneratedNonce: options.nonce,
700
- };
701
- }
702
- await mdocDeviceResponse.verify(agentContext, {
703
- sessionTranscriptOptions,
704
- trustedCertificates,
705
- });
706
- isValid = true;
707
- verifiablePresentation = mdocDeviceResponse;
708
- }
709
- else if (vpTokenPresentationParseResult.format === 'jwt_vp_json') {
710
- const sdJwtPresentation = core_1.W3cJwtVerifiablePresentation.fromSerializedJwt(vpTokenPresentationParseResult.presentation);
711
- const certificateChain = (0, core_1.extractX509CertificatesFromJwt)(sdJwtPresentation.jwt);
712
- let trustedCertificates = undefined;
713
- if (certificateChain && x509Config.getTrustedCertificatesForVerification) {
714
- trustedCertificates = await x509Config.getTrustedCertificatesForVerification?.(agentContext, {
715
- certificateChain,
716
- verification: {
717
- type: 'credential',
718
- credential: sdJwtPresentation,
719
- openId4VcVerificationSessionId: options.verificationSessionRecordId,
720
- },
517
+ const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, {
518
+ presentation: core_1.JsonTransformer.fromJSON(encodedPresentation, core_1.W3cJsonLdVerifiablePresentation),
519
+ challenge: options.nonce,
520
+ domain: options.audience,
721
521
  });
522
+ isValid = verificationResult.isValid;
523
+ reason = verificationResult.error?.message;
722
524
  }
723
- if (!trustedCertificates) {
724
- trustedCertificates = x509Config.trustedCertificates ?? [];
525
+ if (!isValid) {
526
+ throw new Error(reason);
725
527
  }
726
- const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, {
727
- presentation: vpTokenPresentationParseResult.presentation,
728
- challenge: options.nonce,
729
- domain: options.audience,
730
- trustedCertificates,
731
- });
732
- isValid = verificationResult.isValid;
733
- reason = verificationResult.error?.message;
734
- verifiablePresentation = core_1.W3cJwtVerifiablePresentation.fromSerializedJwt(vpTokenPresentationParseResult.presentation);
528
+ return {
529
+ verified: true,
530
+ };
735
531
  }
736
- else {
737
- const w3cJsonLdVerifiablePresentation = core_1.JsonTransformer.fromJSON(vpTokenPresentationParseResult.presentation, core_1.W3cJsonLdVerifiablePresentation);
738
- const verificationResult = await this.w3cCredentialService.verifyPresentation(agentContext, {
739
- presentation: w3cJsonLdVerifiablePresentation,
740
- challenge: options.nonce,
741
- domain: options.audience,
532
+ catch (error) {
533
+ agentContext.config.logger.warn('Error occurred during verification of presentation', {
534
+ error,
742
535
  });
743
- isValid = verificationResult.isValid;
744
- reason = verificationResult.error?.message;
745
- verifiablePresentation = w3cJsonLdVerifiablePresentation;
746
- }
747
- if (!isValid) {
748
- throw new Error(reason);
536
+ return {
537
+ verified: false,
538
+ reason: error.message,
539
+ };
749
540
  }
750
- return {
751
- verified: true,
752
- transactionDataMeta,
753
- presentation: verifiablePresentation,
754
- credentialId: options.credentialId,
755
- };
756
- }
757
- catch (error) {
758
- agentContext.config.logger.warn('Error occurred during verification of presentation', {
759
- error,
760
- });
761
- return {
762
- verified: false,
763
- reason: error.message,
764
- credentialId: options.credentialId,
765
- };
766
- }
767
- }
768
- /**
769
- * Update the record to a new state and emit an state changed event. Also updates the record
770
- * in storage.
771
- */
772
- async updateState(agentContext, verificationSession, newState) {
773
- agentContext.config.logger.debug(`Updating openid4vc verification session record ${verificationSession.id} to state ${newState} (previous=${verificationSession.state})`);
774
- const previousState = verificationSession.state;
775
- verificationSession.state = newState;
776
- await this.openId4VcVerificationSessionRepository.update(agentContext, verificationSession);
777
- this.emitStateChangedEvent(agentContext, verificationSession, previousState);
778
- }
779
- emitStateChangedEvent(agentContext, verificationSession, previousState) {
780
- const eventEmitter = agentContext.dependencyManager.resolve(core_1.EventEmitter);
781
- eventEmitter.emit(agentContext, {
782
- type: OpenId4VcVerifierEvents_1.OpenId4VcVerifierEvents.VerificationSessionStateChanged,
783
- payload: {
784
- verificationSession: verificationSession.clone(),
785
- previousState,
786
- },
787
- });
541
+ };
788
542
  }
789
543
  };
790
544
  exports.OpenId4VcSiopVerifierService = OpenId4VcSiopVerifierService;