@pagopa/io-react-native-wallet 2.1.0 → 2.2.0
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/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +82 -58
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/index.js +3 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/mdoc/index.js +15 -0
- package/lib/commonjs/mdoc/index.js.map +1 -1
- package/lib/commonjs/mdoc/utils.js +37 -1
- package/lib/commonjs/mdoc/utils.js.map +1 -1
- package/lib/commonjs/trust/build-chain.js +22 -19
- package/lib/commonjs/trust/build-chain.js.map +1 -1
- package/lib/commonjs/utils/nestedProperty.js +21 -10
- package/lib/commonjs/utils/nestedProperty.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +83 -59
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/mdoc/index.js +1 -0
- package/lib/module/mdoc/index.js.map +1 -1
- package/lib/module/mdoc/utils.js +35 -0
- package/lib/module/mdoc/utils.js.map +1 -1
- package/lib/module/trust/build-chain.js +22 -19
- package/lib/module/trust/build-chain.js.map +1 -1
- package/lib/module/utils/nestedProperty.js +21 -10
- package/lib/module/utils/nestedProperty.js.map +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/mdoc/index.d.ts +1 -0
- package/lib/typescript/mdoc/index.d.ts.map +1 -1
- package/lib/typescript/mdoc/utils.d.ts +50 -0
- package/lib/typescript/mdoc/utils.d.ts.map +1 -1
- package/lib/typescript/trust/build-chain.d.ts +2 -3
- package/lib/typescript/trust/build-chain.d.ts.map +1 -1
- package/lib/typescript/utils/nestedProperty.d.ts +2 -1
- package/lib/typescript/utils/nestedProperty.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/07-verify-and-parse-credential.ts +60 -26
- package/src/index.ts +2 -0
- package/src/mdoc/index.ts +1 -0
- package/src/mdoc/utils.ts +43 -0
- package/src/trust/build-chain.ts +28 -25
- package/src/utils/nestedProperty.ts +35 -10
package/src/trust/build-chain.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import type { JWK } from "../utils/jwk";
|
2
1
|
import {
|
3
2
|
BuildTrustChainError,
|
4
3
|
FederationListParseError,
|
@@ -266,39 +265,27 @@ export async function getFederationList(
|
|
266
265
|
* Build a not-verified trust chain for a given Relying Party (RP) entity.
|
267
266
|
*
|
268
267
|
* @param relyingPartyEntityBaseUrl The base URL of the RP entity
|
269
|
-
* @param
|
268
|
+
* @param trustAnchorConfig The entity configuration of the known trust anchor.
|
270
269
|
* @param appFetch An optional instance of the http client to be used.
|
271
270
|
* @returns A list of signed tokens that represent the trust chain, in the order of the chain (from the RP to the Trust Anchor)
|
272
271
|
* @throws {FederationError} When an element of the chain fails to parse or other build steps fail.
|
273
272
|
*/
|
274
273
|
export async function buildTrustChain(
|
275
274
|
relyingPartyEntityBaseUrl: string,
|
276
|
-
|
275
|
+
trustAnchorConfig: TrustAnchorEntityConfiguration,
|
277
276
|
appFetch: GlobalFetch["fetch"] = fetch
|
278
277
|
): Promise<string[]> {
|
279
|
-
// 1:
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
);
|
278
|
+
// 1: Verify if the RP is authorized by the Trust Anchor's federation list
|
279
|
+
// Extract the Trust Anchor's signing key and federation_list_endpoint
|
280
|
+
// (we assume the TA has only one key, as per spec)
|
281
|
+
const trustAnchorKey = trustAnchorConfig.payload.jwks.keys[0];
|
284
282
|
|
285
|
-
|
286
|
-
const trustAnchorJwt = trustChain[trustChain.length - 1];
|
287
|
-
if (!trustAnchorJwt) {
|
283
|
+
if (!trustAnchorKey) {
|
288
284
|
throw new BuildTrustChainError(
|
289
|
-
"Cannot verify trust anchor: missing
|
290
|
-
{ relyingPartyUrl: relyingPartyEntityBaseUrl }
|
285
|
+
"Cannot verify trust anchor: missing signing key in entity configuration."
|
291
286
|
);
|
292
287
|
}
|
293
288
|
|
294
|
-
if (!trustAnchorKey.kid) {
|
295
|
-
throw new TrustAnchorKidMissingError();
|
296
|
-
}
|
297
|
-
|
298
|
-
await verify(trustAnchorJwt, trustAnchorKey.kid, [trustAnchorKey]);
|
299
|
-
|
300
|
-
// 3: Check the federation list
|
301
|
-
const trustAnchorConfig = EntityConfiguration.parse(decode(trustAnchorJwt));
|
302
289
|
const federationListEndpoint =
|
303
290
|
trustAnchorConfig.payload.metadata.federation_entity
|
304
291
|
.federation_list_endpoint;
|
@@ -316,6 +303,26 @@ export async function buildTrustChain(
|
|
316
303
|
}
|
317
304
|
}
|
318
305
|
|
306
|
+
// 1: Recursively gather the trust chain from the RP up to the Trust Anchor
|
307
|
+
const trustChain = await gatherTrustChain(
|
308
|
+
relyingPartyEntityBaseUrl,
|
309
|
+
appFetch
|
310
|
+
);
|
311
|
+
// 2: Trust Anchor signature verification
|
312
|
+
const chainTrustAnchorJwt = trustChain[trustChain.length - 1];
|
313
|
+
if (!chainTrustAnchorJwt) {
|
314
|
+
throw new BuildTrustChainError(
|
315
|
+
"Cannot verify trust anchor: missing entity configuration in gathered chain.",
|
316
|
+
{ relyingPartyUrl: relyingPartyEntityBaseUrl }
|
317
|
+
);
|
318
|
+
}
|
319
|
+
|
320
|
+
if (!trustAnchorKey.kid) {
|
321
|
+
throw new TrustAnchorKidMissingError();
|
322
|
+
}
|
323
|
+
|
324
|
+
await verify(chainTrustAnchorJwt, trustAnchorKey.kid, [trustAnchorKey]);
|
325
|
+
|
319
326
|
return trustChain;
|
320
327
|
}
|
321
328
|
|
@@ -339,7 +346,6 @@ async function gatherTrustChain(
|
|
339
346
|
appFetch,
|
340
347
|
});
|
341
348
|
const entityEC = EntityConfiguration.parse(decode(entityECJwt));
|
342
|
-
|
343
349
|
if (isLeaf) {
|
344
350
|
// Only push EC for the leaf
|
345
351
|
chain.push(entityECJwt);
|
@@ -354,7 +360,6 @@ async function gatherTrustChain(
|
|
354
360
|
}
|
355
361
|
return chain;
|
356
362
|
}
|
357
|
-
|
358
363
|
const parentEntityBaseUrl = authorityHints[0]!;
|
359
364
|
|
360
365
|
// Fetch parent EC
|
@@ -362,7 +367,6 @@ async function gatherTrustChain(
|
|
362
367
|
appFetch,
|
363
368
|
});
|
364
369
|
const parentEC = EntityConfiguration.parse(decode(parentECJwt));
|
365
|
-
|
366
370
|
// Fetch ES
|
367
371
|
const federationFetchEndpoint =
|
368
372
|
parentEC.payload.metadata.federation_entity.federation_fetch_endpoint;
|
@@ -372,7 +376,6 @@ async function gatherTrustChain(
|
|
372
376
|
{ entityBaseUrl, missingInEntityUrl: parentEntityBaseUrl }
|
373
377
|
);
|
374
378
|
}
|
375
|
-
|
376
379
|
const entityStatementJwt = await getSignedEntityStatement(
|
377
380
|
federationFetchEndpoint,
|
378
381
|
entityBaseUrl,
|
@@ -25,7 +25,7 @@ const buildName = (display: DisplayData): LocalizedNames =>
|
|
25
25
|
{}
|
26
26
|
);
|
27
27
|
|
28
|
-
// Handles the case where the path key is `null`
|
28
|
+
// Handles the case where the path key is `null` (indicating an array)
|
29
29
|
const handleNullKeyCase = (
|
30
30
|
currentObject: NodeOrStructure,
|
31
31
|
rest: Path,
|
@@ -39,7 +39,15 @@ const handleNullKeyCase = (
|
|
39
39
|
const existingValue = Array.isArray(node.value) ? node.value : [];
|
40
40
|
|
41
41
|
const mappedArray = sourceValue.map((item, idx) =>
|
42
|
-
|
42
|
+
// When mapping over an array, recursively call with `skipMissingLeaves` set to `true`.
|
43
|
+
// This tells the function to skip optional keys inside these array objects.
|
44
|
+
createNestedProperty(
|
45
|
+
existingValue[idx] || {},
|
46
|
+
rest,
|
47
|
+
item,
|
48
|
+
displayData,
|
49
|
+
true
|
50
|
+
)
|
43
51
|
);
|
44
52
|
|
45
53
|
return {
|
@@ -55,7 +63,8 @@ const handleStringKeyCase = (
|
|
55
63
|
key: string,
|
56
64
|
rest: Path,
|
57
65
|
sourceValue: unknown,
|
58
|
-
displayData: DisplayData
|
66
|
+
displayData: DisplayData,
|
67
|
+
skipMissingLeaves: boolean
|
59
68
|
): NodeOrStructure => {
|
60
69
|
let nextSourceValue = sourceValue;
|
61
70
|
const isLeaf = rest.length === 0;
|
@@ -73,7 +82,13 @@ const handleStringKeyCase = (
|
|
73
82
|
|
74
83
|
// Skip processing when the key is not found within the claim object
|
75
84
|
if (!(key in sourceValue)) {
|
76
|
-
//
|
85
|
+
// If the flag is set (we're inside an array), skip the missing key completely.
|
86
|
+
if (skipMissingLeaves) {
|
87
|
+
return currentObject;
|
88
|
+
}
|
89
|
+
|
90
|
+
// If the flag is NOT set, create the empty placeholder
|
91
|
+
// so that its children can be attached later.
|
77
92
|
if (isLeaf) {
|
78
93
|
return {
|
79
94
|
...currentObject,
|
@@ -101,7 +116,13 @@ const handleStringKeyCase = (
|
|
101
116
|
|
102
117
|
return {
|
103
118
|
...currentObject,
|
104
|
-
[key]: createNestedProperty(
|
119
|
+
[key]: createNestedProperty(
|
120
|
+
nextObject,
|
121
|
+
rest,
|
122
|
+
nextSourceValue,
|
123
|
+
displayData,
|
124
|
+
skipMissingLeaves
|
125
|
+
),
|
105
126
|
};
|
106
127
|
};
|
107
128
|
|
@@ -132,13 +153,15 @@ const handleNumberKeyCase = (
|
|
132
153
|
* @param path - The path segments to follow.
|
133
154
|
* @param sourceValue - The raw value to place at the end of the path.
|
134
155
|
* @param displayData - The data for generating localized names.
|
156
|
+
* @param skipMissingLeaves - If true, skips optional keys when mapping over arrays.
|
135
157
|
* @returns The new object or array structure.
|
136
158
|
*/
|
137
159
|
export const createNestedProperty = (
|
138
160
|
currentObject: NodeOrStructure,
|
139
161
|
path: Path,
|
140
|
-
sourceValue: unknown,
|
141
|
-
displayData: DisplayData
|
162
|
+
sourceValue: unknown,
|
163
|
+
displayData: DisplayData,
|
164
|
+
skipMissingLeaves: boolean = false
|
142
165
|
): NodeOrStructure => {
|
143
166
|
const [key, ...rest] = path;
|
144
167
|
|
@@ -152,7 +175,8 @@ export const createNestedProperty = (
|
|
152
175
|
key as string,
|
153
176
|
rest,
|
154
177
|
sourceValue,
|
155
|
-
displayData
|
178
|
+
displayData,
|
179
|
+
skipMissingLeaves
|
156
180
|
);
|
157
181
|
|
158
182
|
case typeof key === "number":
|
@@ -178,11 +202,12 @@ const handleRestKey = (
|
|
178
202
|
displayData: DisplayData
|
179
203
|
): NodeOrStructure => {
|
180
204
|
const currentNode = currentObject[key] ?? {};
|
181
|
-
// Take the first key in the remaining path
|
182
205
|
const restKey = rest[0] as string;
|
183
206
|
const nextSourceValue = sourceValue[restKey];
|
207
|
+
if (typeof nextSourceValue === "undefined") {
|
208
|
+
return currentObject;
|
209
|
+
}
|
184
210
|
|
185
|
-
// Merge the current node with the updated nested property for the remaining path.
|
186
211
|
return {
|
187
212
|
...currentObject,
|
188
213
|
[key]: {
|