@pagopa/io-react-native-wallet 1.2.3 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +3 -0
  2. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
  3. package/lib/commonjs/credential/presentation/01-start-flow.js +12 -28
  4. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
  5. package/lib/commonjs/credential/presentation/04-retrieve-rp-jwks.js +96 -24
  6. package/lib/commonjs/credential/presentation/04-retrieve-rp-jwks.js.map +1 -1
  7. package/lib/commonjs/credential/presentation/05-verify-request-object.js +7 -2
  8. package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
  9. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +9 -5
  10. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
  11. package/lib/commonjs/credential/presentation/08-send-authorization-response.js +20 -16
  12. package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
  13. package/lib/commonjs/credential/presentation/README.md +4 -4
  14. package/lib/commonjs/credential/presentation/errors.js +2 -19
  15. package/lib/commonjs/credential/presentation/errors.js.map +1 -1
  16. package/lib/commonjs/credential/presentation/types.js +9 -1
  17. package/lib/commonjs/credential/presentation/types.js.map +1 -1
  18. package/lib/commonjs/entity/trust/chain.js.map +1 -1
  19. package/lib/commonjs/utils/crypto.js +41 -1
  20. package/lib/commonjs/utils/crypto.js.map +1 -1
  21. package/lib/commonjs/utils/decoder.js.map +1 -1
  22. package/lib/module/credential/issuance/03-start-user-authorization.js +3 -0
  23. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  24. package/lib/module/credential/presentation/01-start-flow.js +12 -28
  25. package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
  26. package/lib/module/credential/presentation/04-retrieve-rp-jwks.js +96 -24
  27. package/lib/module/credential/presentation/04-retrieve-rp-jwks.js.map +1 -1
  28. package/lib/module/credential/presentation/05-verify-request-object.js +7 -2
  29. package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -1
  30. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +9 -5
  31. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
  32. package/lib/module/credential/presentation/08-send-authorization-response.js +18 -14
  33. package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
  34. package/lib/module/credential/presentation/README.md +4 -4
  35. package/lib/module/credential/presentation/errors.js +0 -16
  36. package/lib/module/credential/presentation/errors.js.map +1 -1
  37. package/lib/module/credential/presentation/types.js +9 -1
  38. package/lib/module/credential/presentation/types.js.map +1 -1
  39. package/lib/module/entity/trust/chain.js.map +1 -1
  40. package/lib/module/utils/crypto.js +38 -0
  41. package/lib/module/utils/crypto.js.map +1 -1
  42. package/lib/module/utils/decoder.js +0 -1
  43. package/lib/module/utils/decoder.js.map +1 -1
  44. package/lib/module/utils/jwk.js.map +1 -1
  45. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  46. package/lib/typescript/credential/presentation/01-start-flow.d.ts +3 -3
  47. package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
  48. package/lib/typescript/credential/presentation/03-get-request-object.d.ts +1 -1
  49. package/lib/typescript/credential/presentation/04-retrieve-rp-jwks.d.ts +15 -8
  50. package/lib/typescript/credential/presentation/04-retrieve-rp-jwks.d.ts.map +1 -1
  51. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -1
  52. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts +3 -2
  53. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
  54. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +5 -5
  55. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
  56. package/lib/typescript/credential/presentation/errors.d.ts +0 -11
  57. package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
  58. package/lib/typescript/credential/presentation/types.d.ts +252 -3
  59. package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
  60. package/lib/typescript/entity/trust/chain.d.ts +1 -1
  61. package/lib/typescript/entity/trust/chain.d.ts.map +1 -1
  62. package/lib/typescript/utils/crypto.d.ts +24 -0
  63. package/lib/typescript/utils/crypto.d.ts.map +1 -1
  64. package/lib/typescript/utils/decoder.d.ts +1 -1
  65. package/lib/typescript/utils/decoder.d.ts.map +1 -1
  66. package/lib/typescript/utils/jwk.d.ts +2 -0
  67. package/lib/typescript/utils/jwk.d.ts.map +1 -1
  68. package/package.json +4 -2
  69. package/src/credential/issuance/03-start-user-authorization.ts +3 -0
  70. package/src/credential/presentation/01-start-flow.ts +16 -32
  71. package/src/credential/presentation/03-get-request-object.ts +1 -1
  72. package/src/credential/presentation/04-retrieve-rp-jwks.ts +122 -34
  73. package/src/credential/presentation/05-verify-request-object.ts +4 -3
  74. package/src/credential/presentation/07-evaluate-input-descriptor.ts +20 -6
  75. package/src/credential/presentation/08-send-authorization-response.ts +25 -17
  76. package/src/credential/presentation/README.md +4 -4
  77. package/src/credential/presentation/errors.ts +0 -16
  78. package/src/credential/presentation/types.ts +10 -1
  79. package/src/entity/trust/chain.ts +1 -2
  80. package/src/utils/crypto.ts +43 -0
  81. package/src/utils/decoder.ts +1 -1
  82. package/src/utils/jwk.ts +3 -1
