@pagopa/io-react-native-wallet 0.7.4 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (217) hide show
  1. package/README.md +49 -31
  2. package/lib/commonjs/credential/index.js +13 -0
  3. package/lib/commonjs/credential/index.js.map +1 -0
  4. package/lib/commonjs/credential/issuance/01-start-flow.js +2 -0
  5. package/lib/commonjs/credential/issuance/01-start-flow.js.map +1 -0
  6. package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js +26 -0
  7. package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js.map +1 -0
  8. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +119 -0
  9. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -0
  10. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +6 -0
  11. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -0
  12. package/lib/commonjs/credential/issuance/05-authorize-access.js +63 -0
  13. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -0
  14. package/lib/commonjs/credential/issuance/06-obtain-credential.js +128 -0
  15. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -0
  16. package/lib/commonjs/credential/issuance/07-confirm-credential.js +6 -0
  17. package/lib/commonjs/credential/issuance/07-confirm-credential.js.map +1 -0
  18. package/lib/commonjs/credential/issuance/const.js +9 -0
  19. package/lib/commonjs/credential/issuance/const.js.map +1 -0
  20. package/lib/commonjs/credential/issuance/index.js +34 -0
  21. package/lib/commonjs/credential/issuance/index.js.map +1 -0
  22. package/lib/commonjs/credential/presentation/01-start-flow.js +55 -0
  23. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -0
  24. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +32 -0
  25. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -0
  26. package/lib/commonjs/credential/presentation/03-get-request-object.js +68 -0
  27. package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -0
  28. package/lib/commonjs/credential/presentation/04-send-authorization-response.js +139 -0
  29. package/lib/commonjs/credential/presentation/04-send-authorization-response.js.map +1 -0
  30. package/lib/commonjs/credential/presentation/index.js +34 -0
  31. package/lib/commonjs/credential/presentation/index.js.map +1 -0
  32. package/lib/commonjs/{rp → credential/presentation}/types.js +17 -34
  33. package/lib/commonjs/credential/presentation/types.js.map +1 -0
  34. package/lib/commonjs/index.js +10 -61
  35. package/lib/commonjs/index.js.map +1 -1
  36. package/lib/commonjs/pid/index.js +1 -3
  37. package/lib/commonjs/pid/index.js.map +1 -1
  38. package/lib/commonjs/sd-jwt/index.js +1 -1
  39. package/lib/commonjs/sd-jwt/index.js.map +1 -1
  40. package/lib/commonjs/sd-jwt/types.js +1 -1
  41. package/lib/commonjs/sd-jwt/types.js.map +1 -1
  42. package/lib/commonjs/trust/chain.js +32 -4
  43. package/lib/commonjs/trust/chain.js.map +1 -1
  44. package/lib/commonjs/trust/index.js +105 -20
  45. package/lib/commonjs/trust/index.js.map +1 -1
  46. package/lib/commonjs/trust/types.js +54 -35
  47. package/lib/commonjs/trust/types.js.map +1 -1
  48. package/lib/commonjs/utils/crypto.js +5 -18
  49. package/lib/commonjs/utils/crypto.js.map +1 -1
  50. package/lib/commonjs/utils/errors.js +35 -4
  51. package/lib/commonjs/utils/errors.js.map +1 -1
  52. package/lib/commonjs/utils/misc.js +23 -0
  53. package/lib/commonjs/utils/misc.js.map +1 -0
  54. package/lib/commonjs/utils/par.js +86 -0
  55. package/lib/commonjs/utils/par.js.map +1 -0
  56. package/lib/module/credential/index.js +4 -0
  57. package/lib/module/credential/index.js.map +1 -0
  58. package/lib/module/credential/issuance/01-start-flow.js +2 -0
  59. package/lib/module/credential/issuance/01-start-flow.js.map +1 -0
  60. package/lib/module/credential/issuance/02-evaluate-issuer-trust.js +19 -0
  61. package/lib/module/credential/issuance/02-evaluate-issuer-trust.js.map +1 -0
  62. package/lib/module/credential/issuance/03-start-user-authorization.js +109 -0
  63. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -0
  64. package/lib/module/credential/issuance/04-complete-user-authorization.js +2 -0
  65. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -0
  66. package/lib/module/credential/issuance/05-authorize-access.js +55 -0
  67. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -0
  68. package/lib/module/credential/issuance/06-obtain-credential.js +117 -0
  69. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -0
  70. package/lib/module/credential/issuance/07-confirm-credential.js +2 -0
  71. package/lib/module/credential/issuance/07-confirm-credential.js.map +1 -0
  72. package/lib/module/credential/issuance/const.js +2 -0
  73. package/lib/module/credential/issuance/const.js.map +1 -0
  74. package/lib/module/credential/issuance/index.js +6 -0
  75. package/lib/module/credential/issuance/index.js.map +1 -0
  76. package/lib/module/credential/presentation/01-start-flow.js +46 -0
  77. package/lib/module/credential/presentation/01-start-flow.js.map +1 -0
  78. package/lib/module/credential/presentation/02-evaluate-rp-trust.js +25 -0
  79. package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -0
  80. package/lib/module/credential/presentation/03-get-request-object.js +60 -0
  81. package/lib/module/credential/presentation/03-get-request-object.js.map +1 -0
  82. package/lib/module/credential/presentation/04-send-authorization-response.js +128 -0
  83. package/lib/module/credential/presentation/04-send-authorization-response.js.map +1 -0
  84. package/lib/module/credential/presentation/index.js +6 -0
  85. package/lib/module/credential/presentation/index.js.map +1 -0
  86. package/lib/module/credential/presentation/types.js +21 -0
  87. package/lib/module/credential/presentation/types.js.map +1 -0
  88. package/lib/module/index.js +4 -5
  89. package/lib/module/index.js.map +1 -1
  90. package/lib/module/pid/index.js +1 -2
  91. package/lib/module/pid/index.js.map +1 -1
  92. package/lib/module/sd-jwt/index.js +1 -1
  93. package/lib/module/sd-jwt/index.js.map +1 -1
  94. package/lib/module/sd-jwt/types.js +1 -1
  95. package/lib/module/sd-jwt/types.js.map +1 -1
  96. package/lib/module/trust/chain.js +30 -3
  97. package/lib/module/trust/chain.js.map +1 -1
  98. package/lib/module/trust/index.js +99 -16
  99. package/lib/module/trust/index.js.map +1 -1
  100. package/lib/module/trust/types.js +50 -31
  101. package/lib/module/trust/types.js.map +1 -1
  102. package/lib/module/utils/crypto.js +2 -15
  103. package/lib/module/utils/crypto.js.map +1 -1
  104. package/lib/module/utils/errors.js +35 -4
  105. package/lib/module/utils/errors.js.map +1 -1
  106. package/lib/module/utils/misc.js +17 -0
  107. package/lib/module/utils/misc.js.map +1 -0
  108. package/lib/module/utils/par.js +74 -0
  109. package/lib/module/utils/par.js.map +1 -0
  110. package/lib/typescript/credential/index.d.ts +4 -0
  111. package/lib/typescript/credential/index.d.ts.map +1 -0
  112. package/lib/typescript/credential/issuance/01-start-flow.d.ts +11 -0
  113. package/lib/typescript/credential/issuance/01-start-flow.d.ts.map +1 -0
  114. package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts +18 -0
  115. package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts.map +1 -0
  116. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +31 -0
  117. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -0
  118. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +16 -0
  119. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -0
  120. package/lib/typescript/credential/issuance/05-authorize-access.d.ts +26 -0
  121. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -0
  122. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +32 -0
  123. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -0
  124. package/lib/typescript/credential/issuance/07-confirm-credential.d.ts +11 -0
  125. package/lib/typescript/credential/issuance/07-confirm-credential.d.ts.map +1 -0
  126. package/lib/typescript/credential/issuance/const.d.ts +2 -0
  127. package/lib/typescript/credential/issuance/const.d.ts.map +1 -0
  128. package/lib/typescript/credential/issuance/index.d.ts +10 -0
  129. package/lib/typescript/credential/issuance/index.d.ts.map +1 -0
  130. package/lib/typescript/credential/presentation/01-start-flow.d.ts +20 -0
  131. package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -0
  132. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts +18 -0
  133. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -0
  134. package/lib/typescript/credential/presentation/03-get-request-object.d.ts +25 -0
  135. package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -0
  136. package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts +34 -0
  137. package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts.map +1 -0
  138. package/lib/typescript/credential/presentation/index.d.ts +7 -0
  139. package/lib/typescript/credential/presentation/index.d.ts.map +1 -0
  140. package/lib/typescript/credential/presentation/types.d.ts +49 -0
  141. package/lib/typescript/credential/presentation/types.d.ts.map +1 -0
  142. package/lib/typescript/index.d.ts +4 -5
  143. package/lib/typescript/index.d.ts.map +1 -1
  144. package/lib/typescript/pid/index.d.ts +1 -2
  145. package/lib/typescript/pid/index.d.ts.map +1 -1
  146. package/lib/typescript/sd-jwt/index.d.ts +2 -2
  147. package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
  148. package/lib/typescript/sd-jwt/types.d.ts +5 -5
  149. package/lib/typescript/trust/chain.d.ts +12 -3
  150. package/lib/typescript/trust/chain.d.ts.map +1 -1
  151. package/lib/typescript/trust/index.d.ts +198 -24
  152. package/lib/typescript/trust/index.d.ts.map +1 -1
  153. package/lib/typescript/trust/types.d.ts +1299 -623
  154. package/lib/typescript/trust/types.d.ts.map +1 -1
  155. package/lib/typescript/utils/crypto.d.ts +1 -1
  156. package/lib/typescript/utils/crypto.d.ts.map +1 -1
  157. package/lib/typescript/utils/dpop.d.ts +2 -2
  158. package/lib/typescript/utils/errors.d.ts.map +1 -1
  159. package/lib/typescript/utils/misc.d.ts +8 -0
  160. package/lib/typescript/utils/misc.d.ts.map +1 -0
  161. package/lib/typescript/utils/par.d.ts +68 -0
  162. package/lib/typescript/utils/par.d.ts.map +1 -0
  163. package/package.json +2 -2
  164. package/src/credential/index.ts +4 -0
  165. package/src/credential/issuance/01-start-flow.ts +10 -0
  166. package/src/credential/issuance/02-evaluate-issuer-trust.ts +31 -0
  167. package/src/credential/issuance/03-start-user-authorization.ts +138 -0
  168. package/src/credential/issuance/04-complete-user-authorization.ts +17 -0
  169. package/src/credential/issuance/05-authorize-access.ts +92 -0
  170. package/src/credential/issuance/06-obtain-credential.ts +179 -0
  171. package/src/credential/issuance/07-confirm-credential.ts +14 -0
  172. package/src/credential/issuance/const.ts +2 -0
  173. package/src/credential/issuance/index.ts +32 -0
  174. package/src/credential/presentation/01-start-flow.ts +51 -0
  175. package/src/credential/presentation/02-evaluate-rp-trust.ts +33 -0
  176. package/src/credential/presentation/03-get-request-object.ts +85 -0
  177. package/src/credential/presentation/04-send-authorization-response.ts +168 -0
  178. package/src/credential/presentation/index.ts +26 -0
  179. package/src/credential/presentation/types.ts +27 -0
  180. package/src/index.ts +7 -28
  181. package/src/pid/index.ts +1 -2
  182. package/src/sd-jwt/index.ts +2 -2
  183. package/src/sd-jwt/types.ts +1 -1
  184. package/src/trust/chain.ts +45 -3
  185. package/src/trust/index.ts +136 -19
  186. package/src/trust/types.ts +57 -35
  187. package/src/utils/crypto.ts +2 -20
  188. package/src/utils/errors.ts +40 -8
  189. package/src/utils/misc.ts +23 -0
  190. package/src/utils/par.ts +103 -0
  191. package/lib/commonjs/pid/issuing.js +0 -276
  192. package/lib/commonjs/pid/issuing.js.map +0 -1
  193. package/lib/commonjs/rp/__test__/index.test.js +0 -172
  194. package/lib/commonjs/rp/__test__/index.test.js.map +0 -1
  195. package/lib/commonjs/rp/index.js +0 -239
  196. package/lib/commonjs/rp/index.js.map +0 -1
  197. package/lib/commonjs/rp/types.js.map +0 -1
  198. package/lib/module/pid/issuing.js +0 -266
  199. package/lib/module/pid/issuing.js.map +0 -1
  200. package/lib/module/rp/__test__/index.test.js +0 -168
  201. package/lib/module/rp/__test__/index.test.js.map +0 -1
  202. package/lib/module/rp/index.js +0 -228
  203. package/lib/module/rp/index.js.map +0 -1
  204. package/lib/module/rp/types.js +0 -36
  205. package/lib/module/rp/types.js.map +0 -1
  206. package/lib/typescript/pid/issuing.d.ts +0 -57
  207. package/lib/typescript/pid/issuing.d.ts.map +0 -1
  208. package/lib/typescript/rp/__test__/index.test.d.ts +0 -2
  209. package/lib/typescript/rp/__test__/index.test.d.ts.map +0 -1
  210. package/lib/typescript/rp/index.d.ts +0 -43
  211. package/lib/typescript/rp/index.d.ts.map +0 -1
  212. package/lib/typescript/rp/types.d.ts +0 -122
  213. package/lib/typescript/rp/types.d.ts.map +0 -1
  214. package/src/pid/issuing.ts +0 -405
  215. package/src/rp/__test__/index.test.ts +0 -250
  216. package/src/rp/index.ts +0 -287
  217. package/src/rp/types.ts +0 -42
