@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.d.mts +858 -11
- package/dist/index.mjs +403 -201
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -20
- package/dist/index.d.ts +0 -15
- package/dist/index.js +0 -244
- package/dist/index.js.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,207 +1,409 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|