@@ -3,6 +3,12 @@ import { hasStatusOrThrow } from "../../utils/misc";
3
3
  import { RelyingPartyEntityConfiguration } from "../../entity/trust/types";
4
4
  import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
5
5
  import { NoSuitableKeysFoundInEntityConfiguration } from "./errors";
6
+ import { RequestObject } from "./types";
7
+ import {
8
+ convertCertToPem,
9
+ parsePublicKey,
10
+ getSigningJwk,
11
+ } from "../../utils/crypto";
6
12
 
7
13
  /**
8
14
  * Defines the signature for a function that retrieves JSON Web Key Sets (JWKS) from a client.
@@ -16,54 +22,136 @@ export type FetchJwks<T extends Array<unknown> = []> = (...args: T) => Promise<{
16
22
  }>;
17
23
 
18
24
  /**
19
- * Retrieves the JSON Web Key Set (JWKS) from the specified client's well-known endpoint.
20
- * It is formed using `{issUrl.base}/.well-known/jar-issuer${issUrl.pah}` as explained in SD-JWT VC issuer metadata section
25
+ * Fetches and parses JWKS from a given URI.
21
26
  *
22
- * @param requestObjectEncodedJwt - Request Object in JWT format.
23
- * @param options - Optional context containing a custom fetch implementation.
24
- * @param options.context - Optional context object.
25
- * @param options.context.appFetch - Optional custom fetch function to use instead of the global `fetch`.
26
- * @returns A promise resolving to an object containing an array of JWKs.
27
- * @throws Will throw an error if the JWKS retrieval fails.
27
+ * @param jwksUri - The JWKS URI.
28
+ * @param fetchFn - The fetch function to use.
29
+ * @returns An array of JWKs.
30
+ */
31
+ const fetchJwksFromUri = async (
32
+ jwksUri: string,
33
+ appFetch: GlobalFetch["fetch"]
34
+ ): Promise<JWK[]> => {
35
+ const jwks = await appFetch(jwksUri, {
36
+ method: "GET",
37
+ })
38
+ .then(hasStatusOrThrow(200))
39
+ .then((raw) => raw.json())
40
+ .then((json) => (json.jwks ? JWKS.parse(json.jwks) : JWKS.parse(json)));
41
+ return jwks.keys;
42
+ };
43
+
44
+ /**
45
+ * Retrieves JWKS when the client ID scheme includes x509 SAN DNS.
46
+ *
47
+ * @param decodedJwt - The decoded JWT.
48
+ * @param fetchFn - The fetch function to use.
49
+ * @returns An array of JWKs.
50
+ * @throws Will throw an error if no suitable keys are found.
51
+ */
52
+ const getJwksFromX509Cert = async (certChain: string[]): Promise<JWK[]> => {
53
+ if (!Array.isArray(certChain) || certChain.length === 0 || !certChain[0]) {
54
+ throw new NoSuitableKeysFoundInEntityConfiguration(
55
+ "No RP encrypt key found!"
56
+ );
57
+ }
58
+
59
+ const pemCert = convertCertToPem(certChain[0]);
60
+ const publicKey = parsePublicKey(pemCert);
61
+ if (!publicKey) {
62
+ throw new NoSuitableKeysFoundInEntityConfiguration(
63
+ "Unsupported public key type."
64
+ );
65
+ }
66
+ const signingJwk = getSigningJwk(publicKey);
67
+
68
+ return [signingJwk];
69
+ };
70
+
71
+ /**
72
+ * Constructs the well-known JWKS URL based on the issuer claim.
73
+ *
74
+ * @param issuer - The issuer URL.
75
+ * @returns The well-known JWKS URL.
76
+ */
77
+ const constructWellKnownJwksUrl = (issuer: string): string => {
78
+ const issuerUrl = new URL(issuer);
79
+ return new URL(
80
+ `/.well-known/jar-issuer${issuerUrl.pathname}`,
81
+ `${issuerUrl.protocol}//${issuerUrl.host}`
82
+ ).toString();
83
+ };
84
+
85
+ /**
86
+ * Fetches the JSON Web Key Set (JWKS) based on the provided Request Object encoded as a JWT.
87
+ * The retrieval process follows these steps in order:
88
+ *
89
+ * 1. **Direct JWK Retrieval**: If the JWT's protected header contains a `jwk` attribute, it uses this key directly.
90
+ * 2. **X.509 Certificate Retrieval**: If the protected header includes an `x5c` attribute, it extracts the JWKs from the provided X.509 certificate chain.
91
+ * 3. **Issuer's Well-Known Endpoint**: If neither `jwk` nor `x5c` are present, it constructs the JWKS URL using the issuer (`iss`) claim and fetches the keys from the issuer's well-known JWKS endpoint.
92
+ *
93
+ * The JWKS URL is constructed in the format `{issUrl.base}/.well-known/jar-issuer${issUrl.path}`,
94
+ * as detailed in the SD-JWT VC issuer metadata specification.
95
+ *
96
+ * @param requestObjectEncodedJwt - The Request Object encoded as a JWT.
97
+ * @param options - Optional parameters for fetching the JWKS.
98
+ * @param options.context - Optional context providing a custom fetch implementation.
99
+ * @param options.context.appFetch - A custom fetch function to replace the global `fetch` if provided.
100
+ * @returns A promise that resolves to an object containing an array of JSON Web Keys (JWKs).
101
+ * @throws {NoSuitableKeysFoundInEntityConfiguration} Throws an error if JWKS retrieval or key extraction fails.
28
102
  */