@@ -135,7 +135,7 @@ export const disclose = async (
135
135
  *
136
136
  *
137
137
  * @param token The encoded token that represents a valid sd-jwt for verifiable credentials
138
- * @param publicKey The public key to validate the signature
138
+ * @param publicKey The single public key or an array of public keys to validate the signature.
139
139
  * @param schema Schema to use to parse the SD-JWT
140
140
  *
141
141
  * @returns The parsed SD-JWT token and the parsed disclosures
@@ -143,7 +143,7 @@ export const disclose = async (
143
143
  */
144
144
  export const verify = async <S extends z.AnyZodObject>(
145
145
  token: string,
146
- publicKey: JWK,
146
+ publicKey: JWK | JWK[],
147
147
  schema: S
148
148
  ): Promise<{ sdJwt: z.infer<S>; disclosures: Disclosure[] }> => {
149
149
  // get decoded data
@@ -51,7 +51,7 @@ export const SdJwt4VC = z.object({
51
51
  cnf: z.object({
52
52
  jwk: JWK,
53
53
  }),
54
- type: z.literal("PersonIdentificationData"),
54
+ type: z.string(),
55
55
  verified_claims: z.object({
56
56
  verification: z.intersection(
57
57
  z.object({
@@ -11,6 +11,7 @@ import { JWK } from "../utils/jwk";
11
11
  import { IoWalletError } from "../utils/errors";
12
12
  import * as z from "zod";
13
13
  import type { JWTDecodeResult } from "@pagopa/io-react-native-jwt/lib/typescript/types";
14
+ import { getSignedEntityConfiguration, getSignedEntityStatement } from ".";
14
15
 
15
16
  type ParsedToken = {
16
17
  header: JWTDecodeResult["protectedHeader"];
@@ -51,12 +52,12 @@ const LastElementShape = z.union([
51
52
  /**
52
53
  * Validates a provided trust chain against a known trust
53
54
  *
54
- * @param trustAnchorEntity
55
- * @param chain
55
+ * @param trustAnchorEntity The entity configuration of the known trust anchor
56
+ * @param chain The chain of statements to be validate
56
57
  * @returns The list of parsed token representing the chain
57
58
  * @throws {IoWalletError} If the chain is not valid
58
59
  */
59
- export async function verifyTrustChain(
60
+ export async function validateTrustChain(
60
61
  trustAnchorEntity: TrustAnchorEntityConfiguration,
61
62
  chain: string[]
62
63
  ): Promise<ParsedToken[]> {
@@ -107,3 +108,44 @@ export async function verifyTrustChain(
107
108
  .map((args) => verify(...args))
108
109
  );
109
110
  }
111
+
112
+ /**
113
+ * Given a trust chain, obtain a new trust chain by fetching each element's fresh version
114
+ *
115
+ * @param chain The original chain
116
+ * @param appFetch (optional) fetch api implementation
117
+ * @returns A list of signed token that reprensent the trust chain, in the same order of the provided chain
118
+ * @throws When an element of the chain fails to parse
119
+ */
120
+ export function renewTrustChain(
121
+ chain: string[],
122
+ appFetch: GlobalFetch["fetch"] = fetch
123
+ ) {
124
+ return Promise.all(
125
+ chain
126
+ // Decode each item to determine its shape
127
+ .map(decode)
128
+ .map(
129
+ (e) =>
130
+ [
131
+ EntityStatement.safeParse(e),
132
+ EntityConfiguration.safeParse(e),
133
+ ] as const
134
+ )
135
+ // fetch the element according to its shape
136
+ .map(([es, ec], i) =>
137
+ ec.success
138
+ ? getSignedEntityConfiguration(ec.data.payload.iss, { appFetch })
139
+ : es.success
140
+ ? getSignedEntityStatement(es.data.payload.iss, es.data.payload.sub, {
141
+ appFetch,
142
+ })
143
+ : // if the element fail to parse in both EntityStatement and EntityConfiguration, raise an error
144
+ Promise.reject(
145
+ new IoWalletError(
146
+ `Cannot renew trust chain because the element #${i} failed to be parsed.`
147
+ )
148
+ )
149
+ )
150
+ );
151
+ }
@@ -5,11 +5,74 @@ import {
5
5
  CredentialIssuerEntityConfiguration,
6
6
  RelyingPartyEntityConfiguration,
7
7
  EntityConfiguration,
8
+ EntityStatement,
8
9
  } from "./types";
9
- import { IoWalletError } from "../utils/errors";
10
- import { verifyTrustChain } from "./chain";
10
+ import { validateTrustChain, renewTrustChain } from "./chain";
11
+ import { hasStatus } from "../utils/misc";
11
12
 
12
- export { verifyTrustChain };
13
+ export type {
14
+ WalletProviderEntityConfiguration,
15
+ TrustAnchorEntityConfiguration,
16
+ CredentialIssuerEntityConfiguration,
17
+ RelyingPartyEntityConfiguration,
18
+ EntityConfiguration,
19
+ EntityStatement,
20
+ };
21
+
22
+ /**
23
+ * Verify a given trust chain is actually valid.
24
+ * It can handle fast chain renewal, which means we try to fetch a fresh version of each statement.
25
+ *
26
+ * @param trustAnchorEntity The entity configuration of the known trust anchor
27
+ * @param chain The chain of statements to be validate
28
+ * @param options.renewOnFail Whether to renew the provided chain if the validation fails at first. Default: true
29
+ * @param options.appFetch Fetch api implementation. Default: the built-in implementation
30
+ * @returns The result of the chain validation
31
+ * @throws {IoWalletError} When either validation or renewal fail
32
+ */
33
+ export async function verifyTrustChain(
34
+ trustAnchorEntity: TrustAnchorEntityConfiguration,
35
+ chain: string[],
36
+ {
37
+ appFetch = fetch,
38
+ renewOnFail = true,
39
+ }: { appFetch?: GlobalFetch["fetch"]; renewOnFail?: boolean } = {}
40
+ ): Promise<ReturnType<typeof validateTrustChain>> {
41
+ try {
42
+ return validateTrustChain(trustAnchorEntity, chain);
43
+ } catch (error) {
44
+ if (renewOnFail) {
45
+ const renewedChain = await renewTrustChain(chain, appFetch);
46
+ return validateTrustChain(trustAnchorEntity, renewedChain);
47
+ } else {
48
+ throw error;
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Fetch the signed entity configuration token for an entity
55
+ *
56
+ * @param entityBaseUrl The url of the entity to fetch
57
+ * @param param.appFetch (optional) fetch api implemention
58
+ * @returns The signed Entity Configuration token
59
+ */
60
+ export async function getSignedEntityConfiguration(
61
+ entityBaseUrl: string,
62
+ {
63
+ appFetch = fetch,
64
+ }: {
65
+ appFetch?: GlobalFetch["fetch"];
66
+ } = {}
67
+ ): Promise<string> {
68
+ const wellKnownUrl = `${entityBaseUrl}/.well-known/openid-federation`;
69
+
70
+ return await appFetch(wellKnownUrl, {
71
+ method: "GET",
72
+ })
73
+ .then(hasStatus(200))
74
+ .then((res) => res.text());
75
+ }
13
76
 
14
77
  /**
15
78
  * Fetch and parse the entity configuration document for a given federation entity.
@@ -77,24 +140,15 @@ async function fetchAndParseEntityConfiguration(
77
140
  appFetch?: GlobalFetch["fetch"];
78
141
  } = {}
79
142
  ) {
80
- const wellKnownUrl = `${entityBaseUrl}/.well-known/openid-federation`;
81
-
82
- const response = await appFetch(wellKnownUrl, {
83
- method: "GET",
143
+ const responseText = await getSignedEntityConfiguration(entityBaseUrl, {
144
+ appFetch,
84
145
  });
85
146
 
86
- if (response.status === 200) {
87
- const responseText = await response.text();
88
- const responseJwt = decodeJwt(responseText);
89
- return schema.parse({
90
- header: responseJwt.protectedHeader,
91
- payload: responseJwt.payload,
92
- });
93
- }
94
-
95
- throw new IoWalletError(
96
- `Unable to obtain Entity Configuration at ${wellKnownUrl}. Response code: ${response.status}`
97
- );
147
+ const responseJwt = decodeJwt(responseText);
148
+ return schema.parse({
149
+ header: responseJwt.protectedHeader,
150
+ payload: responseJwt.payload,
151
+ });
98
152
  }
99
153
 
100
154
  export const getWalletProviderEntityConfiguration = (
@@ -142,3 +196,66 @@ export const getEntityConfiguration = (
142
196
  options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
143
197
  ) =>
144
198
  fetchAndParseEntityConfiguration(entityBaseUrl, EntityConfiguration, options);
199
+
200
+ /**
201
+ * Fetch and parse the entity statement document for a given federation entity.
202
+ *
203
+ * @param accreditationBodyBaseUrl The base url of the accreditaion body which holds and signs the required entity statement
204
+ * @param subordinatedEntityBaseUrl The url that identifies the subordinate entity
205
+ * @param options.appFetch An optional instance of the http client to be used.
206
+ * @returns The parsed entity configuration object
207
+ * @throws {IoWalletError} If the http request fails
208
+ * @throws Parse error if the document is not in the expected shape.
209
+ */
210
+ export async function getEntityStatement(
211
+ accreditationBodyBaseUrl: string,
212
+ subordinatedEntityBaseUrl: string,
213
+ {
214
+ appFetch = fetch,
215
+ }: {
216
+ appFetch?: GlobalFetch["fetch"];
217
+ } = {}
218
+ ) {
219
+ const responseText = await getSignedEntityStatement(
220
+ accreditationBodyBaseUrl,
221
+ subordinatedEntityBaseUrl,
222
+ {
223
+ appFetch,
224
+ }
225
+ );
226
+
227
+ const responseJwt = decodeJwt(responseText);
228
+ return EntityStatement.parse({
229
+ header: responseJwt.protectedHeader,
230
+ payload: responseJwt.payload,
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Fetch the entity statement document for a given federation entity.
236
+ *
237
+ * @param accreditationBodyBaseUrl The base url of the accreditaion body which holds and signs the required entity statement
238
+ * @param subordinatedEntityBaseUrl The url that identifies the subordinate entity
239
+ * @param options.appFetch An optional instance of the http client to be used.
240
+ * @returns The signed entity statement token
241
+ * @throws {IoWalletError} If the http request fails
242
+ */
243
+ export async function getSignedEntityStatement(
244
+ accreditationBodyBaseUrl: string,
245
+ subordinatedEntityBaseUrl: string,
246
+ {
247
+ appFetch = fetch,
248
+ }: {
249
+ appFetch?: GlobalFetch["fetch"];
250
+ } = {}
251
+ ) {
252
+ const url = `${accreditationBodyBaseUrl}/fetch?${new URLSearchParams({
253
+ sub: subordinatedEntityBaseUrl,
254
+ })}`;
255
+
256
+ return await appFetch(url, {
257
+ method: "GET",
258
+ })
259
+ .then(hasStatus(200))
260
+ .then((res) => res.text());
261
+ }
@@ -5,6 +5,15 @@ import * as z from "zod";
5
5
  export const TrustMark = z.object({ id: z.string(), trust_mark: z.string() });
6
6
  export type TrustMark = z.infer<typeof TrustMark>;
7
7
 
8
+ const RelyingPartyMetadata = z.object({
9
+ application_type: z.string().optional(),
10
+ client_id: z.string().optional(),
11
+ client_name: z.string().optional(),
12
+ jwks: z.object({ keys: z.array(JWK) }),
13
+ contacts: z.array(z.string()).optional(),
14
+ });
15
+ //.passthrough();
16
+
8
17
  // Display metadata for a credential, used by the issuer to
9
18
  // instruct the Wallet Solution on how to render the credential correctly
10
19
  type CredentialDisplayMetadata = z.infer<typeof CredentialDisplayMetadata>;
@@ -19,13 +28,28 @@ const CredentialDisplayMetadata = z.object({
19
28
  text_color: z.string(),
20
29
  });
21
30
 
31
+ type CredentialDefinitionMetadata = z.infer<
32
+ typeof CredentialDefinitionMetadata
33
+ >;
34
+ const CredentialDefinitionMetadata = z.object({
35
+ type: z.array(z.string()),
36
+ credentialSubject: z.record(
37
+ z.object({
38
+ mandatory: z.boolean(),
39
+ display: z.array(z.object({ name: z.string(), locale: z.string() })),
40
+ })
41
+ ),
42
+ });
43
+
22
44
  // Metadata for a credentia which i supported by a Issuer
23
45
  type SupportedCredentialMetadata = z.infer<typeof SupportedCredentialMetadata>;
24
46
  const SupportedCredentialMetadata = z.object({
47
+ id: z.string(),
25
48
  format: z.literal("vc+sd-jwt"),
26
49
  cryptographic_binding_methods_supported: z.array(z.string()),
27
50
  cryptographic_suites_supported: z.array(z.string()),
28
51
  display: z.array(CredentialDisplayMetadata),
52
+ credential_definition: CredentialDefinitionMetadata,
29
53
  });
30
54
 
31
55
  export type EntityStatement = z.infer<typeof EntityStatement>;
@@ -54,6 +78,20 @@ export const EntityConfigurationHeader = z.object({
54
78
  kid: z.string(),
55
79
  });
56
80
 
81
+ const FederationEntityMetadata = z
82
+ .object({
83
+ federation_fetch_endpoint: z.string().optional(),
84
+ federation_list_endpoint: z.string().optional(),
85
+ federation_resolve_endpoint: z.string().optional(),
86
+ federation_trust_mark_status_endpoint: z.string().optional(),
87
+ federation_trust_mark_list_endpoint: z.string().optional(),
88
+ homepage_uri: z.string().optional(),
89
+ policy_uri: z.string().optional(),
90
+ logo_uri: z.string().optional(),
91
+ contacts: z.array(z.string()).optional(),
92
+ })
93
+ .passthrough();
94
+
57
95
  // Structuire common to every Entity Configuration document
58
96
  const BaseEntityConfiguration = z.object({
59
97
  header: EntityConfigurationHeader,
@@ -68,19 +106,7 @@ const BaseEntityConfiguration = z.object({
68
106
  }),
69
107
  metadata: z
70
108
  .object({
71
- federation_entity: z
72
- .object({
73
- federation_fetch_endpoint: z.string().optional(),
74
- federation_list_endpoint: z.string().optional(),
75
- federation_resolve_endpoint: z.string().optional(),
76
- federation_trust_mark_status_endpoint: z.string().optional(),
77
- federation_trust_mark_list_endpoint: z.string().optional(),
78
- homepage_uri: z.string().optional(),
79
- policy_uri: z.string().optional(),
80
- logo_uri: z.string().optional(),
81
- contacts: z.array(z.string()).optional(),
82
- })
83
- .passthrough(),
109
+ federation_entity: FederationEntityMetadata,
84
110
  })
85
111
  .passthrough(),
86
112
  authority_hints: z.array(z.string()).optional(),
@@ -113,6 +139,24 @@ export const CredentialIssuerEntityConfiguration = BaseEntityConfiguration.and(
113
139
  credentials_supported: z.array(SupportedCredentialMetadata),
114
140
  jwks: z.object({ keys: z.array(JWK) }),
115
141
  }),
142
+ /** Credential Issuers act as Relying Party
143
+ when they require the presentation of other credentials.
144
+ This does not apply for PID issuance, which requires CIE authz. */
145
+ wallet_relying_party: RelyingPartyMetadata.optional(),
146
+ }),
147
+ }),
148
+ })
149
+ );
150
+
151
+ // Entity configuration for a Relying Party
152
+ export type RelyingPartyEntityConfiguration = z.infer<
153
+ typeof RelyingPartyEntityConfiguration
154
+ >;
155
+ export const RelyingPartyEntityConfiguration = BaseEntityConfiguration.and(
156
+ z.object({
157
+ payload: z.object({
158
+ metadata: z.object({
159
+ wallet_relying_party: RelyingPartyMetadata,
116
160
  }),
117
161
  }),
118
162
  })
@@ -145,28 +189,6 @@ export const WalletProviderEntityConfiguration = BaseEntityConfiguration.and(
145
189
  })
146
190
  );
147
191
 
148
- // Entity configuration for a Relying Party
149
- export type RelyingPartyEntityConfiguration = z.infer<
150
- typeof RelyingPartyEntityConfiguration
151
- >;
152
- export const RelyingPartyEntityConfiguration = BaseEntityConfiguration.and(
153
- z.object({
154
- payload: z.object({
155
- metadata: z.object({
156
- wallet_relying_party: z
157
- .object({
158
- application_type: z.string().optional(),
159
- client_id: z.string().optional(),
160
- client_name: z.string().optional(),
161
- jwks: z.object({ keys: z.array(JWK) }),
162
- contacts: z.array(z.string()).optional(),
163
- })
164
- .passthrough(),
165
- }),
166
- }),
167
- })
168
- );
169
-
170
192
  // Maps any entity configuration by the union of every possible shapes
171
193
  export type EntityConfiguration = z.infer<typeof EntityConfiguration>;
172
194
  export const EntityConfiguration = z.union(
@@ -46,24 +46,6 @@ export const createCryptoContextFor = (keytag: string): CryptoContext => {
46
46
  };
47
47
  };
48
48
 
49
- // Wraps finally for async expressions
50
- const asyncFinally =
51
- <A extends Array<unknown>, R>(
52
- fn: (...args: A) => Promise<R>,
53
- onFinally: () => void | Promise<void>
54
- ) =>
55
- async (...args: A): Promise<R> => {
56
- try {
57
- return await fn(...args);
58
- // ^^^^^ return await is usually to be avoided,
59
- // in this case is needed for the finally{} statement to be executed correctly
60
- } catch (error) {
61
- throw error;
62
- } finally {
63
- await onFinally();
64
- }
65
- };
66
-
67
49
  /**
68
50
  * Executes the input function injecting an ephemeral crypto context.
69
51
  * An ephemeral crypto context is a context which is bound to a key
@@ -72,12 +54,12 @@ const asyncFinally =
72
54
  * @param fn The procedure to be executed
73
55
  * @returns The returned value of the input procedure.
74
56
  */
75
- export const useEphemeralKey = async <R>(
57
+ export const withEphemeralKey = async <R>(
76
58
  fn: (ephemeralContext: CryptoContext) => Promise<R>
77
59
  ): Promise<R> => {
78
60
  // Use an ephemeral key to be destroyed after use
79
61
  const keytag = `ephemeral-${uuid.v4()}`;
80
62
  await generate(keytag);
81
63
  const ephemeralContext = createCryptoContextFor(keytag);
82
- return asyncFinally(fn, () => deleteKey(keytag))(ephemeralContext);
64
+ return fn(ephemeralContext).finally(() => deleteKey(keytag));
83
65
  };
@@ -1,3 +1,19 @@
1
+ /**
2
+ * utility to format a set of attributes into an error message string
3
+ *
4
+ * @example
5
+ * // returns "foo=value bar=(list, item)"
6
+ * serializeAttrs({ foo: "value", bar: ["list", "item"] })
7
+ *
8
+ * @param attrs A key value record set
9
+ * @returns a human-readable serialization of the set
10
+ */
11
+ const serializeAttrs = (attrs: Record<string, string | string>): string =>
12
+ Object.entries(attrs)
13
+ .map(([k, v]) => [k, Array.isArray(v) ? `(${v.join(", ")})` : v])
14
+ .map((_) => _.join("="))
15
+ .join(" ");
16
+
1
17
  /**
2
18
  * A generic Error that all other io-wallet specific Error subclasses extend.
3
19
  *
@@ -42,8 +58,12 @@ export class ValidationFailed extends IoWalletError {
42
58
  /** Reason code for the validation failure. */
43
59
  reason: string;
44
60
 
45
- constructor(message: string, claim = "unspecified", reason = "unspecified") {
46
- super(message);
61
+ constructor(
62
+ message: string,
63
+ claim: string = "unspecified",
64
+ reason: string = "unspecified"
65
+ ) {
66
+ super(serializeAttrs({ message, claim, reason }));
47
67
  this.claim = claim;
48
68
  this.reason = reason;
49
69
  }
@@ -66,8 +86,12 @@ export class WalletInstanceAttestationIssuingError extends IoWalletError {
66
86
  /** Reason code for the validation failure. */
67
87
  reason: string;
68
88
 
69
- constructor(message: string, claim = "unspecified", reason = "unspecified") {
70
- super(message);
89
+ constructor(
90
+ message: string,
91
+ claim: string = "unspecified",
92
+ reason: string = "unspecified"
93
+ ) {
94
+ super(serializeAttrs({ message, claim, reason }));
71
95
  this.claim = claim;
72
96
  this.reason = reason;
73
97
  }
@@ -90,8 +114,12 @@ export class AuthRequestDecodeError extends IoWalletError {
90
114
  /** Reason code for the validation failure. */
91
115
  reason: string;
92
116
 
93
- constructor(message: string, claim = "unspecified", reason = "unspecified") {
94
- super(message);
117
+ constructor(
118
+ message: string,
119
+ claim: string = "unspecified",
120
+ reason: string = "unspecified"
121
+ ) {
122
+ super(serializeAttrs({ message, claim, reason }));
95
123
  this.claim = claim;
96
124
  this.reason = reason;
97
125
  }
@@ -114,8 +142,12 @@ export class PidIssuingError extends IoWalletError {
114
142
  /** Reason code for the validation failure. */
115
143
  reason: string;
116
144
 
117
- constructor(message: string, claim = "unspecified", reason = "unspecified") {
118
- super(message);
145
+ constructor(
146
+ message: string,
147
+ claim: string = "unspecified",
148
+ reason: string = "unspecified"
149
+ ) {
150
+ super(serializeAttrs({ message, claim, reason }));
119
151
  this.claim = claim;
120
152
  this.reason = reason;
121
153
  }
@@ -0,0 +1,23 @@
1
+ import { IoWalletError } from "./errors";
2
+
3
+ /**
4
+ * Check if a response is in the expected status, other
5
+ * @param status The expected status
6
+ * @returns The given response object
7
+ */
8
+ export const hasStatus =
9
+ (status: number) =>
10
+ (res: Response): Response => {
11
+ if (res.status !== status) {
12
+ throw new IoWalletError(
13
+ `Http request failed. Expected ${status}, got ${res.status}, url: ${res.url}`
14
+ );
15
+ }
16
+ return res;
17
+ };
18
+
19
+ // extract a type from an async function output
20
+ // helpful to bind the input of a function to the output of another
21
+ export type Out<FN> = FN extends (...args: any[]) => Promise<any>
22
+ ? Awaited<ReturnType<FN>>
23
+ : never;
@@ -0,0 +1,103 @@
1
+ import {
2
+ sha256ToBase64,
3
+ type CryptoContext,
4
+ SignJWT,
5
+ } from "@pagopa/io-react-native-jwt";
6
+ import uuid from "react-native-uuid";
7
+ import * as z from "zod";
8
+ import * as WalletInstanceAttestation from "../wallet-instance-attestation";
9
+ import { hasStatus } from "./misc";
10
+
11
+ export type AuthorizationDetail = z.infer<typeof AuthorizationDetail>;
12
+ export const AuthorizationDetail = z.object({
13
+ credential_definition: z.object({
14
+ type: z.string(),
15
+ }),
16
+ format: z.literal("vc+sd-jwt"),
17
+ type: z.literal("openid_credential"),
18
+ });
19
+
20
+ export type AuthorizationDetails = z.infer<typeof AuthorizationDetails>;
21
+ export const AuthorizationDetails = z.array(AuthorizationDetail);
22
+
23
+ /**
24
+ * Make a PAR request to the issuer and return the response url
25
+ */
26
+ export const makeParRequest =
27
+ ({
28
+ wiaCryptoContext,
29
+ appFetch = fetch,
30
+ }: {
31
+ wiaCryptoContext: CryptoContext;
32
+ appFetch?: GlobalFetch["fetch"];
33
+ }) =>
34
+ async (
35
+ clientId: string,
36
+ codeVerifier: string,
37
+ walletProviderBaseUrl: string,
38
+ parEndpoint: string,
39
+ walletInstanceAttestation: string,
40
+ authorizationDetails: AuthorizationDetails,
41
+ assertionType: string
42
+ ): Promise<string> => {
43
+ const wiaPublicKey = await wiaCryptoContext.getPublicKey();
44
+
45
+ const parUrl = new URL(parEndpoint);
46
+ const aud = `${parUrl.protocol}//${parUrl.hostname}`;
47
+
48
+ const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
49
+ .payload.cnf.jwk.kid;
50
+
51
+ /** A code challenge is provided so that the PAR is bound
52
+ to the subsequent authorization code request
53
+ @see https://datatracker.ietf.org/doc/html/rfc9126#name-request */
54
+ const codeChallengeMethod = "s256";
55
+ const codeChallenge = await sha256ToBase64(codeVerifier);
56
+
57
+ /** The PAR request token is signed used the Wallet Instance Attestation key.
58
+ The signature can be verified by reading the public key from the key set shippet
59
+ with the it will ship the Wallet Instance Attestation.
60
+ The key is matched by its kid */
61
+ const signedJwtForPar = await new SignJWT(wiaCryptoContext)
62
+ .setProtectedHeader({
63
+ kid: wiaPublicKey.kid,
64
+ })
65
+ .setPayload({
66
+ iss,
67
+ aud,
68
+ jti: `${uuid.v4()}`,
69
+ client_assertion_type: assertionType,
70
+ authorization_details: authorizationDetails,
71
+ response_type: "code",
72
+ redirect_uri: walletProviderBaseUrl,
73
+ state: `${uuid.v4()}`,
74
+ client_id: clientId,
75
+ code_challenge_method: codeChallengeMethod,
76
+ code_challenge: codeChallenge,
77
+ })
78
+ .setIssuedAt()
79
+ .setExpirationTime("1h")
80
+ .sign();
81
+
82
+ /** The request body for the Pushed Authorization Request */
83
+ var formBody = new URLSearchParams({
84
+ response_type: "code",
85
+ client_id: clientId,
86
+ code_challenge: codeChallenge,
87
+ code_challenge_method: "S256",
88
+ client_assertion_type: assertionType,
89
+ client_assertion: walletInstanceAttestation,
90
+ request: signedJwtForPar,
91
+ });
92
+
93
+ return await appFetch(parEndpoint, {
94
+ method: "POST",
95
+ headers: {
96
+ "Content-Type": "application/x-www-form-urlencoded",
97
+ },
98
+ body: formBody.toString(),
99
+ })
100
+ .then(hasStatus(201))
101
+ .then((res) => res.json())
102
+ .then((result) => result.request_uri);
103
+ };