@pagopa/io-react-native-wallet 3.1.1 → 3.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.
Files changed (174) hide show
  1. package/lib/commonjs/credential/issuance/README.md +44 -17
  2. package/lib/commonjs/credential/issuance/v1.0.0/03-complete-user-authorization.js +5 -2
  3. package/lib/commonjs/credential/issuance/v1.0.0/03-complete-user-authorization.js.map +1 -1
  4. package/lib/commonjs/credential/issuance/v1.0.0/05-obtain-credential.js +5 -1
  5. package/lib/commonjs/credential/issuance/v1.0.0/05-obtain-credential.js.map +1 -1
  6. package/lib/commonjs/credential/issuance/v1.0.0/index.js +1 -0
  7. package/lib/commonjs/credential/issuance/v1.0.0/index.js.map +1 -1
  8. package/lib/commonjs/credential/issuance/v1.3.3/03-complete-user-authorization.js +1 -1
  9. package/lib/commonjs/credential/issuance/v1.3.3/03-complete-user-authorization.js.map +1 -1
  10. package/lib/commonjs/credential/issuance/v1.3.3/05-obtain-credential.js +147 -52
  11. package/lib/commonjs/credential/issuance/v1.3.3/05-obtain-credential.js.map +1 -1
  12. package/lib/commonjs/credential/issuance/v1.3.3/index.js +1 -0
  13. package/lib/commonjs/credential/issuance/v1.3.3/index.js.map +1 -1
  14. package/lib/commonjs/credential/presentation/api/types.js.map +1 -1
  15. package/lib/commonjs/credential/presentation/v1.0.0/05-verify-request-object.js +18 -12
  16. package/lib/commonjs/credential/presentation/v1.0.0/05-verify-request-object.js.map +1 -1
  17. package/lib/commonjs/credential/presentation/v1.0.0/07-send-authorization-response.js +3 -0
  18. package/lib/commonjs/credential/presentation/v1.0.0/07-send-authorization-response.js.map +1 -1
  19. package/lib/commonjs/credential/presentation/v1.0.0/index.js +0 -2
  20. package/lib/commonjs/credential/presentation/v1.0.0/index.js.map +1 -1
  21. package/lib/commonjs/credential/presentation/v1.0.0/mappers.js +23 -13
  22. package/lib/commonjs/credential/presentation/v1.0.0/mappers.js.map +1 -1
  23. package/lib/commonjs/credential/presentation/v1.0.0/types.js +25 -17
  24. package/lib/commonjs/credential/presentation/v1.0.0/types.js.map +1 -1
  25. package/lib/commonjs/credential/presentation/v1.3.3/05-verify-request-object.js +29 -6
  26. package/lib/commonjs/credential/presentation/v1.3.3/05-verify-request-object.js.map +1 -1
  27. package/lib/commonjs/credential/presentation/v1.3.3/07-send-authorization-response.js +9 -6
  28. package/lib/commonjs/credential/presentation/v1.3.3/07-send-authorization-response.js.map +1 -1
  29. package/lib/commonjs/credential/presentation/v1.3.3/mappers.js +25 -13
  30. package/lib/commonjs/credential/presentation/v1.3.3/mappers.js.map +1 -1
  31. package/lib/commonjs/credential/presentation/v1.3.3/types.js +6 -3
  32. package/lib/commonjs/credential/presentation/v1.3.3/types.js.map +1 -1
  33. package/lib/commonjs/credentials-catalogue/api/DigitalCredentialsCatalogue.js +26 -1
  34. package/lib/commonjs/credentials-catalogue/api/DigitalCredentialsCatalogue.js.map +1 -1
  35. package/lib/commonjs/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.js +4 -0
  36. package/lib/commonjs/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.js.map +1 -1
  37. package/lib/commonjs/credentials-catalogue/v1.3.3/fetch-translations.js +5 -3
  38. package/lib/commonjs/credentials-catalogue/v1.3.3/fetch-translations.js.map +1 -1
  39. package/lib/commonjs/credentials-catalogue/v1.3.3/mappers.js +9 -1
  40. package/lib/commonjs/credentials-catalogue/v1.3.3/mappers.js.map +1 -1
  41. package/lib/commonjs/credentials-catalogue/v1.3.3/types.js +49 -1
  42. package/lib/commonjs/credentials-catalogue/v1.3.3/types.js.map +1 -1
  43. package/lib/commonjs/utils/callbacks.js +25 -6
  44. package/lib/commonjs/utils/callbacks.js.map +1 -1
  45. package/lib/commonjs/utils/crypto.js +58 -5
  46. package/lib/commonjs/utils/crypto.js.map +1 -1
  47. package/lib/module/credential/issuance/README.md +44 -17
  48. package/lib/module/credential/issuance/v1.0.0/03-complete-user-authorization.js +6 -3
  49. package/lib/module/credential/issuance/v1.0.0/03-complete-user-authorization.js.map +1 -1
  50. package/lib/module/credential/issuance/v1.0.0/05-obtain-credential.js +4 -1
  51. package/lib/module/credential/issuance/v1.0.0/05-obtain-credential.js.map +1 -1
  52. package/lib/module/credential/issuance/v1.0.0/index.js +2 -1
  53. package/lib/module/credential/issuance/v1.0.0/index.js.map +1 -1
  54. package/lib/module/credential/issuance/v1.3.3/03-complete-user-authorization.js +1 -1
  55. package/lib/module/credential/issuance/v1.3.3/03-complete-user-authorization.js.map +1 -1
  56. package/lib/module/credential/issuance/v1.3.3/05-obtain-credential.js +143 -49
  57. package/lib/module/credential/issuance/v1.3.3/05-obtain-credential.js.map +1 -1
  58. package/lib/module/credential/issuance/v1.3.3/index.js +2 -1
  59. package/lib/module/credential/issuance/v1.3.3/index.js.map +1 -1
  60. package/lib/module/credential/presentation/api/types.js.map +1 -1
  61. package/lib/module/credential/presentation/v1.0.0/05-verify-request-object.js +13 -7
  62. package/lib/module/credential/presentation/v1.0.0/05-verify-request-object.js.map +1 -1
  63. package/lib/module/credential/presentation/v1.0.0/07-send-authorization-response.js +4 -1
  64. package/lib/module/credential/presentation/v1.0.0/07-send-authorization-response.js.map +1 -1
  65. package/lib/module/credential/presentation/v1.0.0/index.js +0 -2
  66. package/lib/module/credential/presentation/v1.0.0/index.js.map +1 -1
  67. package/lib/module/credential/presentation/v1.0.0/mappers.js +23 -13
  68. package/lib/module/credential/presentation/v1.0.0/mappers.js.map +1 -1
  69. package/lib/module/credential/presentation/v1.0.0/types.js +23 -15
  70. package/lib/module/credential/presentation/v1.0.0/types.js.map +1 -1
  71. package/lib/module/credential/presentation/v1.3.3/05-verify-request-object.js +28 -6
  72. package/lib/module/credential/presentation/v1.3.3/05-verify-request-object.js.map +1 -1
  73. package/lib/module/credential/presentation/v1.3.3/07-send-authorization-response.js +9 -6
  74. package/lib/module/credential/presentation/v1.3.3/07-send-authorization-response.js.map +1 -1
  75. package/lib/module/credential/presentation/v1.3.3/mappers.js +25 -13
  76. package/lib/module/credential/presentation/v1.3.3/mappers.js.map +1 -1
  77. package/lib/module/credential/presentation/v1.3.3/types.js +5 -2
  78. package/lib/module/credential/presentation/v1.3.3/types.js.map +1 -1
  79. package/lib/module/credentials-catalogue/api/DigitalCredentialsCatalogue.js +24 -0
  80. package/lib/module/credentials-catalogue/api/DigitalCredentialsCatalogue.js.map +1 -1
  81. package/lib/module/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.js +5 -1
  82. package/lib/module/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.js.map +1 -1
  83. package/lib/module/credentials-catalogue/v1.3.3/fetch-translations.js +5 -3
  84. package/lib/module/credentials-catalogue/v1.3.3/fetch-translations.js.map +1 -1
  85. package/lib/module/credentials-catalogue/v1.3.3/mappers.js +9 -1
  86. package/lib/module/credentials-catalogue/v1.3.3/mappers.js.map +1 -1
  87. package/lib/module/credentials-catalogue/v1.3.3/types.js +47 -0
  88. package/lib/module/credentials-catalogue/v1.3.3/types.js.map +1 -1
  89. package/lib/module/utils/callbacks.js +26 -7
  90. package/lib/module/utils/callbacks.js.map +1 -1
  91. package/lib/module/utils/crypto.js +57 -6
  92. package/lib/module/utils/crypto.js.map +1 -1
  93. package/lib/typescript/credential/issuance/api/05-obtain-credential.d.ts +20 -0
  94. package/lib/typescript/credential/issuance/api/05-obtain-credential.d.ts.map +1 -1
  95. package/lib/typescript/credential/issuance/v1.0.0/03-complete-user-authorization.d.ts.map +1 -1
  96. package/lib/typescript/credential/issuance/v1.0.0/05-obtain-credential.d.ts +1 -0
  97. package/lib/typescript/credential/issuance/v1.0.0/05-obtain-credential.d.ts.map +1 -1
  98. package/lib/typescript/credential/issuance/v1.0.0/index.d.ts.map +1 -1
  99. package/lib/typescript/credential/issuance/v1.3.3/05-obtain-credential.d.ts +23 -2
  100. package/lib/typescript/credential/issuance/v1.3.3/05-obtain-credential.d.ts.map +1 -1
  101. package/lib/typescript/credential/issuance/v1.3.3/index.d.ts.map +1 -1
  102. package/lib/typescript/credential/presentation/api/04-verify-certificate-chain.d.ts +9 -2
  103. package/lib/typescript/credential/presentation/api/04-verify-certificate-chain.d.ts.map +1 -1
  104. package/lib/typescript/credential/presentation/api/05-verify-request-object.d.ts +2 -2
  105. package/lib/typescript/credential/presentation/api/05-verify-request-object.d.ts.map +1 -1
  106. package/lib/typescript/credential/presentation/api/07-send-authorization-response.d.ts +2 -2
  107. package/lib/typescript/credential/presentation/api/07-send-authorization-response.d.ts.map +1 -1
  108. package/lib/typescript/credential/presentation/api/types.d.ts +18 -0
  109. package/lib/typescript/credential/presentation/api/types.d.ts.map +1 -1
  110. package/lib/typescript/credential/presentation/v1.0.0/05-verify-request-object.d.ts.map +1 -1
  111. package/lib/typescript/credential/presentation/v1.0.0/07-send-authorization-response.d.ts.map +1 -1
  112. package/lib/typescript/credential/presentation/v1.0.0/index.d.ts.map +1 -1
  113. package/lib/typescript/credential/presentation/v1.0.0/mappers.d.ts +21 -13
  114. package/lib/typescript/credential/presentation/v1.0.0/mappers.d.ts.map +1 -1
  115. package/lib/typescript/credential/presentation/v1.0.0/types.d.ts +23 -15
  116. package/lib/typescript/credential/presentation/v1.0.0/types.d.ts.map +1 -1
  117. package/lib/typescript/credential/presentation/v1.3.3/05-verify-request-object.d.ts.map +1 -1
  118. package/lib/typescript/credential/presentation/v1.3.3/07-send-authorization-response.d.ts.map +1 -1
  119. package/lib/typescript/credential/presentation/v1.3.3/mappers.d.ts +89 -79
  120. package/lib/typescript/credential/presentation/v1.3.3/mappers.d.ts.map +1 -1
  121. package/lib/typescript/credential/presentation/v1.3.3/types.d.ts +87 -79
  122. package/lib/typescript/credential/presentation/v1.3.3/types.d.ts.map +1 -1
  123. package/lib/typescript/credentials-catalogue/api/DigitalCredentialsCatalogue.d.ts +73 -0
  124. package/lib/typescript/credentials-catalogue/api/DigitalCredentialsCatalogue.d.ts.map +1 -1
  125. package/lib/typescript/credentials-catalogue/api/index.d.ts +6 -5
  126. package/lib/typescript/credentials-catalogue/api/index.d.ts.map +1 -1
  127. package/lib/typescript/credentials-catalogue/v1.0.0/mappers.d.ts +25 -0
  128. package/lib/typescript/credentials-catalogue/v1.0.0/mappers.d.ts.map +1 -1
  129. package/lib/typescript/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.d.ts.map +1 -1
  130. package/lib/typescript/credentials-catalogue/v1.3.3/fetch-translations.d.ts.map +1 -1
  131. package/lib/typescript/credentials-catalogue/v1.3.3/mappers.d.ts +51 -0
  132. package/lib/typescript/credentials-catalogue/v1.3.3/mappers.d.ts.map +1 -1
  133. package/lib/typescript/credentials-catalogue/v1.3.3/types.d.ts +33 -0
  134. package/lib/typescript/credentials-catalogue/v1.3.3/types.d.ts.map +1 -1
  135. package/lib/typescript/utils/callbacks.d.ts.map +1 -1
  136. package/lib/typescript/utils/crypto.d.ts +32 -15
  137. package/lib/typescript/utils/crypto.d.ts.map +1 -1
  138. package/package.json +2 -1
  139. package/src/credential/issuance/README.md +44 -17
  140. package/src/credential/issuance/api/05-obtain-credential.ts +24 -0
  141. package/src/credential/issuance/v1.0.0/03-complete-user-authorization.ts +8 -3
  142. package/src/credential/issuance/v1.0.0/05-obtain-credential.ts +6 -0
  143. package/src/credential/issuance/v1.0.0/index.ts +5 -1
  144. package/src/credential/issuance/v1.3.3/03-complete-user-authorization.ts +1 -1
  145. package/src/credential/issuance/v1.3.3/05-obtain-credential.ts +175 -80
  146. package/src/credential/issuance/v1.3.3/index.ts +5 -1
  147. package/src/credential/presentation/api/04-verify-certificate-chain.ts +9 -2
  148. package/src/credential/presentation/api/05-verify-request-object.ts +2 -2
  149. package/src/credential/presentation/api/07-send-authorization-response.ts +2 -2
  150. package/src/credential/presentation/api/types.ts +16 -0
  151. package/src/credential/presentation/v1.0.0/05-verify-request-object.ts +21 -10
  152. package/src/credential/presentation/v1.0.0/07-send-authorization-response.ts +7 -0
  153. package/src/credential/presentation/v1.0.0/index.ts +0 -2
  154. package/src/credential/presentation/v1.0.0/mappers.ts +17 -17
  155. package/src/credential/presentation/v1.0.0/types.ts +23 -15
  156. package/src/credential/presentation/v1.3.3/05-verify-request-object.ts +63 -10
  157. package/src/credential/presentation/v1.3.3/07-send-authorization-response.ts +13 -4
  158. package/src/credential/presentation/v1.3.3/mappers.ts +19 -17
  159. package/src/credential/presentation/v1.3.3/types.ts +9 -3
  160. package/src/credentials-catalogue/api/DigitalCredentialsCatalogue.ts +32 -0
  161. package/src/credentials-catalogue/api/index.ts +6 -3
  162. package/src/credentials-catalogue/v1.3.3/fetch-and-parse-catalogue.ts +6 -0
  163. package/src/credentials-catalogue/v1.3.3/fetch-translations.ts +6 -3
  164. package/src/credentials-catalogue/v1.3.3/mappers.ts +17 -1
  165. package/src/credentials-catalogue/v1.3.3/types.ts +51 -0
  166. package/src/utils/callbacks.ts +29 -8
  167. package/src/utils/crypto.ts +86 -15
  168. package/lib/commonjs/credential/presentation/v1.0.0/04-verify-certificate-chain.js +0 -12
  169. package/lib/commonjs/credential/presentation/v1.0.0/04-verify-certificate-chain.js.map +0 -1
  170. package/lib/module/credential/presentation/v1.0.0/04-verify-certificate-chain.js +0 -5
  171. package/lib/module/credential/presentation/v1.0.0/04-verify-certificate-chain.js.map +0 -1
  172. package/lib/typescript/credential/presentation/v1.0.0/04-verify-certificate-chain.d.ts +0 -3
  173. package/lib/typescript/credential/presentation/v1.0.0/04-verify-certificate-chain.d.ts.map +0 -1
  174. package/src/credential/presentation/v1.0.0/04-verify-certificate-chain.ts +0 -10