29
103
  export const fetchJwksFromRequestObject: FetchJwks<
30
104
  [string, { context?: { appFetch?: GlobalFetch["fetch"] } }?]
31
105
  > = async (requestObjectEncodedJwt, { context = {} } = {}) => {
32
106
  const { appFetch = fetch } = context;
33
107
  const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
108
+ const jwks: JWK[] = [];
34
109
 
35
110
  // 1. check if request object jwt contains the 'jwk' attribute
36
111
  if (requestObjectJwt.protectedHeader?.jwk) {
37
- return {
38
- keys: [JWK.parse(requestObjectJwt.protectedHeader.jwk)],
39
- };
112
+ const keys = [JWK.parse(requestObjectJwt.protectedHeader.jwk)];
113
+ jwks.push(...keys);
114
+ }
115
+
116
+ // 2. check if request object jwt contains the 'x5c' attribute
117
+ if (requestObjectJwt.protectedHeader.x5c) {
118
+ const keys = await getJwksFromX509Cert(
119
+ requestObjectJwt.protectedHeader.x5c
120
+ );
121
+ jwks.push(...keys);
122
+ }
123
+
124
+ // 3. check if client_metadata contains the 'jwks' or 'jwks_uri' attribute
125
+ const requestObject = RequestObject.parse(requestObjectJwt.payload);
126
+ const { client_metadata } = requestObject;
127
+
128
+ if (client_metadata?.jwks_uri) {
129
+ const fetchedJwks = await fetchJwksFromUri(
130
+ new URL(client_metadata.jwks_uri).toString(),
131
+ appFetch
132
+ );
133
+ jwks.push(...fetchedJwks);
134
+ }
135
+
136
+ if (client_metadata?.jwks) {
137
+ jwks.push(...client_metadata.jwks.keys);
138
+ }
139
+
140
+ // 3. According to Potential profile, retrieve from RP endpoint using iss claim
141
+ const issuer = requestObjectJwt.payload?.iss;
142
+ if (jwks.length === 0 && typeof issuer === "string") {
143
+ const wellKnownJwksUrl = constructWellKnownJwksUrl(issuer);
144
+ const jwksKeys = await fetchJwksFromUri(wellKnownJwksUrl, appFetch);
145
+ jwks.push(...jwksKeys);
40
146
  }
41
147
 
42
- // 2. According to Potential profile, retrieve from RP endpoint using iss claim
43
- const issClaimValue = requestObjectJwt.payload?.iss as string;
44
- if (issClaimValue) {
45
- const issUrl = new URL(issClaimValue);
46
- const wellKnownUrl = new URL(
47
- `/.well-known/jar-issuer${issUrl.pathname}`,
48
- `${issUrl.protocol}//${issUrl.host}`
49
- ).toString();
50
-
51
- // Fetches the JWKS from a specific endpoint of the entity's well-known configuration
52
- const jwks = await appFetch(wellKnownUrl, {
53
- method: "GET",
54
- })
55
- .then(hasStatusOrThrow(200))
56
- .then((raw) => raw.json())
57
- .then((json) => JWKS.parse(json.jwks));
58
-
59
- return {
60
- keys: jwks.keys,
61
- };
148
+ if (jwks.length === 0) {
149
+ throw new NoSuitableKeysFoundInEntityConfiguration(
150
+ "Request Object signature verification"
151
+ );
62
152
  }
63
153
 
64
- throw new NoSuitableKeysFoundInEntityConfiguration(
65
- "Request Object signature verification"
66
- );
154
+ return { keys: jwks };
67
155
  };
68
156
 
69
157
  /**
@@ -15,9 +15,10 @@ export const verifyRequestObjectSignature: VerifyRequestObjectSignature =
15
15
  const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
16
16
 
17
17
  // verify token signature to ensure the request object is authentic
18
- const pubKey = jwkKeys?.find(
19
- ({ kid }) => kid === requestObjectJwt.protectedHeader.kid
20
- );
18
+ const pubKey =
19
+ jwkKeys?.find(
20
+ ({ kid }) => kid === requestObjectJwt.protectedHeader.kid
21
+ ) || jwkKeys?.find(({ use }) => use === "sig");
21
22
 
22
23
  if (!pubKey) {
23
24
  throw new UnverifiedEntityError("Request Object signature verification!");
@@ -9,6 +9,7 @@ const INDEX_CLAIM_NAME = 1;
9
9
  export type EvaluatedDisclosures = {
10
10
  requiredDisclosures: DisclosureWithEncoded[];
11
11
  optionalDisclosures: DisclosureWithEncoded[];
12
+ unrequestedDisclosures: DisclosureWithEncoded[];
12
13
  };
13
14
 
14
15
  export type EvaluateInputDescriptorSdJwt4VC = (
@@ -99,8 +100,8 @@ const extractClaimName = (path: string): string | undefined => {
99
100
  * - Validates whether required fields are present (unless marked optional)
100
101
  * and match any specified JSONPath.
101
102
  * - If a field includes a JSON Schema filter, validates the claim value against that schema.
102
- * - Enforces `limit_disclosure` rules by returning only disclosures matching the specified fields
103
- * if set to "required". Otherwise return the array of all disclosures.
103
+ * - Enforces `limit_disclosure` rules by returning only disclosures, required and optional, matching the specified fields
104
+ * if set to "required". Otherwise also return the array unrequestedDisclosures with disclosures which can be passed for a particular use case.
104
105
  * - Throws an error if a required field is invalid or missing.
105
106
  *
106
107
  * @param inputDescriptor - Describes constraints (fields, filters, etc.) that must be satisfied.
@@ -115,7 +116,8 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
115
116
  // No validation, all field are optional
116
117
  return {
117
118
  requiredDisclosures: [],
118
- optionalDisclosures: disclosures,
119
+ optionalDisclosures: [],
120
+ unrequestedDisclosures: disclosures,
119
121
  };
120
122
  }
121
123
  const requiredClaimNames: string[] = [];
@@ -182,9 +184,6 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
182
184
  }
183
185
 
184
186
  // Categorizes disclosures into required and optional based on claim names and disclosure constraints.
185
- const isNotLimitDisclosure = !(
186
- inputDescriptor.constraints.limit_disclosure === "required"
187
- );
188
187
 
189
188
  const requiredDisclosures = disclosures.filter((disclosure) =>
190
189
  requiredClaimNames.includes(disclosure.decoded[INDEX_CLAIM_NAME])
@@ -197,8 +196,23 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
197
196
  !requiredClaimNames.includes(disclosure.decoded[INDEX_CLAIM_NAME]))
198
197
  );
199
198
 
199
+ const isNotLimitDisclosure = !(
200
+ inputDescriptor.constraints.limit_disclosure === "required"
201
+ );
202
+
203
+ const unrequestedDisclosures = isNotLimitDisclosure
204
+ ? disclosures.filter(
205
+ (disclosure) =>
206
+ !optionalClaimNames.includes(
207
+ disclosure.decoded[INDEX_CLAIM_NAME]
208
+ ) &&
209
+ !requiredClaimNames.includes(disclosure.decoded[INDEX_CLAIM_NAME])
210
+ )
211
+ : [];
212
+
200
213
  return {
201
214
  requiredDisclosures,
202
215
  optionalDisclosures,
216
+ unrequestedDisclosures,
203
217
  };
204
218
  };
@@ -6,12 +6,12 @@ import {
6
6
  import uuid from "react-native-uuid";
7
7
  import type { FetchJwks } from "./04-retrieve-rp-jwks";
8
8
  import type { VerifyRequestObjectSignature } from "./05-verify-request-object";
9
- import type { JWK } from "@pagopa/io-react-native-jwt/lib/typescript/types";
10
9
  import { NoSuitableKeysFoundInEntityConfiguration } from "./errors";
11
10
  import { hasStatusOrThrow, type Out } from "../../utils/misc";
12
11
  import { disclose } from "../../sd-jwt";
13
12
  import { PresentationDefinition, type Presentation } from "./types";
14
13
  import * as z from "zod";
14
+ import type { JWK } from "../../utils/jwk";
15
15
 
16
16
  export type AuthorizationResponse = z.infer<typeof AuthorizationResponse>;
17
17
  export const AuthorizationResponse = z.object({
@@ -27,27 +27,25 @@ export const AuthorizationResponse = z.object({
27
27
  });
28
28
 
29
29
  /**
30
- * Selects an RSA public key (with `use = enc` and `kty = RSA`) from the set of JWK keys
30
+ * Selects a public key (with `use = enc`) from the set of JWK keys
31
31
  * offered by the Relying Party (RP) for encryption.
32
32
  *
33
33
  * @param rpJwkKeys - The array of JWKs retrieved from the RP entity configuration.
34
- * @returns The first suitable RSA public key found in the list.
35
- * @throws {NoSuitableKeysFoundInEntityConfiguration} If no suitable RSA encryption key is found.
34
+ * @returns The first suitable public key found in the list.
35
+ * @throws {NoSuitableKeysFoundInEntityConfiguration} If no suitable encryption key is found.
36
36
  */
37
- export const chooseRSAPublicKeyToEncrypt = (
37
+ export const choosePublicKeyToEncrypt = (
38
38
  rpJwkKeys: Out<FetchJwks>["keys"]
39
39
  ): JWK => {
40
- const [rsaEncKey] = rpJwkKeys.filter(
41
- (jwk) => jwk.use === "enc" && jwk.kty === "RSA"
42
- );
40
+ const [encKey] = rpJwkKeys.filter((jwk) => jwk.use === "enc");
43
41
 
44
- if (rsaEncKey) {
45
- return rsaEncKey;
42
+ if (encKey) {
43
+ return encKey;
46
44
  }
47
45
 
48
46
  // No suitable key found
49
47
  throw new NoSuitableKeysFoundInEntityConfiguration(
50
- "No suitable RSA public key found for encryption."
48
+ "No suitable public key found for encryption."
51
49
  );
52
50
  };
53
51
 
@@ -169,17 +167,27 @@ export const buildDirectPostJwtBody = async (
169
167
  });
170
168
 
171
169
  // Choose a suitable RSA public key for encryption
172
- const rsaPublicJwk = chooseRSAPublicKeyToEncrypt(jwkKeys);
170
+ const encPublicJwk = choosePublicKeyToEncrypt(jwkKeys);
173
171
 
174
172
  // Encrypt the authorization payload
173
+ const { client_metadata } = requestObject;
175
174
  const encryptedResponse = await new EncryptJwe(authzResponsePayload, {
176
- alg: "RSA-OAEP-256",
177
- enc: "A256CBC-HS512",
178
- kid: rsaPublicJwk.kid,
179
- }).encrypt(rsaPublicJwk);
175
+ alg:
176
+ (client_metadata?.authorization_encrypted_response_alg as
177
+ | "RSA-OAEP-256"
178
+ | "RSA-OAEP") || "RSA-OAEP-256",
179
+ enc:
180
+ (client_metadata?.authorization_encrypted_response_enc as
181
+ | "A256CBC-HS512"
182
+ | "A128CBC-HS256") || "A256CBC-HS512",
183
+ kid: encPublicJwk.kid,
184
+ }).encrypt(encPublicJwk);
180
185
 
