@animo-id/eudi-wallet-functionality 0.0.0-alpha-20250604122408 → 0.0.0-alpha-20260108162340

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/dist/index.mjs CHANGED
@@ -1,207 +1,409 @@
1
- // src/verifyOpenid4VpAuthorizationRequest.ts
2
- import { JwsService, Jwt, X509Certificate } from "@credo-ts/core";
3
- import z from "zod";
1
+ import z$1, { z } from "zod";
2
+ import { JwsService, Jwt, X509Certificate, equalsIgnoreOrder, equalsWithOrder } from "@credo-ts/core";
4
3
 
5
- // src/isDcqlQueryEqualOrSubset.ts
6
- import { equalsIgnoreOrder, equalsWithOrder } from "@credo-ts/core";
4
+ //#region src/error.ts
5
+ var EudiWalletExtensionsError = class EudiWalletExtensionsError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "EudiWalletExtensionsError";
9
+ if (Error.captureStackTrace) Error.captureStackTrace(this, EudiWalletExtensionsError);
10
+ }
11
+ };
12
+ var Ts12IntegrityError = class Ts12IntegrityError extends EudiWalletExtensionsError {
13
+ constructor(uri, integrity) {
14
+ super(`Invalid integrity for ${uri}, expected ${integrity}`);
15
+ this.name = "Ts12IntegrityError";
16
+ if (Error.captureStackTrace) Error.captureStackTrace(this, Ts12IntegrityError);
17
+ }
18
+ };
19
+
20
+ //#endregion
21
+ //#region src/validation/z-sca-attestation-ext.ts
22
+ const zScaTransactionDataTypeClaims = z.array(z.object({
23
+ path: z.array(z.string()),
24
+ visualisation: z.union([
25
+ z.literal(1),
26
+ z.literal(2),
27
+ z.literal(3),
28
+ z.literal(4)
29
+ ]).default(3),
30
+ display: z.array(z.object({
31
+ name: z.string(),
32
+ locale: z.string().optional(),
33
+ logo: z.string().optional()
34
+ })).min(1)
35
+ }));
36
+ const zScaTransactionDataTypeUiLabels = z.object({
37
+ affirmative_action_label: z.array(z.object({
38
+ lang: z.string(),
39
+ value: z.string().max(30)
40
+ })).min(1),
41
+ denial_action_label: z.array(z.object({
42
+ lang: z.string(),
43
+ value: z.string().max(30)
44
+ })).min(1).optional(),
45
+ transaction_title: z.array(z.object({
46
+ lang: z.string(),
47
+ value: z.string().max(50)
48
+ })).min(1).optional(),
49
+ security_hint: z.array(z.object({
50
+ lang: z.string(),
51
+ value: z.string().max(250)
52
+ })).min(1).optional()
53
+ }).catchall(z.array(z.object({
54
+ lang: z.string(),
55
+ value: z.string()
56
+ })));
57
+ /**
58
+ * @name zScaAttestationExt
59
+ * @version EUDI TS12 v1.0 (05 December 2025)
60
+ * @description Defines metadata for SCA Attestations, including transaction types and UI localization.
61
+ * @see [EUDI TS12, Section 3] for VC Type Metadata requirements.
62
+ * @see [EUDI TS12, Section 4.1] for Metadata structure.
63
+ */
64
+ const zScaAttestationExt = z.object({
65
+ category: z.string().optional(),
66
+ transaction_data_types: z.record(z.string().describe("Transaction Type URI (e.g., urn:eudi:sca:payment:1). Must be collision resistant."), z.intersection(z.union([z.object({ schema: z.string() }), z.object({
67
+ schema_uri: z.url(),
68
+ "schema_uri#integrity": z.string().optional()
69
+ })]), z.intersection(z.union([z.object({ claims: zScaTransactionDataTypeClaims }), z.object({
70
+ claims_uri: z.url(),
71
+ "claims_uri#integrity": z.string().optional()
72
+ })]), z.union([z.object({ ui_labels: zScaTransactionDataTypeUiLabels }), z.object({
73
+ ui_labels_uri: z.string().url(),
74
+ "ui_labels_uri#integrity": z.string().optional()
75
+ })]))))
76
+ });
77
+
78
+ //#endregion
79
+ //#region src/validation/z-transaction-data-common.ts
80
+ /**
81
+ * **OpenID4VP Common Fields**
82
+ * Fields required by the transport protocol.
83
+ */
84
+ const zBaseTransaction = z.object({
85
+ type: z.string(),
86
+ credential_ids: z.tuple([z.string()]).rest(z.string()),
87
+ transaction_data_hashes_alg: z.tuple([z.string()]).rest(z.string()).optional()
88
+ });
89
+
90
+ //#endregion
91
+ //#region src/validation/z-transaction-data-funke.ts
92
+ /**
93
+ * **Funke (German) QES Authorization Data**
94
+ * * Profile used by the SPRIND/Bundesdruckerei EUDI Wallet.
95
+ * * This type bridges OpenID4VP with ETSI TS 119 432 (Remote Signing).
96
+ * * @see German National EUDI Wallet Architecture (Appendix 07)
97
+ */
98
+ const zFunkeQesTransaction = zBaseTransaction.extend({
99
+ signatureQualifier: z.enum(["eu_eidas_qes", "eu_eidas_aes"]).describe("eu_eidas_qes (Qualified) or eu_eidas_aes (Advanced)"),
100
+ documentDigests: z.array(z.object({
101
+ label: z.string().describe("Filename (e.g. 'Contract.pdf')"),
102
+ hash: z.string().describe("Base64 encoded hash"),
103
+ hashAlgorithmOID: z.string().optional().describe("OID of the hash algorithm")
104
+ })).min(1)
105
+ });
106
+
107
+ //#endregion
108
+ //#region src/validation/z-transaction-data-ts12.ts
109
+ /**
110
+ * **TS12 Payment Payload**
111
+ * * The business data strictly defined for Payments.
112
+ * * @see EUDI TS12 Section 4.3.1 "Payment Confirmation"
113
+ */
114
+ const zPaymentPayload = z.object({
115
+ transaction_id: z.string().min(1).max(36).describe("Unique identifier of the Relying Party's interaction"),
116
+ date_time: z.iso.datetime().optional(),
117
+ payee: z.object({
118
+ name: z.string(),
119
+ id: z.string(),
120
+ logo: z.url().optional(),
121
+ website: z.url().optional()
122
+ }),
123
+ currency: z.string().regex(/^[A-Z]{3}$/),
124
+ amount: z.number(),
125
+ amount_estimated: z.boolean().optional(),
126
+ amount_earmarked: z.boolean().optional(),
127
+ sct_inst: z.boolean().optional(),
128
+ pisp: z.object({
129
+ legal_name: z.string(),
130
+ brand_name: z.string(),
131
+ domain_name: z.string()
132
+ }).optional(),
133
+ execution_date: z.iso.datetime().optional().refine((date) => {
134
+ if (!date) return true;
135
+ return new Date(date) >= /* @__PURE__ */ new Date();
136
+ }, { message: "Execution date must not be in the past" }),
137
+ recurrence: z.object({
138
+ start_date: z.iso.datetime().optional(),
139
+ end_date: z.iso.datetime().optional(),
140
+ number: z.number().int().optional(),
141
+ frequency: z.enum([
142
+ "INDA",
143
+ "DAIL",
144
+ "WEEK",
145
+ "TOWK",
146
+ "TWMN",
147
+ "MNTH",
148
+ "TOMN",
149
+ "QUTR",
150
+ "FOMN",
151
+ "SEMI",
152
+ "YEAR",
153
+ "TYEA"
154
+ ]),
155
+ mit_options: z.object({
156
+ amount_variable: z.boolean().optional(),
157
+ min_amount: z.number().optional(),
158
+ max_amount: z.number().optional(),
159
+ total_amount: z.number().optional(),
160
+ initial_amount: z.number().optional(),
161
+ initial_amount_number: z.number().int().optional(),
162
+ apr: z.number().optional()
163
+ }).optional()
164
+ }).optional()
165
+ }).refine((data) => !(data.recurrence && data.execution_date), {
166
+ message: "Execution date must not be present when recurrence is present",
167
+ path: ["execution_date"]
168
+ });
169
+ /**
170
+ * **TS12 Login / Risk Payload**
171
+ * * @see EUDI TS12 Section 4.3.2
172
+ */
173
+ const zLoginPayload = z.object({
174
+ transaction_id: z.string().min(1).max(36),
175
+ date_time: z.iso.datetime().optional(),
176
+ service: z.string().max(100).optional(),
177
+ action: z.string().max(140).describe("Description of the action to be authorized")
178
+ });
179
+ /**
180
+ * **TS12 Account Access Payload**
181
+ * * @see EUDI TS12 Section 4.3.3
182
+ */
183
+ const zAccountAccessPayload = z.object({
184
+ transaction_id: z.string().min(1).max(36),
185
+ date_time: z.iso.datetime().optional(),
186
+ aisp: z.object({
187
+ legal_name: z.string(),
188
+ brand_name: z.string(),
189
+ domain_name: z.string()
190
+ }).optional(),
191
+ description: z.string().max(140).optional()
192
+ });
193
+ /**
194
+ * **TS12 E-Mandate Payload**
195
+ * * @see EUDI TS12 Section 4.3.4
196
+ */
197
+ const zEMandatePayload = z.object({
198
+ transaction_id: z.string().min(1).max(36),
199
+ date_time: z.iso.datetime().optional(),
200
+ start_date: z.iso.datetime().optional(),
201
+ end_date: z.iso.datetime().optional(),
202
+ reference_number: z.string().min(1).max(50).optional(),
203
+ creditor_id: z.string().min(1).max(50).optional(),
204
+ purpose: z.string().max(1e3).optional(),
205
+ payment_payload: zPaymentPayload.optional()
206
+ }).refine((data) => data.payment_payload || data.purpose, {
207
+ message: "Purpose is required if payment_payload is missing",
208
+ path: ["purpose"]
209
+ });
210
+ const URN_SCA_PAYMENT = "urn:eudi:sca:payment:1";
211
+ const URN_SCA_LOGIN_RISK = "urn:eudi:sca:login_risk_transaction:1";
212
+ const URN_SCA_ACCOUNT_ACCESS = "urn:eudi:sca:account_access:1";
213
+ const URN_SCA_EMANDATE = "urn:eudi:sca:emandate:1";
214
+ /**
215
+ * **TS12 Transaction**
216
+ * @see TS12 Section 4.3
217
+ */
218
+ const zTs12Transaction = zBaseTransaction.extend({ payload: z.union([
219
+ zPaymentPayload,
220
+ zLoginPayload,
221
+ zAccountAccessPayload,
222
+ zEMandatePayload,
223
+ z.unknown()
224
+ ]) });
225
+
226
+ //#endregion
227
+ //#region src/validation/z-transaction-data.ts
228
+ const zTransactionDataEntry = zTs12Transaction.or(zFunkeQesTransaction);
229
+ const zTransactionData = z.array(zTransactionDataEntry);
230
+ const ts12BuiltinSchemaValidators = {
231
+ [URN_SCA_PAYMENT]: zPaymentPayload,
232
+ [URN_SCA_LOGIN_RISK]: zLoginPayload,
233
+ [URN_SCA_ACCOUNT_ACCESS]: zAccountAccessPayload,
234
+ [URN_SCA_EMANDATE]: zEMandatePayload
235
+ };
236
+
237
+ //#endregion
238
+ //#region src/validation/ts12.ts
239
+ async function fetchVerified(uri, schema, integrity, validateIntegrity) {
240
+ const response = await fetch(uri);
241
+ if (!response.ok) throw new Error(`Failed to fetch URI: ${uri}`);
242
+ if (integrity && validateIntegrity && !validateIntegrity(await response.clone().arrayBuffer(), integrity)) throw new Ts12IntegrityError(uri, integrity);
243
+ return schema.parse(await response.json());
244
+ }
245
+ async function resolveTs12TransactionDisplayMetadata(metadata, type, validateIntegrity) {
246
+ if (!metadata.transaction_data_types || !metadata.transaction_data_types[type]) return;
247
+ const typeMetadata = metadata.transaction_data_types[type];
248
+ const resolved = {};
249
+ if ("schema" in typeMetadata && typeMetadata.schema) {
250
+ if (!(typeMetadata.schema in ts12BuiltinSchemaValidators)) throw new Error(`unknown builtin schema: ${typeMetadata.schema}`);
251
+ resolved.schema = typeMetadata.schema;
252
+ } else if ("schema_uri" in typeMetadata && typeMetadata.schema_uri) resolved.schema = await fetchVerified(typeMetadata.schema_uri, z.object({}), typeMetadata["schema_uri#integrity"], validateIntegrity);
253
+ else throw new Error(`Unknown schema type for ${typeMetadata}`);
254
+ if ("claims" in typeMetadata && typeMetadata.claims) resolved.claims = typeMetadata.claims;
255
+ else if ("claims_uri" in typeMetadata && typeMetadata.claims_uri) resolved.claims = await fetchVerified(typeMetadata.claims_uri, zScaTransactionDataTypeClaims, typeMetadata["claims_uri#integrity"], validateIntegrity);
256
+ else throw new Error(`Unknown claims for ${typeMetadata}`);
257
+ if ("ui_labels" in typeMetadata && typeMetadata.ui_labels) resolved.ui_labels = typeMetadata.ui_labels;
258
+ else if ("ui_labels_uri" in typeMetadata && typeMetadata.ui_labels_uri) resolved.ui_labels = await fetchVerified(typeMetadata.ui_labels_uri, zScaTransactionDataTypeUiLabels, typeMetadata["ui_labels_uri#integrity"], validateIntegrity);
259
+ else throw new Error(`Unknown ui_labels for ${typeMetadata}`);
260
+ return resolved;
261
+ }
262
+
263
+ //#endregion
264
+ //#region src/isDcqlQueryEqualOrSubset.ts
7
265
  function isDcqlQueryEqualOrSubset(arq, rcq) {
8
- if (rcq.credential_sets) {
9
- return false;
10
- }
11
- if (rcq.credentials.some((c) => c.id)) {
12
- return false;
13
- }
14
- if (arq.credentials.some((c) => c.format !== "mso_mdoc" && c.format !== "vc+sd-jwt" && c.format !== "dc+sd-jwt")) {
15
- return false;
16
- }
17
- credentialQueryLoop: for (const credentialQuery of arq.credentials) {
18
- const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format);
19
- if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false;
20
- switch (credentialQuery.format) {
21
- case "mso_mdoc": {
22
- const doctypeValue = credentialQuery.meta?.doctype_value;
23
- if (!doctypeValue) return false;
24
- if (typeof credentialQuery.meta?.doctype_value !== "string") return false;
25
- const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(
26
- (c) => !!(c.format === "mso_mdoc" && c.meta && c.meta.doctype_value === doctypeValue)
27
- );
28
- if (foundMatchingRequests.length === 0) return false;
29
- let foundFullyMatching = false;
30
- for (const matchedRequest of foundMatchingRequests) {
31
- if (!matchedRequest.claims) continue credentialQueryLoop;
32
- if (!credentialQuery.claims) continue credentialQueryLoop;
33
- const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(
34
- (c) => "path" in c && matchedRequest.claims?.some(
35
- (mrc) => "path" in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]
36
- )
37
- );
38
- if (isEveryClaimAllowedToBeRequested) {
39
- foundFullyMatching = true;
40
- }
41
- }
42
- if (!foundFullyMatching) return false;
43
- break;
44
- }
45
- case "dc+sd-jwt":
46
- case "vc+sd-jwt": {
47
- const vctValues = credentialQuery.meta?.vct_values;
48
- if (!vctValues) return false;
49
- if (credentialQuery.meta?.vct_values?.length === 0) return false;
50
- const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(
51
- (c) => !!((c.format === "dc+sd-jwt" || c.format === "vc+sd-jwt") && c.meta?.vct_values && equalsIgnoreOrder(c.meta.vct_values, vctValues))
52
- );
53
- if (foundMatchingRequests.length === 0) return false;
54
- let foundFullyMatching = false;
55
- for (const matchedRequest of foundMatchingRequests) {
56
- if (!matchedRequest.claims) continue credentialQueryLoop;
57
- if (!credentialQuery.claims) continue credentialQueryLoop;
58
- const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(
59
- (c) => "path" in c && matchedRequest.claims?.some((mrc) => "path" in mrc && equalsWithOrder(c.path, mrc.path))
60
- );
61
- if (isEveryClaimAllowedToBeRequested) {
62
- foundFullyMatching = true;
63
- }
64
- }
65
- if (!foundFullyMatching) return false;
66
- break;
67
- }
68
- default:
69
- return false;
70
- }
71
- }
72
- return true;
266
+ if (rcq.credential_sets) return false;
267
+ if (rcq.credentials.some((c) => c.id)) return false;
268
+ if (arq.credentials.some((c) => c.format !== "mso_mdoc" && c.format !== "vc+sd-jwt" && c.format !== "dc+sd-jwt")) return false;
269
+ credentialQueryLoop: for (const credentialQuery of arq.credentials) {
270
+ const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format);
271
+ if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false;
272
+ switch (credentialQuery.format) {
273
+ case "mso_mdoc": {
274
+ const doctypeValue = credentialQuery.meta?.doctype_value;
275
+ if (!doctypeValue) return false;
276
+ if (typeof credentialQuery.meta?.doctype_value !== "string") return false;
277
+ const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter((c) => !!(c.format === "mso_mdoc" && c.meta && c.meta.doctype_value === doctypeValue));
278
+ if (foundMatchingRequests.length === 0) return false;
279
+ let foundFullyMatching = false;
280
+ for (const matchedRequest of foundMatchingRequests) {
281
+ if (!matchedRequest.claims) continue credentialQueryLoop;
282
+ if (!credentialQuery.claims) continue credentialQueryLoop;
283
+ if (credentialQuery.claims.every((c) => "path" in c && matchedRequest.claims?.some((mrc) => "path" in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]))) foundFullyMatching = true;
284
+ }
285
+ if (!foundFullyMatching) return false;
286
+ break;
287
+ }
288
+ case "dc+sd-jwt": {
289
+ const vctValues = credentialQuery.meta?.vct_values;
290
+ if (!vctValues || vctValues.length === 0) return false;
291
+ const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter((c) => !!(c.format === "dc+sd-jwt" && c.meta?.vct_values && equalsIgnoreOrder(c.meta.vct_values, vctValues)));
292
+ if (foundMatchingRequests.length === 0) return false;
293
+ let foundFullyMatching = false;
294
+ for (const matchedRequest of foundMatchingRequests) {
295
+ if (!matchedRequest.claims) continue credentialQueryLoop;
296
+ if (!credentialQuery.claims) continue credentialQueryLoop;
297
+ if (credentialQuery.claims.every((c) => "path" in c && matchedRequest.claims?.some((mrc) => "path" in mrc && equalsWithOrder(c.path, mrc.path)))) foundFullyMatching = true;
298
+ }
299
+ if (!foundFullyMatching) return false;
300
+ break;
301
+ }
302
+ default: return false;
303
+ }
304
+ }
305
+ return true;
73
306
  }
