@pagopa/io-react-native-wallet 2.1.1 → 2.3.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/README.md +4 -3
- package/lib/commonjs/credential/index.js +3 -1
- package/lib/commonjs/credential/index.js.map +1 -1
- 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/credential/offer/01-start-flow.js +75 -0
- package/lib/commonjs/credential/offer/01-start-flow.js.map +1 -0
- package/lib/commonjs/credential/offer/02-fetch-credential-offer.js +45 -0
- package/lib/commonjs/credential/offer/02-fetch-credential-offer.js.map +1 -0
- package/lib/commonjs/credential/offer/README.md +174 -0
- package/lib/commonjs/credential/offer/errors.js +22 -0
- package/lib/commonjs/credential/offer/errors.js.map +1 -0
- package/lib/commonjs/credential/offer/index.js +25 -0
- package/lib/commonjs/credential/offer/index.js.map +1 -0
- package/lib/commonjs/credential/offer/types.js +51 -0
- package/lib/commonjs/credential/offer/types.js.map +1 -0
- package/lib/commonjs/credential/presentation/01-start-flow.js +1 -1
- package/lib/commonjs/credentials-catalogue/README.md +15 -0
- package/lib/commonjs/credentials-catalogue/fetch-and-parse-catalogue.js +42 -0
- package/lib/commonjs/credentials-catalogue/fetch-and-parse-catalogue.js.map +1 -0
- package/lib/commonjs/credentials-catalogue/index.js +13 -0
- package/lib/commonjs/credentials-catalogue/index.js.map +1 -0
- package/lib/commonjs/credentials-catalogue/types.js +99 -0
- package/lib/commonjs/credentials-catalogue/types.js.map +1 -0
- package/lib/commonjs/index.js +5 -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/utils/nestedProperty.js +21 -10
- package/lib/commonjs/utils/nestedProperty.js.map +1 -1
- package/lib/commonjs/utils/zod.js +28 -0
- package/lib/commonjs/utils/zod.js.map +1 -0
- package/lib/module/credential/index.js +2 -1
- package/lib/module/credential/index.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/credential/offer/01-start-flow.js +66 -0
- package/lib/module/credential/offer/01-start-flow.js.map +1 -0
- package/lib/module/credential/offer/02-fetch-credential-offer.js +38 -0
- package/lib/module/credential/offer/02-fetch-credential-offer.js.map +1 -0
- package/lib/module/credential/offer/README.md +174 -0
- package/lib/module/credential/offer/errors.js +14 -0
- package/lib/module/credential/offer/errors.js.map +1 -0
- package/lib/module/credential/offer/index.js +5 -0
- package/lib/module/credential/offer/index.js.map +1 -0
- package/lib/module/credential/offer/types.js +41 -0
- package/lib/module/credential/offer/types.js.map +1 -0
- package/lib/module/credential/presentation/01-start-flow.js +1 -1
- package/lib/module/credentials-catalogue/README.md +15 -0
- package/lib/module/credentials-catalogue/fetch-and-parse-catalogue.js +35 -0
- package/lib/module/credentials-catalogue/fetch-and-parse-catalogue.js.map +1 -0
- package/lib/module/credentials-catalogue/index.js +2 -0
- package/lib/module/credentials-catalogue/index.js.map +1 -0
- package/lib/module/credentials-catalogue/types.js +89 -0
- package/lib/module/credentials-catalogue/types.js.map +1 -0
- package/lib/module/index.js +3 -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/utils/nestedProperty.js +21 -10
- package/lib/module/utils/nestedProperty.js.map +1 -1
- package/lib/module/utils/zod.js +20 -0
- package/lib/module/utils/zod.js.map +1 -0
- package/lib/typescript/credential/index.d.ts +2 -1
- package/lib/typescript/credential/index.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/01-start-flow.d.ts +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/offer/01-start-flow.d.ts +172 -0
- package/lib/typescript/credential/offer/01-start-flow.d.ts.map +1 -0
- package/lib/typescript/credential/offer/02-fetch-credential-offer.d.ts +20 -0
- package/lib/typescript/credential/offer/02-fetch-credential-offer.d.ts.map +1 -0
- package/lib/typescript/credential/offer/errors.d.ts +10 -0
- package/lib/typescript/credential/offer/errors.d.ts.map +1 -0
- package/lib/typescript/credential/offer/index.d.ts +7 -0
- package/lib/typescript/credential/offer/index.d.ts.map +1 -0
- package/lib/typescript/credential/offer/types.d.ts +264 -0
- package/lib/typescript/credential/offer/types.d.ts.map +1 -0
- package/lib/typescript/credential/presentation/01-start-flow.d.ts +1 -1
- package/lib/typescript/credentials-catalogue/fetch-and-parse-catalogue.d.ts +15 -0
- package/lib/typescript/credentials-catalogue/fetch-and-parse-catalogue.d.ts.map +1 -0
- package/lib/typescript/credentials-catalogue/index.d.ts +3 -0
- package/lib/typescript/credentials-catalogue/index.d.ts.map +1 -0
- package/lib/typescript/credentials-catalogue/types.d.ts +844 -0
- package/lib/typescript/credentials-catalogue/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +3 -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/utils/nestedProperty.d.ts +2 -1
- package/lib/typescript/utils/nestedProperty.d.ts.map +1 -1
- package/lib/typescript/utils/zod.d.ts +15 -0
- package/lib/typescript/utils/zod.d.ts.map +1 -0
- package/package.json +21 -2
- package/src/credential/index.ts +2 -1
- package/src/credential/issuance/01-start-flow.ts +1 -1
- package/src/credential/issuance/07-verify-and-parse-credential.ts +60 -26
- package/src/credential/offer/01-start-flow.ts +89 -0
- package/src/credential/offer/02-fetch-credential-offer.ts +54 -0
- package/src/credential/offer/README.md +174 -0
- package/src/credential/offer/errors.ts +17 -0
- package/src/credential/offer/index.ts +16 -0
- package/src/credential/offer/types.ts +59 -0
- package/src/credential/presentation/01-start-flow.ts +1 -1
- package/src/credentials-catalogue/README.md +15 -0
- package/src/credentials-catalogue/fetch-and-parse-catalogue.ts +54 -0
- package/src/credentials-catalogue/index.ts +2 -0
- package/src/credentials-catalogue/types.ts +97 -0
- package/src/index.ts +4 -0
- package/src/mdoc/index.ts +1 -0
- package/src/mdoc/utils.ts +43 -0
- package/src/utils/nestedProperty.ts +35 -10
- package/src/utils/zod.ts +28 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IoWalletError } from "../../utils/errors";
|
|
2
|
+
|
|
3
|
+
export class InvalidCredentialOfferError extends IoWalletError {
|
|
4
|
+
code = "ERR_INVALID_CREDENTIAL_OFFER";
|
|
5
|
+
|
|
6
|
+
constructor(message?: string) {
|
|
7
|
+
super(message);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class InvalidQRCodeError extends IoWalletError {
|
|
12
|
+
code = "ERR_INVALID_QR_CODE";
|
|
13
|
+
|
|
14
|
+
constructor(message?: string) {
|
|
15
|
+
super(message);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { startFlowFromQR, type StartFlow } from "./01-start-flow";
|
|
2
|
+
import {
|
|
3
|
+
fetchCredentialOffer,
|
|
4
|
+
type GetCredentialOffer,
|
|
5
|
+
} from "./02-fetch-credential-offer";
|
|
6
|
+
import * as Errors from "./errors";
|
|
7
|
+
export type {
|
|
8
|
+
CredentialOffer,
|
|
9
|
+
Grants,
|
|
10
|
+
AuthorizationCodeGrant,
|
|
11
|
+
PreAuthorizedCodeGrant,
|
|
12
|
+
TransactionCode,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
export { Errors, fetchCredentialOffer, startFlowFromQR };
|
|
16
|
+
export type { GetCredentialOffer, StartFlow };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OAuth 2.0 Authorization Code flow parameters.
|
|
5
|
+
*/
|
|
6
|
+
export const AuthorizationCodeGrantSchema = z.object({
|
|
7
|
+
issuer_state: z.string().optional(),
|
|
8
|
+
authorization_server: z.string().url().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export type AuthorizationCodeGrant = z.infer<
|
|
12
|
+
typeof AuthorizationCodeGrantSchema
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Transaction Code requirements for Pre-Authorized Code flow.
|
|
17
|
+
*/
|
|
18
|
+
export const TransactionCodeSchema = z.object({
|
|
19
|
+
input_mode: z.enum(["numeric", "text"]).optional(),
|
|
20
|
+
length: z.number().int().positive().optional(),
|
|
21
|
+
description: z.string().max(300).optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type TransactionCode = z.infer<typeof TransactionCodeSchema>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pre-Authorized Code flow parameters.
|
|
28
|
+
*/
|
|
29
|
+
export const PreAuthorizedCodeGrantSchema = z.object({
|
|
30
|
+
"pre-authorized_code": z.string(),
|
|
31
|
+
tx_code: TransactionCodeSchema.optional(),
|
|
32
|
+
authorization_server: z.string().url().optional(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export type PreAuthorizedCodeGrant = z.infer<
|
|
36
|
+
typeof PreAuthorizedCodeGrantSchema
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Supported grant types for Credential Offer.
|
|
41
|
+
*/
|
|
42
|
+
export const GrantsSchema = z.object({
|
|
43
|
+
authorization_code: AuthorizationCodeGrantSchema.optional(),
|
|
44
|
+
"urn:ietf:params:oauth:grant-type:pre-authorized_code":
|
|
45
|
+
PreAuthorizedCodeGrantSchema.optional(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export type Grants = z.infer<typeof GrantsSchema>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Credential Offer object as defined in OpenID4VCI Section 4.1.1.
|
|
52
|
+
*/
|
|
53
|
+
export const CredentialOfferSchema = z.object({
|
|
54
|
+
credential_issuer: z.string().url(),
|
|
55
|
+
credential_configuration_ids: z.array(z.string()).min(1),
|
|
56
|
+
grants: GrantsSchema.optional(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export type CredentialOffer = z.infer<typeof CredentialOfferSchema>;
|
|
@@ -11,7 +11,7 @@ export type PresentationParams = z.infer<typeof PresentationParams>;
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The beginning of the presentation flow.
|
|
14
|
-
* To be implemented
|
|
14
|
+
* To be implemented according to the user touchpoint
|
|
15
15
|
*
|
|
16
16
|
* @param params Presentation parameters, depending on the starting touchpoint
|
|
17
17
|
* @returns The url for the Relying Party to connect with
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Digital Credentials Catalogue
|
|
2
|
+
|
|
3
|
+
Module that manages the [**Digital Credentials Catalogue**](https://italia.github.io/eid-wallet-it-docs/releases/1.1.0/en/registry-catalogue.html) published by the Trust Anchor.
|
|
4
|
+
|
|
5
|
+
The module allows:
|
|
6
|
+
- Fetching, verifying and parsing the catalogue's JWT.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
// Fetch the catalogue
|
|
12
|
+
const TRUST_ANCHOR_BASE_URL = "https://pre.ta.wallet.ipzs.it";
|
|
13
|
+
const credentialsCatalogue =
|
|
14
|
+
await CredentialsCatalogue.fetchAndParseCatalogue(TRUST_ANCHOR_BASE_URL);
|
|
15
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { decode as decodeJwt, verify } from "@pagopa/io-react-native-jwt";
|
|
2
|
+
import { hasStatusOrThrow } from "../utils/misc";
|
|
3
|
+
import { IoWalletError } from "../utils/errors";
|
|
4
|
+
import { DigitalCredentialsCatalogue } from "./types";
|
|
5
|
+
import { getTrustAnchorEntityConfiguration } from "../trust/build-chain";
|
|
6
|
+
|
|
7
|
+
type GetCatalogueContext = {
|
|
8
|
+
appFetch?: GlobalFetch["fetch"];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fetch and parse the Digital Credential Catalogue from the Trust Anchor.
|
|
13
|
+
* The catalogue's JWT signature is verified against the Trust Anchor's JWKs.
|
|
14
|
+
*
|
|
15
|
+
* @param trustAnchorUrl Base URL of the Trust Anchor
|
|
16
|
+
* @param context.appFetch (optional) fetch API implementation. Default: built-in fetch
|
|
17
|
+
* @returns The Digital Credential Catalogue payload
|
|
18
|
+
*/
|
|
19
|
+
export const fetchAndParseCatalogue = async (
|
|
20
|
+
trustAnchorBaseUrl: string,
|
|
21
|
+
{ appFetch = fetch }: GetCatalogueContext = {}
|
|
22
|
+
): Promise<DigitalCredentialsCatalogue["payload"]> => {
|
|
23
|
+
const trustAnchorConfig =
|
|
24
|
+
await getTrustAnchorEntityConfiguration(trustAnchorBaseUrl);
|
|
25
|
+
|
|
26
|
+
const responseText = await appFetch(
|
|
27
|
+
`${trustAnchorConfig.payload.sub}/.well-known/credential-catalogue`,
|
|
28
|
+
{ method: "GET" }
|
|
29
|
+
)
|
|
30
|
+
.then(hasStatusOrThrow(200))
|
|
31
|
+
.then((res) => res.text());
|
|
32
|
+
|
|
33
|
+
const responseJwt = decodeJwt(responseText);
|
|
34
|
+
const catalogueKid = responseJwt.protectedHeader.kid;
|
|
35
|
+
|
|
36
|
+
const trustAnchorJwk = trustAnchorConfig.payload.jwks.keys.find(
|
|
37
|
+
(jwk) => jwk.kid === catalogueKid
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!trustAnchorJwk) {
|
|
41
|
+
throw new IoWalletError(
|
|
42
|
+
`Could not find JWK with kid ${catalogueKid} in Trust Anchor's Entity Configuration`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await verify(responseText, trustAnchorJwk);
|
|
47
|
+
|
|
48
|
+
const parsedDigitalCredentialsCatalogue = DigitalCredentialsCatalogue.parse({
|
|
49
|
+
header: responseJwt.protectedHeader,
|
|
50
|
+
payload: responseJwt.payload,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return parsedDigitalCredentialsCatalogue.payload;
|
|
54
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { UnixTime } from "../sd-jwt/types";
|
|
3
|
+
|
|
4
|
+
const CredentialPurpose = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
description: z.string(),
|
|
7
|
+
category: z.string(),
|
|
8
|
+
subcategory: z.string(),
|
|
9
|
+
claims_required: z.array(z.string()),
|
|
10
|
+
claim_recommended: z.array(z.string()),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const CredentialIssuer = z.object({
|
|
14
|
+
id: z.string(),
|
|
15
|
+
organization_name: z.string(),
|
|
16
|
+
organization_code: z.string(),
|
|
17
|
+
organization_country: z.string(),
|
|
18
|
+
contacts: z.array(z.string()).optional(),
|
|
19
|
+
homepage_uri: z.string().optional(),
|
|
20
|
+
logo_uri: z.string().optional(),
|
|
21
|
+
policy_uri: z.string().optional(),
|
|
22
|
+
tos_uri: z.string().optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const AuthenticSource = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
organization_name: z.string(),
|
|
28
|
+
organization_code: z.string(),
|
|
29
|
+
organization_country: z.string(),
|
|
30
|
+
source_type: z.enum(["public", "private"]),
|
|
31
|
+
contacts: z.array(z.string()).optional(),
|
|
32
|
+
homepage_uri: z.string().optional(),
|
|
33
|
+
logo_uri: z.string().optional(),
|
|
34
|
+
user_information: z.string().optional(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const CredentialFormat = z.object({
|
|
38
|
+
configuration_id: z.string(),
|
|
39
|
+
format: z.enum(["dc+sd-jwt", "mso_mdoc"]),
|
|
40
|
+
vct: z.string().url().optional(),
|
|
41
|
+
docType: z.string().optional(),
|
|
42
|
+
schema_uri: z.string().url().optional(),
|
|
43
|
+
"schema_uri#integrity": z.string().optional(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const Claim = z.object({
|
|
47
|
+
name: z.string(),
|
|
48
|
+
taxonomy_ref: z.string(),
|
|
49
|
+
display_name: z.string(),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const DigitalCredential = z.object({
|
|
53
|
+
version: z.string(),
|
|
54
|
+
credential_type: z.string(),
|
|
55
|
+
legal_type: z.string(),
|
|
56
|
+
name: z.string(),
|
|
57
|
+
description: z.string(),
|
|
58
|
+
validity_info: z.object({
|
|
59
|
+
max_validity_days: z.number(),
|
|
60
|
+
status_methods: z.array(z.string()),
|
|
61
|
+
allowed_states: z.array(z.string()),
|
|
62
|
+
}),
|
|
63
|
+
authentication: z.object({
|
|
64
|
+
user_auth_required: z.boolean(),
|
|
65
|
+
min_loa: z.string(),
|
|
66
|
+
supported_eid_schemes: z.array(z.string()),
|
|
67
|
+
}),
|
|
68
|
+
purposes: z.array(CredentialPurpose),
|
|
69
|
+
issuers: z.array(CredentialIssuer),
|
|
70
|
+
authentic_sources: z.array(AuthenticSource),
|
|
71
|
+
formats: z.array(CredentialFormat),
|
|
72
|
+
claims: z.array(Claim),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The Digital Credentials Catalogue published by the Trust Anchor
|
|
77
|
+
*
|
|
78
|
+
* @version 1.1.0
|
|
79
|
+
* @see https://italia.github.io/eid-wallet-it-docs/releases/1.1.0/en/registry-catalogue.html
|
|
80
|
+
*/
|
|
81
|
+
export const DigitalCredentialsCatalogue = z.object({
|
|
82
|
+
header: z.object({
|
|
83
|
+
typ: z.string(),
|
|
84
|
+
alg: z.string(),
|
|
85
|
+
kid: z.string(),
|
|
86
|
+
}),
|
|
87
|
+
payload: z.object({
|
|
88
|
+
catalog_version: z.string(),
|
|
89
|
+
taxonomy_uri: z.string().url(),
|
|
90
|
+
credentials: z.array(DigitalCredential),
|
|
91
|
+
iat: UnixTime,
|
|
92
|
+
exp: UnixTime,
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
export type DigitalCredentialsCatalogue = z.infer<
|
|
96
|
+
typeof DigitalCredentialsCatalogue
|
|
97
|
+
>;
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,10 @@ import { fixBase64EncodingOnKey } from "./utils/jwk";
|
|
|
5
5
|
import "react-native-url-polyfill/auto";
|
|
6
6
|
|
|
7
7
|
import * as Credential from "./credential";
|
|
8
|
+
import * as CredentialsCatalogue from "./credentials-catalogue";
|
|
8
9
|
import * as PID from "./pid";
|
|
9
10
|
import * as SdJwt from "./sd-jwt";
|
|
11
|
+
import * as Mdoc from "./mdoc";
|
|
10
12
|
import * as Errors from "./utils/errors";
|
|
11
13
|
import * as WalletInstanceAttestation from "./wallet-instance-attestation";
|
|
12
14
|
import * as Trust from "./trust";
|
|
@@ -18,8 +20,10 @@ import type { IntegrityContext } from "./utils/integrity";
|
|
|
18
20
|
|
|
19
21
|
export {
|
|
20
22
|
SdJwt,
|
|
23
|
+
Mdoc,
|
|
21
24
|
PID,
|
|
22
25
|
Credential,
|
|
26
|
+
CredentialsCatalogue,
|
|
23
27
|
WalletInstanceAttestation,
|
|
24
28
|
WalletInstance,
|
|
25
29
|
Errors,
|
package/src/mdoc/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { MissingX509CertsError, X509ValidationError } from "../trust/errors";
|
|
10
10
|
import { IoWalletError } from "../utils/errors";
|
|
11
11
|
import { convertBase64DerToPem, getSigninJwkFromCert } from "../utils/crypto";
|
|
12
|
+
export * from "./utils";
|
|
12
13
|
|
|
13
14
|
export const verify = async (
|
|
14
15
|
token: string,
|
package/src/mdoc/utils.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { CBOR } from "@pagopa/io-react-native-iso18013";
|
|
2
|
+
import { Verification } from "../sd-jwt/types";
|
|
3
|
+
import type { VerifyAndParseCredential } from "../credential/issuance";
|
|
4
|
+
import type { Out } from "../utils/misc";
|
|
5
|
+
import { MDOC_DEFAULT_NAMESPACE } from "./const";
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* @param namespace The mdoc credential `namespace`
|
|
3
9
|
* @param key The claim attribute key
|
|
@@ -5,3 +11,40 @@
|
|
|
5
11
|
*/
|
|
6
12
|
export const getParsedCredentialClaimKey = (namespace: string, key: string) =>
|
|
7
13
|
`${namespace}:${key}`;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extract and validate the `verification` claim from an mdoc parsed credential.
|
|
17
|
+
*
|
|
18
|
+
* This method is **synchronous**, so it requires a credential that was already parsed.
|
|
19
|
+
*
|
|
20
|
+
* @param parsedCredential The parsed mdoc credential
|
|
21
|
+
* @returns The verification claim or undefined if it wasn't found
|
|
22
|
+
*/
|
|
23
|
+
export const getVerificationFromParsedCredential = (
|
|
24
|
+
parsedCredential: Out<VerifyAndParseCredential>["parsedCredential"]
|
|
25
|
+
) => {
|
|
26
|
+
const verificationKey = getParsedCredentialClaimKey(
|
|
27
|
+
`${MDOC_DEFAULT_NAMESPACE}.IT`,
|
|
28
|
+
"verification"
|
|
29
|
+
);
|
|
30
|
+
const verification = parsedCredential[verificationKey]?.value;
|
|
31
|
+
return verification ? Verification.parse(verification) : undefined;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract and validate the `verification` claim from an MDOC credential.
|
|
36
|
+
*
|
|
37
|
+
* This method is **asynchronous**. See {@link getVerificationFromParsedCredential} for the synchronous version.
|
|
38
|
+
*
|
|
39
|
+
* @param token The raw MDOC credential
|
|
40
|
+
* @returns The verification claim or undefined if it wasn't found
|
|
41
|
+
*/
|
|
42
|
+
export const getVerification = async (token: string) => {
|
|
43
|
+
const issuerSigned = await CBOR.decodeIssuerSigned(token);
|
|
44
|
+
const namespace = issuerSigned.nameSpaces[`${MDOC_DEFAULT_NAMESPACE}.IT`];
|
|
45
|
+
const verification = namespace?.find(
|
|
46
|
+
(x) => x.elementIdentifier === "verification"
|
|
47
|
+
)?.elementValue;
|
|
48
|
+
|
|
49
|
+
return verification ? Verification.parse(verification) : undefined;
|
|
50
|
+
};
|
|
@@ -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]: {
|
package/src/utils/zod.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/JacobWeisenburger/zod_utilz/blob/main/src/stringToJSON.ts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
8
|
+
|
|
9
|
+
type Literal = z.infer<typeof literalSchema>;
|
|
10
|
+
|
|
11
|
+
type Json = Literal | { [key: string]: Json } | Json[];
|
|
12
|
+
|
|
13
|
+
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
|
14
|
+
z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const json = () => jsonSchema;
|
|
18
|
+
|
|
19
|
+
export const stringToJSONSchema = z
|
|
20
|
+
.string()
|
|
21
|
+
.transform((str, ctx): z.infer<ReturnType<typeof json>> => {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(str);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
ctx.addIssue({ code: "custom", message: "Invalid JSON" });
|
|
26
|
+
return z.NEVER;
|
|
27
|
+
}
|
|
28
|
+
});
|