181
186
  // Build the x-www-form-urlencoded form body
182
- const formBody = new URLSearchParams({ response: encryptedResponse });
187
+ const formBody = new URLSearchParams({
188
+ response: encryptedResponse,
189
+ state: requestObject.state,
190
+ });
183
191
  return formBody.toString();
184
192
  };
185
193
 
@@ -29,8 +29,8 @@ sequenceDiagram
29
29
  <summary>Remote Presentation flow</summary>
30
30
 
31
31
  ```ts
32
- // Scan e retrive qr-code
33
- const qrcode = ...
32
+ // Scan e retrive qr-code, decode it and get its parameters
33
+ const {requestUri, clientId} = ...
34
34
 
35
35
  // Retrieve the integrity key tag from the store and create its context
36
36
  const integrityKeyTag = "example"; // Let's assume this is the key tag used to create the wallet instance
@@ -55,7 +55,7 @@ const walletInstanceAttestation =
55
55
  });
56
56
 
57
57
  // Start the issuance flow
58
- const { requestURI, clientId } = Credential.Presentation.startFlowFromQR(qrcode);
58
+ const { requestURI, clientId } = Credential.Presentation.startFlowFromQR(requestUri, clientId);
59
59
 
60
60
  // If use trust federation: Evaluate issuer trust
61
61
  const { rpConf } = await Credential.Presentation.evaluateRelyingPartyTrust(clientId);
@@ -111,4 +111,4 @@ const { presentationDefinition } = await Credential.Presentation.fetchPresentDef
111
111
 
112
112
  ```