@@ -1,10 +1,17 @@
1
- import type { RemotePresentationApi } from "../api";
2
- import { parseAuthorizeRequest as sdkParseAuthorizeRequest } from "@pagopa/io-wallet-oid4vp";
1
+ import type { RelyingPartyConfig, RemotePresentationApi } from "../api";
2
+ import {
3
+ parseAuthorizeRequest as sdkParseAuthorizeRequest,
4
+ ClientIdPrefix,
5
+ extractClientIdPrefix,
6
+ } from "@pagopa/io-wallet-oid4vp";
7
+ import QuickCrypto from "react-native-quick-crypto";
3
8
  import { partialCallbacks } from "../../../utils/callbacks";
4
9
  import { sdkConfigV1_3 } from "../../../utils/config";
10
+ import { IoWalletError } from "../../../utils/errors";
5
11
  import { InvalidRequestObjectError } from "../common/errors";
6
12
  import { mapSdkRequestObjectError } from "./sdkErrorMapper";
7
13
  import { mapToRequestObject } from "./mappers";
14
+ import type { RawRequestObject } from "./types";
8
15
 
9
16
  export const verifyRequestObject: RemotePresentationApi["verifyRequestObject"] =
10
17
  async (requestObjectEncodedJwt, { clientId, rpConf }) => {
@@ -16,18 +23,64 @@ export const verifyRequestObject: RemotePresentationApi["verifyRequestObject"] =
16
23
  },