74
307
 
75
- // src/verifyOpenid4VpAuthorizationRequest.ts
76
- var verifyOpenid4VpAuthorizationRequest = async (agentContext, {
77
- resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql },
78
- trustedCertificates,
79
- allowUntrustedSigned
80
- }) => {
81
- const results = [];
82
- if (!authorizationRequestPayload.verifier_attestations) return;
83
- for (const va of authorizationRequestPayload.verifier_attestations) {
84
- if (va.format === "jwt") {
85
- if (typeof va.data !== "string") {
86
- throw new Error("Only inline JWTs are supported");
87
- }
88
- const jwsService = agentContext.dependencyManager.resolve(JwsService);
89
- let isValidButUntrusted = false;
90
- let isValidAndTrusted = false;
91
- const jwt = Jwt.fromSerializedJwt(va.data);
92
- try {
93
- const { isValid } = await jwsService.verifyJws(agentContext, {
94
- jws: va.data,
95
- trustedCertificates
96
- });
97
- isValidAndTrusted = isValid;
98
- } catch {
99
- if (allowUntrustedSigned) {
100
- const { isValid } = await jwsService.verifyJws(agentContext, {
101
- jws: va.data,
102
- trustedCertificates: jwt.header.x5c ?? []
103
- });
104
- isValidButUntrusted = isValid;
105
- }
106
- }
107
- if (jwt.header.typ !== "rc-rp+jwt") {
108
- throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`);
109
- }
110
- if (!signedAuthorizationRequest) {
111
- throw new Error("Request must be signed for the registration certificate");
112
- }
113
- if (signedAuthorizationRequest.signer.method !== "x5c") {
114
- throw new Error("x5c is only supported for registration certificate");
115
- }
116
- const registrationCertificateHeaderSchema = z.object({
117
- typ: z.literal("rc-rp+jwt"),
118
- alg: z.string(),
119
- // sprin-d did not define this
120
- x5u: z.string().url().optional(),
121
- // sprin-d did not define this
122
- "x5t#s256": z.string().optional()
123
- }).passthrough();
124
- const registrationCertificatePayloadSchema = z.object({
125
- credentials: z.array(
126
- z.object({
127
- format: z.string(),
128
- multiple: z.boolean().default(false),
129
- meta: z.object({
130
- vct_values: z.array(z.string()).optional(),
131
- doctype_value: z.string().optional()
132
- }).optional(),
133
- trusted_authorities: z.array(z.object({ type: z.string(), values: z.array(z.string()) })).nonempty().optional(),
134
- require_cryptographic_holder_binding: z.boolean().default(true),
135
- claims: z.array(
136
- z.object({
137
- id: z.string().optional(),
138
- path: z.array(z.string()).nonempty().nonempty(),
139
- values: z.array(z.number().or(z.boolean())).optional()
140
- })
141
- ).nonempty().optional(),
142
- claim_sets: z.array(z.array(z.string())).nonempty().optional()
143
- })
144
- ),
145
- contact: z.object({
146
- website: z.string().url(),
147
- "e-mail": z.string().email(),
148
- phone: z.string()
149
- }),
150
- sub: z.string(),
151
- // Should be service
152
- services: z.array(z.object({ lang: z.string(), name: z.string() })),
153
- public_body: z.boolean().default(false),
154
- entitlements: z.array(z.any()),
155
- provided_attestations: z.array(
156
- z.object({
157
- format: z.string(),
158
- meta: z.any()
159
- })
160
- ).optional(),
161
- privacy_policy: z.string().url(),
162
- iat: z.number().optional(),
163
- exp: z.number().optional(),
164
- purpose: z.array(
165
- z.object({
166
- locale: z.string().optional(),
167
- lang: z.string().optional(),
168
- name: z.string()
169
- })
170
- ).optional(),
171
- status: z.any()
172
- }).passthrough();
173
- registrationCertificateHeaderSchema.parse(jwt.header);
174
- const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson());
175
- const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c;
176
- const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded);
177
- if (rpCert.subject !== parsedPayload.sub) {
178
- throw new Error(
179
- `Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`
180
- );
181
- }
182
- if (parsedPayload.iat && (/* @__PURE__ */ new Date()).getTime() / 1e3 <= parsedPayload.iat) {
183
- throw new Error("Issued at timestamp of the registration certificate is in the future");
184
- }
185
- if (!dcql) {
186
- throw new Error("DCQL must be used when working registration certificates");
187
- }
188
- if (authorizationRequestPayload.presentation_definition || authorizationRequestPayload.presentation_definition_uri) {
189
- throw new Error("Presentation Exchange is not supported for the registration certificate");
190
- }
191
- const isValidDcqlQuery = isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload);
192
- if (!isValidDcqlQuery) {
193
- throw new Error(
194
- "DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate"
195
- );
196
- }
197
- results.push({ isValidButUntrusted, isValidAndTrusted, x509RegistrationCertificate: rpCert });
198
- } else {
199
- throw new Error(`only format of 'jwt' is supported`);
200
- }
201
- }
202
- return results;
203
- };
204
- export {
205
- verifyOpenid4VpAuthorizationRequest
308
+ //#endregion
309
+ //#region src/verifyOpenid4VpAuthorizationRequest.ts
310
+ const verifyOpenid4VpAuthorizationRequest = async (agentContext, { resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql }, trustedCertificates, allowUntrustedSigned }) => {
311
+ const results = [];
312
+ if (!authorizationRequestPayload.verifier_attestations) return;
313
+ for (const va of authorizationRequestPayload.verifier_attestations) if (va.format === "jwt") {
314
+ if (typeof va.data !== "string") throw new Error("Only inline JWTs are supported");
315
+ const jwsService = agentContext.dependencyManager.resolve(JwsService);
316
+ let isValidButUntrusted = false;
317
+ let isValidAndTrusted = false;
318
+ const jwt = Jwt.fromSerializedJwt(va.data);
319
+ try {
320
+ const { isValid } = await jwsService.verifyJws(agentContext, {
321
+ jws: va.data,
322
+ trustedCertificates
323
+ });
324
+ isValidAndTrusted = isValid;
325
+ } catch {
326
+ if (allowUntrustedSigned) {
327
+ const { isValid } = await jwsService.verifyJws(agentContext, {
328
+ jws: va.data,
329
+ trustedCertificates: jwt.header.x5c ?? []
330
+ });
331
+ isValidButUntrusted = isValid;
332
+ }
333
+ }
334
+ if (jwt.header.typ !== "rc-rp+jwt") throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`);
335
+ if (!signedAuthorizationRequest) throw new Error("Request must be signed for the registration certificate");
336
+ if (signedAuthorizationRequest.signer.method !== "x5c") throw new Error("x5c is only supported for registration certificate");
337
+ const registrationCertificateHeaderSchema = z$1.object({
338
+ typ: z$1.literal("rc-rp+jwt"),
339
+ alg: z$1.string(),
340
+ x5u: z$1.string().url().optional(),
341
+ "x5t#s256": z$1.string().optional()
342
+ }).passthrough();
343
+ const registrationCertificatePayloadSchema = z$1.object({
344
+ credentials: z$1.array(z$1.object({
345
+ format: z$1.string(),
346
+ multiple: z$1.boolean().default(false),
347
+ meta: z$1.object({
348
+ vct_values: z$1.array(z$1.string()).optional(),
349
+ doctype_value: z$1.string().optional()
350
+ }).optional(),
351
+ trusted_authorities: z$1.array(z$1.object({
352
+ type: z$1.string(),
353
+ values: z$1.array(z$1.string())
354
+ })).nonempty().optional(),
355
+ require_cryptographic_holder_binding: z$1.boolean().default(true),
356
+ claims: z$1.array(z$1.object({
357
+ id: z$1.string().optional(),
358
+ path: z$1.array(z$1.string()).nonempty().nonempty(),
359
+ values: z$1.array(z$1.number().or(z$1.boolean())).optional()
360
+ })).nonempty().optional(),
361
+ claim_sets: z$1.array(z$1.array(z$1.string())).nonempty().optional()
362
+ })),
363
+ contact: z$1.object({
364
+ website: z$1.string().url(),
365
+ "e-mail": z$1.string().email(),
366
+ phone: z$1.string()
367
+ }),
368
+ sub: z$1.string(),
369
+ services: z$1.array(z$1.object({
370
+ lang: z$1.string(),
371
+ name: z$1.string()
372
+ })),
373
+ public_body: z$1.boolean().default(false),
374
+ entitlements: z$1.array(z$1.any()),
375
+ provided_attestations: z$1.array(z$1.object({
376
+ format: z$1.string(),
377
+ meta: z$1.any()
378
+ })).optional(),
379
+ privacy_policy: z$1.string().url(),
380
+ iat: z$1.number().optional(),
381
+ exp: z$1.number().optional(),
382
+ purpose: z$1.array(z$1.object({
383
+ locale: z$1.string().optional(),
384
+ lang: z$1.string().optional(),
385
+ name: z$1.string()
386
+ })).optional(),
387
+ status: z$1.any()
388
+ }).passthrough();
389
+ registrationCertificateHeaderSchema.parse(jwt.header);
390
+ const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson());
391
+ const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c;
392
+ const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded);
393
+ if (rpCert.subject !== parsedPayload.sub) throw new Error(`Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`);
394
+ if (parsedPayload.iat && Date.now() / 1e3 <= parsedPayload.iat) throw new Error("Issued at timestamp of the registration certificate is in the future");
395
+ if (!dcql) throw new Error("DCQL must be used when working registration certificates");
396
+ if (authorizationRequestPayload.presentation_definition || authorizationRequestPayload.presentation_definition_uri) throw new Error("Presentation Exchange is not supported for the registration certificate");
397
+ if (!isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload)) throw new Error("DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate");
398
+ results.push({
399
+ isValidButUntrusted,
400
+ isValidAndTrusted,
401
+ x509RegistrationCertificate: rpCert
402
+ });
403
+ } else throw new Error(`only format of 'jwt' is supported`);
404
+ return results;
206
405
  };
406
+
407
+ //#endregion
408
+ export { EudiWalletExtensionsError, Ts12IntegrityError, URN_SCA_ACCOUNT_ACCESS, URN_SCA_EMANDATE, URN_SCA_LOGIN_RISK, URN_SCA_PAYMENT, resolveTs12TransactionDisplayMetadata, ts12BuiltinSchemaValidators, verifyOpenid4VpAuthorizationRequest, zAccountAccessPayload, zEMandatePayload, zFunkeQesTransaction, zLoginPayload, zPaymentPayload, zScaAttestationExt, zScaTransactionDataTypeClaims, zScaTransactionDataTypeUiLabels, zTransactionData, zTransactionDataEntry, zTs12Transaction };
207
409
  //# sourceMappingURL=index.mjs.map