113
113
 
114
- </details>
114
+ </details>
@@ -40,22 +40,6 @@ export class NoSuitableKeysFoundInEntityConfiguration extends IoWalletError {
40
40
  }
41
41
  }
42
42
 
43
- /**
44
- * When a QR code is not valid.
45
- *
46
- */
47
- export class InvalidQRCodeError extends IoWalletError {
48
- code = "ERR_INVALID_QR_CODE";
49
-
50
- /**
51
- * @param detail A description of why the QR code is considered invalid.
52
- */
53
- constructor(detail: string) {
54
- const message = `QR code is not valid: ${detail}.`;
55
- super(message);
56
- }
57
- }
58
-
59
43
  /**
60
44
  * When the entity is unverified because the Relying Party is not trusted.
61
45
  *
@@ -1,6 +1,7 @@
1
1
  import type { CryptoContext } from "@pagopa/io-react-native-jwt";
2
2
  import { UnixTime } from "../../sd-jwt/types";
3
3
  import * as z from "zod";
4
+ import { JWKS } from "../../utils/jwk";
4
5
 
5
6
  /**
6
7
  * A pair that associate a tokenized Verified Credential with the claims presented or requested to present.
@@ -77,7 +78,15 @@ export const RequestObject = z.object({
77
78
  response_type: z.literal("vp_token"),
78
79
  response_mode: z.enum(["direct_post.jwt", "direct_post"]),
79
80
  client_id: z.string(),
80
- client_id_scheme: z.string(), // previous z.literal("entity_id"),
81
+ client_id_scheme: z.string().optional(), // previous z.literal("entity_id"),
82
+ client_metadata: z
83
+ .object({
84
+ authorization_encrypted_response_alg: z.string().optional(),
85
+ authorization_encrypted_response_enc: z.string().optional(),
86
+ jwks_uri: z.string().optional(),
87
+ jwks: JWKS.optional(),
88
+ })
89
+ .optional(), // previous z.literal("entity_id"),
81
90
  scope: z.string().optional(),
82
91
  presentation_definition: PresentationDefinition.optional(),
83
92
  });
@@ -7,10 +7,9 @@ import {
7
7
  EntityStatement,
8
8
  TrustAnchorEntityConfiguration,
9
9
  } from "./types";
10
- import { JWK } from "../../utils/jwk";
10
+ import { JWK, type JWTDecodeResult } from "../../utils/jwk";
11
11
  import { IoWalletError } from "../../utils/errors";
12
12
  import * as z from "zod";
13
- import type { JWTDecodeResult } from "@pagopa/io-react-native-jwt/lib/typescript/types";
14
13
  import { getSignedEntityConfiguration, getSignedEntityStatement } from ".";
15
14
 
16
15
  type ParsedToken = {
@@ -7,6 +7,8 @@ import {
7
7
  import uuid from "react-native-uuid";
8
8
  import { thumbprint, type CryptoContext } from "@pagopa/io-react-native-jwt";
9
9
  import { fixBase64EncodingOnKey } from "./jwk";
10
+ import { X509, KEYUTIL, RSAKey, KJUR } from "jsrsasign";
11
+ import { JWK } from "./jwk";
10
12
 
11
13
  /**
12
14
  * Create a CryptoContext bound to a key pair.
@@ -63,3 +65,44 @@ export const withEphemeralKey = async <R>(
63
65
  const ephemeralContext = createCryptoContextFor(keytag);
64
66
  return fn(ephemeralContext).finally(() => deleteKey(keytag));
65
67
  };
68
+
69
+ /**
70
+ * Converts a certificate string to PEM format.
71
+ *
72
+ * @param certificate - The certificate string.
73
+ * @returns The PEM-formatted certificate.
74
+ */
75
+ export const convertCertToPem = (certificate: string): string =>
76
+ `-----BEGIN CERTIFICATE-----\n${certificate}\n-----END CERTIFICATE-----`;
77
+
78
+ /**
79
+ * Parses the public key from a PEM-formatted certificate.
80
+ *
81
+ * @param pemCert - The PEM-formatted certificate.
82
+ * @returns The public key object.
83
+ * @throws Will throw an error if the public key is unsupported.
84
+ */
85
+ export const parsePublicKey = (
86
+ pemCert: string
87
+ ): RSAKey | KJUR.crypto.ECDSA | undefined => {
88
+ const x509 = new X509();
89
+ x509.readCertPEM(pemCert);
90
+ const publicKey = x509.getPublicKey();
91
+
92
+ if (publicKey instanceof RSAKey || publicKey instanceof KJUR.crypto.ECDSA) {
93
+ return publicKey;
94
+ }
95
+
96
+ return undefined;
97
+ };
98
+
99
+ /**
100
+ * Retrieves the signing JWK from the public key.
101
+ *
102
+ * @param publicKey - The public key object.
103
+ * @returns The signing JWK.
104
+ */
105
+ export const getSigningJwk = (publicKey: RSAKey | KJUR.crypto.ECDSA): JWK => ({
106
+ ...JWK.parse(KEYUTIL.getJWKFromKey(publicKey)),
107
+ use: "sig",
108
+ });
@@ -1,6 +1,6 @@
1
1
  import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
2
- import type { JWTDecodeResult } from "@pagopa/io-react-native-jwt/lib/typescript/types";
3
2
  import { ValidationFailed } from "./errors";
3
+ import type { JWTDecodeResult } from "./jwk";
4
4
 
5
5
  /*
6
6
  * Decode a form_post.jwt and return the final JWT.
package/src/utils/jwk.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { removePadding } from "@pagopa/io-react-native-jwt";
1
+ import { decode, removePadding } from "@pagopa/io-react-native-jwt";
2
2
  import { z } from "zod";
3
3
 
4
4
  export type JWK = z.infer<typeof JWK>;
@@ -63,3 +63,5 @@ export type JWKS = z.infer<typeof JWKS>;
63
63
  export const JWKS = z.object({
64
64
  keys: z.array(JWK),
65
65
  });
66
+
67
+ export type JWTDecodeResult = ReturnType<typeof decode>;