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