17
24
  }).catch(mapSdkRequestObjectError);
18
25
 
19
- const payload = parsedRequestObject.payload;
26
+ const rawRequestObject = parsedRequestObject as RawRequestObject;
20
27
 
21
- const isClientIdMatch =
22
- clientId === payload.client_id && clientId === rpConf.subject;
28
+ const clientIdPrefix = extractClientIdPrefix(clientId);
23
29
 
24
- if (!isClientIdMatch) {
25
- throw new InvalidRequestObjectError(
26
- "Client ID does not match Request Object or Entity Configuration"
27
- );
30
+ if (clientIdPrefix === ClientIdPrefix.X509_HASH) {
31
+ validateX509HashClient(rawRequestObject.header.x5c, clientId);
32
+ }
33
+
34
+ if (
35
+ clientIdPrefix === ClientIdPrefix.OPENID_FEDERATION ||
36
+ clientIdPrefix === ClientIdPrefix.NONE
37
+ ) {
38
+ validateOpenIDFederationClient(rawRequestObject, clientId, rpConf);
28
39
  }
29
40
 
30
41
  return {
31
- requestObject: mapToRequestObject(payload),
42
+ requestObject: mapToRequestObject(rawRequestObject),
32
43
  };
33
44
  };
45
+
46
+ const validateOpenIDFederationClient = (
47
+ requestObject: RawRequestObject,
48
+ clientId: string,
49
+ rpConf: RelyingPartyConfig | undefined
50
+ ) => {
51
+ if (!rpConf) {
52
+ throw new IoWalletError(
53
+ "Relying Party Configuration is required for OpenID Federation clients"
54
+ );
55
+ }
56
+
57
+ const isClientIdMatch =
58
+ clientId === requestObject.payload.client_id &&
59
+ stripOpenIdFederationPrefix(clientId) === rpConf.subject;
60
+
61
+ if (!isClientIdMatch) {
62
+ throw new InvalidRequestObjectError(
63
+ "Client ID does not match Request Object or Entity Configuration"
64
+ );
65
+ }
66
+ };
67
+
68
+ const validateX509HashClient = (
69
+ certificateChain: string[],
70
+ clientId: string
71
+ ) => {
72
+ const [, x509Hash] = clientId.split(":");
73
+
74
+ const calculatedHash = QuickCrypto.createHash("sha-256")
75
+ .update(certificateChain[0]!, "base64")
76
+ .digest("base64url");
77
+
78
+ if (x509Hash !== calculatedHash) {
79
+ throw new InvalidRequestObjectError(
80
+ "x509_hash does not match the hash of the x5c leaf certificate"
81
+ );
82
+ }
83
+ };
84
+
85
+ const stripOpenIdFederationPrefix = (clientId: string) =>
86
+ clientId.replace("openid_federation:", "");
@@ -101,14 +101,23 @@ export const sendAuthorizationResponse: RemotePresentationApi["sendAuthorization
101
101
  { appFetch = fetch } = {}
102
102
  ) => {
103
103
  try {
104
- const { presentations } = remotePresentation;
104
+ if (!rpConf && !requestObject.client_metadata) {
105
+ throw new IoWalletError(
106
+ "At least one of rpConf or requestObject.client_metadata must be provided to send the authorization response"
107
+ );
108
+ }
109
+
110
+ // When the RP is not an OpenID Federation client, rpConf will be undefined
111
+ // so the keys are taken from the Request Object's client_metadata.
105
112
  const rpJwks = {
106
- jwks: rpConf.jwks,
113
+ jwks: rpConf?.jwks ?? requestObject.client_metadata!.jwks,
107
114
  encrypted_response_enc_values_supported:
108
- rpConf.encrypted_response_enc_values_supported,
115
+ rpConf?.encrypted_response_enc_values_supported ??
116
+ requestObject.client_metadata!
117
+ .encrypted_response_enc_values_supported,
109
118
  };
110
119
 
111
- const vp_token = presentations.reduce(
120
+ const vp_token = remotePresentation.presentations.reduce(
112
121
  (acc, p) => {
113
122
  (acc[p.credentialId] ??= []).push(p.vpToken);
114
123
  return acc;
@@ -2,16 +2,16 @@ import { RelyingPartyEntityConfiguration } from "../../../trust/v1.3.3/types";
2
2
  import { createMapper } from "../../../utils/mappers";
3
3
  import type { RelyingPartyConfig } from "../api/RelyingPartyConfig";
4
4
  import type { RequestObject } from "../api/types";
5
- import { RequestObjectPayload } from "./types";
5
+ import { RawRequestObject } from "./types";
6
6
 
7
7
  export const mapToRelyingPartyConfig = createMapper<
8
8
  RelyingPartyEntityConfiguration,
9
9
  RelyingPartyConfig
10
- >((x) => {
11
- const { federation_entity, openid_credential_verifier } = x.payload.metadata;
10
+ >(({ payload }) => {
11
+ const { federation_entity, openid_credential_verifier } = payload.metadata;
12
12
 
13
13
  return {
14
- subject: x.payload.sub,
14
+ subject: payload.sub,
15
15
  jwks: openid_credential_verifier.jwks,
16
16
  federation_entity,
17
17
  encrypted_response_enc_values_supported:
@@ -19,16 +19,18 @@ export const mapToRelyingPartyConfig = createMapper<
19
19
  };
20
20
  });
21
21
 
22
- export const mapToRequestObject = createMapper<
23
- RequestObjectPayload,
24
- RequestObject
25
- >((x) => ({
26
- iss: x.iss,
27
- client_id: x.client_id,
28
- dcql_query: x.dcql_query,
29
- nonce: x.nonce,
30
- response_uri: x.response_uri,
31
- state: x.state,
32
- response_mode: x.response_mode,
33
- response_type: x.response_type,
34
- }));
22
+ export const mapToRequestObject = createMapper<RawRequestObject, RequestObject>(
23
+ ({ payload, header }) => ({
24
+ iss: payload.iss,
25
+ client_id: payload.client_id,
26
+ dcql_query: payload.dcql_query,
27
+ nonce: payload.nonce,
28
+ response_uri: payload.response_uri,
29
+ state: payload.state,
30
+ response_mode: payload.response_mode,
31
+ response_type: payload.response_type,
32
+ client_metadata: payload.client_metadata,
33
+ x5c: header.x5c,
34
+ trust_chain: header.trust_chain,
35
+ })
36
+ );
@@ -1,8 +1,14 @@
1
1
  import * as z from "zod";
2
- import { zOpenid4vpAuthorizationRequestPayload as sdkRequestObjectPayload } from "@pagopa/io-wallet-oid4vp";
2
+ import {
3
+ zOpenid4vpAuthorizationRequestHeaderV1_3,
4
+ zOpenid4vpAuthorizationRequestPayload,
5
+ } from "@pagopa/io-wallet-oid4vp";
3
6
 
4
- export type RequestObjectPayload = z.infer<typeof sdkRequestObjectPayload>;
5
- export const RequestObjectPayload = sdkRequestObjectPayload;
7
+ export type RawRequestObject = z.infer<typeof RawRequestObject>;
8
+ export const RawRequestObject = z.object({
9
+ header: zOpenid4vpAuthorizationRequestHeaderV1_3,
10
+ payload: zOpenid4vpAuthorizationRequestPayload,
11
+ });
6
12
 
7
13
  export type AuthorizationResponse = z.infer<typeof AuthorizationResponse>;
8
14
  export const AuthorizationResponse = z.object({
@@ -117,8 +117,40 @@ export const DigitalCredential = z.object({
117
117
  // claims: z.array(Claim), // TODO: [SIW-3978] Should we keep claims?
118
118
  });
119
119
 
120
+ const TaxonomyPurpose = z.object({
121
+ id: z.string(),
122
+ name_l10n_id: z.string(),
123
+ });
124
+ export type TaxonomyPurpose = z.infer<typeof TaxonomyPurpose>;
125
+
126
+ const TaxonomyClass = z.object({
127
+ id: z.string(),
128
+ name_l10n_id: z.string(),
129
+ supported_purposes: z.array(z.string()),
130
+ });
131
+ export type TaxonomyClass = z.infer<typeof TaxonomyClass>;
132
+
133
+ const TaxonomyDomain = z.object({
134
+ id: z.string(),
135
+ name_l10n_id: z.string(),
136
+ description_l10n_id: z.string(),
137
+ classes: z.array(TaxonomyClass),
138
+ });
139
+ export type TaxonomyDomain = z.infer<typeof TaxonomyDomain>;
140
+
141
+ export const Taxonomy = z.object({
142
+ id: z.string(),
143
+ name_l10n_id: z.string(),
144
+ description_l10n_id: z.string(),
145
+ domains: z.array(TaxonomyDomain),
146
+ purposes: z.array(TaxonomyPurpose),
147
+ localization: LocalizationInfo.optional(),
148
+ });
149
+ export type Taxonomy = z.infer<typeof Taxonomy>;
150
+
120
151
  export const DigitalCredentialsCatalogue = z.object({
121
152
  taxonomy_uri: z.string().url(),
153
+ taxonomy: Taxonomy.optional(),
122
154
  credentials: z.array(DigitalCredential),
123
155
  iat: UnixTime,
124
156
  exp: UnixTime,
@@ -2,6 +2,7 @@ import {
2
2
  type CatalogueTranslations,
3
3
  type DigitalCredentialsCatalogue,
4
4
  type LocalizationInfo,
5
+ type Taxonomy,
5
6
  } from "./DigitalCredentialsCatalogue";
6
7
 
7
8
  type FetchContext = { appFetch?: GlobalFetch["fetch"] };
@@ -9,6 +10,7 @@ type FetchContext = { appFetch?: GlobalFetch["fetch"] };
9
10
  type FetchTranslationsLocalizations = {
10
11
  catalogue?: LocalizationInfo;
11
12
  authenticSources?: LocalizationInfo;
13
+ taxonomy?: LocalizationInfo;
12
14
  };
13
15
 
14
16
  export interface CredentialsCatalogueApi {
@@ -27,11 +29,11 @@ export interface CredentialsCatalogueApi {
27
29
  ): Promise<DigitalCredentialsCatalogue>;
28
30
 
29
31
  /**
30
- * Fetch locale bundle files for the credential catalogue and authentic sources.
31
- * For each requested locale, fetches translations from both registries (if the locale
32
+ * Fetch locale bundle files for the credential catalogue, authentic sources, and taxonomy.
33
+ * For each requested locale, fetches translations from all registries (if the locale
32
34
  * is listed in their respective `available_locales`) and merges the keys.
33
35
  * Locales not present in a registry's `available_locales` are silently skipped for that source.
34
- * On key conflicts, authentic-sources translations take precedence.
36
+ * On key conflicts, later sources (authenticSources, taxonomy) take precedence.
35
37
  *
36
38
  * Optional: not supported by all versions. Check for existence before calling.
37
39
  *
@@ -52,4 +54,5 @@ export {
52
54
  type CatalogueTranslations,
53
55
  type DigitalCredentialsCatalogue,
54
56
  type LocalizationInfo,
57
+ type Taxonomy,
55
58
  };
@@ -5,6 +5,7 @@ import {
5
5
  DigitalCredentialsCatalogueJwt,
6
6
  RegistryDiscoveryJwt,
7
7
  SchemaRegistry,
8
+ TaxonomyRegistry,
8
9
  } from "./types";
9
10
  import { mapToCredentialsCatalogue } from "./mappers";
10
11
  import { fetchRegistry } from "./utils";
@@ -46,6 +47,11 @@ export const fetchAndParseCatalogue: Api["fetchAndParseCatalogue"] = async (
46
47
  asJson: true,
47
48
  appFetch,
48
49
  }),
50
+ fetchRegistry(endpoints.taxonomy, {
51
+ schema: TaxonomyRegistry,
52
+ asJson: true,
53
+ appFetch,
54
+ }),
49
55
  ]);
50
56
 
51
57
  return mapToCredentialsCatalogue([discovery, ...registries]);
@@ -2,7 +2,7 @@ import type { CredentialsCatalogueApi as Api } from "../api";
2
2
  import { fetchLocaleBundle } from "./utils";
3
3
 
4
4
  export const fetchTranslations: NonNullable<Api["fetchTranslations"]> = async (
5
- { catalogue, authenticSources },
5
+ { catalogue, authenticSources, taxonomy },
6
6
  locales,
7
7
  { appFetch = fetch } = {}
8
8
  ) => {
@@ -10,16 +10,19 @@ export const fetchTranslations: NonNullable<Api["fetchTranslations"]> = async (
10
10
 
11
11
  await Promise.all(
12
12
  locales.map(async (locale) => {
13
- const [catalogueBundle, asBundle] = await Promise.all([
13
+ const [catalogueBundle, asBundle, taxonomyBundle] = await Promise.all([
14
14
  catalogue?.available_locales.includes(locale)
15
15
  ? fetchLocaleBundle(catalogue.base_uri, locale, appFetch)
16
16
  : Promise.resolve({}),
17
17
  authenticSources?.available_locales.includes(locale)
18
18
  ? fetchLocaleBundle(authenticSources.base_uri, locale, appFetch)
19
19
  : Promise.resolve({}),
20
+ taxonomy?.available_locales.includes(locale)
21
+ ? fetchLocaleBundle(taxonomy.base_uri, locale, appFetch)
22
+ : Promise.resolve({}),
20
23
  ]);
21
24
 
22
- const merged = { ...catalogueBundle, ...asBundle };
25
+ const merged = { ...catalogueBundle, ...asBundle, ...taxonomyBundle };
23
26
 
24
27
  // Only include the locale in the result if at least one source provided translations
25
28
  if (Object.keys(merged).length > 0) {
@@ -11,6 +11,7 @@ import {
11
11
  DigitalCredentialsCatalogueJwt,
12
12
  RegistryDiscoveryJwt,
13
13
  SchemaRegistry,
14
+ TaxonomyRegistry,
14
15
  } from "./types";
15
16
 
16
17
  export const mapToCredentialsCatalogue = createMapper<
@@ -19,10 +20,17 @@ export const mapToCredentialsCatalogue = createMapper<
19
20
  DigitalCredentialsCatalogueJwt,
20
21
  AuthenticSourceRegistry,
21
22
  SchemaRegistry,
23
+ TaxonomyRegistry,
22
24
  ],
23
25
  DigitalCredentialsCatalogue
24
26
  >(
25
- ([discoveryJwt, catalogueJwt, authSourceRegistry, schemaRegistry]) => {
27
+ ([
28
+ discoveryJwt,
29
+ catalogueJwt,
30
+ authSourceRegistry,
31
+ schemaRegistry,
32
+ taxonomyRegistry,
33
+ ]) => {
26
34
  const authSourcesById = keyBy(
27
35
  authSourceRegistry.authentic_sources,
28
36
  "entity_id"
@@ -65,6 +73,14 @@ export const mapToCredentialsCatalogue = createMapper<
65
73
  return {
66
74
  ...catalogueJwt.payload,
67
75
  taxonomy_uri: discoveryJwt.payload.endpoints.taxonomy,
76
+ taxonomy: {
77
+ id: taxonomyRegistry.id,
78
+ name_l10n_id: taxonomyRegistry.name_l10n_id,
79
+ description_l10n_id: taxonomyRegistry.description_l10n_id,
80
+ domains: taxonomyRegistry.domains,
81
+ purposes: taxonomyRegistry.purposes,
82
+ localization: taxonomyRegistry.localization,
83
+ },
68
84
  localization: catalogueJwt.payload.localization,
69
85
  as_localization: authSourceRegistry.localization,
70
86
  credentials: catalogueJwt.payload.credentials.map(
@@ -233,3 +233,54 @@ export const RegistryDiscoveryJwt = z.object({
233
233
  }),
234
234
  });
235
235
  export type RegistryDiscoveryJwt = z.infer<typeof RegistryDiscoveryJwt>;
236
+
237
+ /**
238
+ * Taxonomy purpose (top-level flat list).
239
+ */
240
+ const TaxonomyPurpose = z.object({
241
+ id: z.string(),
242
+ name_l10n_id: z.string(),
243
+ });
244
+
245
+ /**
246
+ * Taxonomy class within a domain.
247
+ */
248
+ const TaxonomyClass = z.object({
249
+ id: z.string(),
250
+ name_l10n_id: z.string(),
251
+ supported_purposes: z.array(z.string()),
252
+ });
253
+
254
+ /**
255
+ * Taxonomy domain containing classes.
256
+ */
257
+ const TaxonomyDomain = z.object({
258
+ id: z.string(),
259
+ name_l10n_id: z.string(),
260
+ description_l10n_id: z.string(),
261
+ classes: z.array(TaxonomyClass),
262
+ });
263
+
264
+ /**
265
+ * Taxonomy registry, available at a dedicated endpoint.
266
+ * Provides a hierarchical classification of domains, classes, and purposes.
267
+ * @see https://italia.github.io/eid-wallet-it-docs/releases/1.3.3/en/registry.html#taxonomy
268
+ */
269
+ export const TaxonomyRegistry = z.object({
270
+ version: z.string(),
271
+ last_modified: z.string(),
272
+ id: z.string(),
273
+ localization: z
274
+ .object({
275
+ available_locales: z.array(z.string()),
276
+ base_uri: z.string(),
277
+ default_locale: z.string(),
278
+ version: z.string(),
279
+ })
280
+ .optional(),
281
+ name_l10n_id: z.string(),
282
+ description_l10n_id: z.string(),
283
+ domains: z.array(TaxonomyDomain),
284
+ purposes: z.array(TaxonomyPurpose),
285
+ });
286
+ export type TaxonomyRegistry = z.infer<typeof TaxonomyRegistry>;
@@ -1,11 +1,12 @@
1
1
  import { EncryptJwe, getJwkFromHeader } from "@pagopa/io-react-native-jwt";
2
2
  import { verify } from "@pagopa/io-react-native-jwt";
3
- import { type CallbackContext } from "@pagopa/io-wallet-oauth2";
3
+ import { type CallbackContext, type JwtSigner } from "@pagopa/io-wallet-oauth2";
4
4
  import { digest } from "@sd-jwt/crypto-nodejs";
5
5
  import { X509 } from "jsrsasign";
6
6
  import { IoWalletError } from "./errors";
7
- import { generateRandomBytes } from "./misc";
7
+ import { assert, generateRandomBytes } from "./misc";
8
8
  import type { JWK } from "./jwk";
9
+ import { getJwkFromCertificateChain, getJwkFromTrustChain } from "./crypto";
9
10
 
10
11
  type PartialCallbackContext = Omit<
11
12
  CallbackContext,
@@ -18,6 +19,29 @@ type DigestFixed = (
18
19
  algorithm?: string
19
20
  ) => Uint8Array;
20
21
 
22
+ /**
23
+ * Extract the signing JWK from one of the supported signer methods.
24
+ * @param signer - The JWT signer.
25
+ * @returns The JWK for signature verification.
26
+ */
27
+ const getJwkFromSigner = async (signer: JwtSigner): Promise<JWK> => {
28
+ switch (signer.method) {
29
+ case "x5c":
30
+ return getJwkFromCertificateChain(signer.x5c);
31
+ case "federation": {
32
+ assert(
33
+ signer.trustChain && signer.trustChain.length > 0,
34
+ "Trust chain is required for federation signer"
35
+ );
36
+ return getJwkFromTrustChain(signer.trustChain, signer.kid);
37
+ }
38
+ case "jwk":
39
+ return signer.publicJwk as JWK;
40
+ default:
41
+ throw new IoWalletError(`Unsupported signer method: ${signer.method}`);
42
+ }
43
+ };
44
+
21
45
  /**
22
46
  * Shared callbacks with React Native implementations for use
23
47
  * in IO Wallet SDK. Callbacks not found here must be provided by the caller,
@@ -32,13 +56,10 @@ export const partialCallbacks: PartialCallbackContext = {
32
56
  encryptionJwk: publicJwk,
33
57
  }),
34
58
  verifyJwt: async (jwtSigner, jwt) => {
35
- // TODO: support other signing methods if needed
36
- if (jwtSigner.method !== "jwk") {
37
- throw new IoWalletError(`Unsupported signer method: ${jwtSigner.method}`);
38
- }
39
59
  try {
40
- await verify(jwt.compact, jwtSigner.publicJwk);
41
- return { verified: true, signerJwk: jwtSigner.publicJwk };
60
+ const signerJwk = await getJwkFromSigner(jwtSigner);
61
+ await verify(jwt.compact, signerJwk);
62
+ return { verified: true, signerJwk };
42
63
  } catch {
43
64
  return { verified: false };
44
65
  }
@@ -5,11 +5,34 @@ import {
5
5
  sign,
6
6
  } from "@pagopa/io-react-native-crypto";
7
7
  import { v4 as uuidv4 } from "uuid";
8
- import { thumbprint, type CryptoContext } from "@pagopa/io-react-native-jwt";
9
- import { JWK } from "./jwk";
8
+ import {
9
+ decode,
10
+ thumbprint,
11
+ type CryptoContext,
12
+ } from "@pagopa/io-react-native-jwt";
13
+ import type { BaseEntityConfiguration } from "../trust/common/types";
14
+ import { JWK, JWKS } from "./jwk";
10
15
  import { KEYUTIL, KJUR, RSAKey, X509 } from "jsrsasign";
11
16
  import { IoWalletError } from "./errors";
12
17
 
18
+ /**
19
+ * Extension of the {@link CryptoContext} that adds key generation with optional key attestation.
20
+ *
21
+ * This context requires the consumer to provide an additional method for **key generation**;
22
+ * on Android this method should also generate a key attestation as a certificate chain
23
+ * to ensure the key pair is hardware-backed.
24
+ */
25
+ export type KeyAttestationCryptoContext = CryptoContext & {
26
+ /**
27
+ * Generate a key pair with an **optional key attestation** (Android).
28
+ * @param challenge The challenge for the key attestation.
29
+ * @returns An object with a success flag and a key attestation, if it was generated.
30
+ */
31
+ generateKeyWithAttestation(
32
+ challenge: string
33
+ ): Promise<{ success: boolean; attestation?: string }>;
34
+ };
35
+
13
36
  /**
14
37
  * Create a CryptoContext bound to a key pair.
15
38
  * Key pair is supposed to exist already in the device's keychain.
@@ -92,19 +115,67 @@ export const getSigninJwkFromCert = (pemCert: string): JWK => {
92
115
  };
93
116
 
94
117
  /**
95
- * Extension of the {@link CryptoContext} that adds key generation with optional key attestation.
118
+ * Retrieves the signing JWK from a x509 certificate chain.
96
119
  *
97
- * This context requires the consumer to provide an additional method for **key generation**;
98
- * on Android this method should also generate a key attestation as a certificate chain
99
- * to ensure the key pair is hardware-backed.
120
+ * @param certChain - The x509 certificate chain.
121
+ * @returns The signing JWK.
122
+ * @throws Will throw an error if no suitable keys are found.
100
123
  */
101
- export type KeyAttestationCryptoContext = CryptoContext & {
102
- /**
103
- * Generate a key pair with an **optional key attestation** (Android).
104
- * @param challenge The challenge for the key attestation.
105
- * @returns An object with a success flag and a key attestation, if it was generated.
106
- */
107
- generateKeyWithAttestation(
108
- challenge: string
109
- ): Promise<{ success: boolean; attestation?: string }>;
124
+ export const getJwkFromCertificateChain = async (
125
+ certChain: string[]
126
+ ): Promise<JWK> => {
127
+ const [leafCert] = certChain;
128
+ if (!leafCert) {
129
+ throw new IoWalletError(
130
+ "The provided certificate chain is invalid or malformed"
131
+ );
132
+ }
133
+ const pemCert = convertBase64DerToPem(leafCert);
134
+ return getSigninJwkFromCert(pemCert);
135
+ };
136
+
137
+ /**
138
+ * Retrieves the signing JWK from a trust chain of entity configuration JWTs, matching the provided signer KID.
139
+ *
140
+ * @param trustChain - The trust chain of entity configuration JWTs.
141
+ * @param signerKid - The KID of the signer to look for in the trust chain.
142
+ * @returns The signing JWK.
143
+ * @throws Will throw an error if no suitable keys are found.
144
+ */
145
+ export const getJwkFromTrustChain = (
146
+ trustChain: string[],
147
+ signerKid: string
148
+ ): JWK => {
149
+ const [entityConfigurationJwt] = trustChain;
150
+ if (!entityConfigurationJwt) {
151
+ throw new IoWalletError("The provided trust chain is invalid or malformed");
152
+ }
153
+
154
+ const keys: JWK[] = [];
155
+ const decodedEntityConfigJwt = decode(entityConfigurationJwt);
156
+ const baseEntityConfig =
157
+ decodedEntityConfigJwt.payload as BaseEntityConfiguration["payload"];
158
+
159
+ // Get top-level JWKS
160
+ if (baseEntityConfig.jwks) {
161
+ keys.push(...JWKS.parse(baseEntityConfig.jwks).keys);
162
+ }
163
+
164
+ // Check metadata entries for additional JWKS like openid_credential_verifier
165
+ if (baseEntityConfig.metadata) {
166
+ for (const metadata of Object.values(
167
+ baseEntityConfig.metadata as Record<string, { jwks?: JWKS }>
168
+ )) {
169
+ if (metadata.jwks) {
170
+ keys.push(...JWKS.parse(metadata.jwks).keys);
171
+ }
172
+ }
173
+ }
174
+
175
+ const federationJwk = keys.find((key) => key.kid === signerKid);
176
+ if (!federationJwk)
177
+ throw new IoWalletError(
178
+ "No suitable key was found in the provided trust chain"
179
+ );
180
+ return federationJwk;
110
181
  };
@@ -1,12 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.verifyAuthRequestCertificateChain = void 0;
7
- var _errors = require("../../../utils/errors");
8
- const verifyAuthRequestCertificateChain = async () => {
9
- throw new _errors.UnimplementedFeatureError("verifyAuthRequestCertificateChain", "1.0.0");
10
- };
11
- exports.verifyAuthRequestCertificateChain = verifyAuthRequestCertificateChain;
12
- //# sourceMappingURL=04-verify-certificate-chain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_errors","require","verifyAuthRequestCertificateChain","UnimplementedFeatureError","exports"],"sourceRoot":"../../../../../src","sources":["credential/presentation/v1.0.0/04-verify-certificate-chain.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGO,MAAMC,iCAA6F,GACxG,MAAAA,CAAA,KAAY;EACV,MAAM,IAAIC,iCAAyB,CACjC,mCAAmC,EACnC,OACF,CAAC;AACH,CAAC;AAACC,OAAA,CAAAF,iCAAA,GAAAA,iCAAA"}
@@ -1,5 +0,0 @@
1
- import { UnimplementedFeatureError } from "../../../utils/errors";
2
- export const verifyAuthRequestCertificateChain = async () => {
3
- throw new UnimplementedFeatureError("verifyAuthRequestCertificateChain", "1.0.0");
4
- };
5
- //# sourceMappingURL=04-verify-certificate-chain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["UnimplementedFeatureError","verifyAuthRequestCertificateChain"],"sourceRoot":"../../../../../src","sources":["credential/presentation/v1.0.0/04-verify-certificate-chain.ts"],"mappings":"AAAA,SAASA,yBAAyB,QAAQ,uBAAuB;AAGjE,OAAO,MAAMC,iCAA6F,GACxG,MAAAA,CAAA,KAAY;EACV,MAAM,IAAID,yBAAyB,CACjC,mCAAmC,EACnC,OACF,CAAC;AACH,CAAC"}
@@ -1,3 +0,0 @@
1
- import { type RemotePresentationApi } from "../api";
2
- export declare const verifyAuthRequestCertificateChain: RemotePresentationApi["verifyAuthRequestCertificateChain"];
3
- //# sourceMappingURL=04-verify-certificate-chain.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"04-verify-certificate-chain.d.ts","sourceRoot":"","sources":["../../../../../src/credential/presentation/v1.0.0/04-verify-certificate-chain.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAEpD,eAAO,MAAM,iCAAiC,EAAE,qBAAqB,CAAC,mCAAmC,CAMtG,CAAC"}
@@ -1,10 +0,0 @@
1
- import { UnimplementedFeatureError } from "../../../utils/errors";
2
- import { type RemotePresentationApi } from "../api";
3
-
4
- export const verifyAuthRequestCertificateChain: RemotePresentationApi["verifyAuthRequestCertificateChain"] =
5
- async () => {
6
- throw new UnimplementedFeatureError(
7
- "verifyAuthRequestCertificateChain",
8
- "1.0.0"
9
- );
10
- };