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

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 (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
@@ -0,0 +1,179 @@
1
+ import * as z from "zod";
2
+ import uuid from "react-native-uuid";
3
+ import { SignJWT, type CryptoContext } from "@pagopa/io-react-native-jwt";
4
+ import { verify as verifySdJwt } from "../../sd-jwt";
5
+ import { createDPopToken } from "../../utils/dpop";
6
+
7
+ import type { StartFlow } from "./01-start-flow";
8
+ import { hasStatus, type Out } from "../../utils/misc";
9
+ import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
10
+ import type { AuthorizeAccess } from "./05-authorize-access";
11
+ import { SdJwt4VC } from "../../sd-jwt/types";
12
+ import { IoWalletError } from "../../utils/errors";
13
+ import type { JWK } from "../../utils/jwk";
14
+
15
+ /**
16
+ * Return the signed jwt for nonce proof of possession
17
+ */
18
+ export const createNonceProof = async (
19
+ nonce: string,
20
+ issuer: string,
21
+ audience: string,
22
+ ctx: CryptoContext
23
+ ): Promise<string> => {
24
+ return new SignJWT(ctx)
25
+ .setPayload({
26
+ nonce,
27
+ jwk: await ctx.getPublicKey(),
28
+ })
29
+ .setProtectedHeader({
30
+ type: "openid4vci-proof+jwt",
31
+ })
32
+ .setAudience(audience)
33
+ .setIssuer(issuer)
34
+ .setIssuedAt()
35
+ .setExpirationTime("1h")
36
+ .sign();
37
+ };
38
+
39
+ /**
40
+ * Given a credential, verify it's in the supported format
41
+ * and the credential is correctly signed
42
+ * and it's bound to the given key
43
+ *
44
+ * @param rawCredential The received credential
45
+ * @param issuerKeys The set of public keys of the issuer,
46
+ * which will be used to verify the signature
47
+ * @param holderBindingContext The access to the holder's key
48
+ *
49
+ * @throws If the signature verification fails
50
+ * @throws If the credential is not in the SdJwt4VC format
51
+ * @throws If the holder binding is not properly configured
52
+ *
53
+ */
54
+ async function verifyCredential(
55
+ rawCredential: string,
56
+ issuerKeys: JWK[],
57
+ holderBindingContext: CryptoContext
58
+ ): Promise<void> {
59
+ const [{ sdJwt }, holderBindingKey] =
60
+ // parallel for optimization
61
+ await Promise.all([
62
+ verifySdJwt(rawCredential, issuerKeys, SdJwt4VC),
63
+ holderBindingContext.getPublicKey(),
64
+ ]);
65
+
66
+ if (
67
+ !sdJwt.payload.cnf.jwk.kid ||
68
+ sdJwt.payload.cnf.jwk.kid !== holderBindingKey.kid
69
+ ) {
70
+ throw new IoWalletError(
71
+ `Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${sdJwt.payload.cnf.jwk.kid}`
72
+ );
73
+ }
74
+ }
75
+
76
+ const CredentialEndpointResponse = z.object({
77
+ credential: z.string(),
78
+ format: z.literal("vc+sd-jwt"),
79
+ });
80
+
81
+ export type ObtainCredential = (
82
+ issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
83
+ accessToken: Out<AuthorizeAccess>["accessToken"],
84
+ nonce: Out<AuthorizeAccess>["nonce"],
85
+ clientId: Out<AuthorizeAccess>["clientId"],
86
+ credentialType: Out<StartFlow>["credentialType"],
87
+ context: {
88
+ credentialCryptoContext: CryptoContext;
89
+ walletProviderBaseUrl: string;
90
+ appFetch?: GlobalFetch["fetch"];
91
+ }
92
+ ) => Promise<{ credential: string; format: string }>;
93
+
94
+ /**
95
+ * Fetch a credential from the issuer
96
+ *
97
+ * @param issuerConf The Issuer configuration
98
+ * @param accessToken The access token to grant access to the credential, obtained with the access authorization step
99
+ * @param nonce The nonce value to prevent reply attacks, obtained with the access authorization step
100
+ * @param clientId Identifies the current client across all the requests of the issuing flow
101
+ * @param credentialType The type of the credential to be requested
102
+ * @param context.credentialCryptoContext The context to access the key the Credential will be bound to
103
+ * @param context.walletProviderBaseUrl The base url of the Wallet Provider
104
+ * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
105
+ * @returns The signed credential token
106
+ */
107
+ export const obtainCredential: ObtainCredential = async (
108
+ issuerConf,
109
+ accessToken,
110
+ nonce,
111
+ clientId,
112
+ credentialType,
113
+ context
114
+ ) => {
115
+ const {
116
+ credentialCryptoContext,
117
+ walletProviderBaseUrl,
118
+ appFetch = fetch,
119
+ } = context;
120
+
121
+ const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
122
+
123
+ /** DPoP token for demonstating the possession
124
+ of the key that will bind the holder User with the Credential
125
+ @see https://datatracker.ietf.org/doc/html/rfc9449 */
126
+ const signedDPopForPid = await createDPopToken(
127
+ {
128
+ htm: "POST",
129
+ htu: credentialUrl,
130
+ jti: `${uuid.v4()}`,
131
+ },
132
+ credentialCryptoContext
133
+ );
134
+
135
+ /** JWT proof token to bind the request nonce
136
+ to the key that will bind the holder User with the Credential
137
+ @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-proof-types */
138
+ const signedNonceProof = await createNonceProof(
139
+ nonce,
140
+ clientId,
141
+ walletProviderBaseUrl,
142
+ credentialCryptoContext
143
+ );
144
+
145
+ /** The credential request body */
146
+ const formBody = new URLSearchParams({
147
+ credential_definition: JSON.stringify({
148
+ type: [credentialType],
149
+ }),
150
+ format: "vc+sd-jwt",
151
+ proof: JSON.stringify({
152
+ jwt: signedNonceProof,
153
+ proof_type: "jwt",
154
+ }),
155
+ });
156
+
157
+ const { credential, format } = await appFetch(credentialUrl, {
158
+ method: "POST",
159
+ headers: {
160
+ "Content-Type": "application/x-www-form-urlencoded",
161
+ DPoP: signedDPopForPid,
162
+ Authorization: accessToken,
163
+ },
164
+ body: formBody.toString(),
165
+ })
166
+ .then(hasStatus(200))
167
+ .then((res) => res.json())
168
+ .then(CredentialEndpointResponse.parse);
169
+
170
+ /** validate the received credential signature
171
+ is correct and refers to the public keys of the issuer */
172
+ await verifyCredential(
173
+ credential,
174
+ issuerConf.openid_credential_issuer.jwks.keys,
175
+ credentialCryptoContext
176
+ );
177
+
178
+ return { credential, format };
179
+ };
@@ -0,0 +1,14 @@
1
+ import type { ObtainCredential } from "./06-obtain-credential";
2
+ import type { Out } from "../../utils/misc";
3
+
4
+ /**
5
+ * The end of the issuing flow.
6
+ * The User accepted the Credential and it can be stored in the device according to the app implementation preferences.
7
+ * To be implemented.
8
+ *
9
+ * @returns The type of the Credential to be issued and the url of the Issuer
10
+ */
11
+ export type ConfirmCredential = (
12
+ credential: Out<ObtainCredential>["credential"],
13
+ format: Out<ObtainCredential>["format"]
14
+ ) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export const ASSERTION_TYPE =
2
+ "urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation";
@@ -0,0 +1,32 @@
1
+ import { type StartFlow } from "./01-start-flow";
2
+ import {
3
+ evaluateIssuerTrust,
4
+ type EvaluateIssuerTrust,
5
+ } from "./02-evaluate-issuer-trust";
6
+ import {
7
+ startUserAuthorization,
8
+ type StartUserAuthorization,
9
+ } from "./03-start-user-authorization";
10
+ import { type CompleteUserAuthorization } from "./04-complete-user-authorization";
11
+ import { authorizeAccess, type AuthorizeAccess } from "./05-authorize-access";
12
+ import {
13
+ obtainCredential,
14
+ type ObtainCredential,
15
+ } from "./06-obtain-credential";
16
+ import type { ConfirmCredential } from "./07-confirm-credential";
17
+
18
+ export {
19
+ evaluateIssuerTrust,
20
+ startUserAuthorization,
21
+ authorizeAccess,
22
+ obtainCredential,
23
+ };
24
+ export type {
25
+ StartFlow,
26
+ EvaluateIssuerTrust,
27
+ StartUserAuthorization,
28
+ CompleteUserAuthorization,
29
+ AuthorizeAccess,
30
+ ObtainCredential,
31
+ ConfirmCredential,
32
+ };
@@ -0,0 +1,51 @@
1
+ import * as z from "zod";
2
+ import { decodeBase64 } from "@pagopa/io-react-native-jwt";
3
+ import { AuthRequestDecodeError } from "../../utils/errors";
4
+
5
+ const QRCodePayload = z.object({
6
+ protocol: z.string(),
7
+ resource: z.string(), // TODO: refine to known paths using literals
8
+ clientId: z.string(),
9
+ requestURI: z.string(),
10
+ });
11
+
12
+ /**
13
+ * The beginning of the presentation flow.
14
+ * To be implemented accordind to the user touchpoint
15
+ *
16
+ * @param Optional parameters, depending on the starting touchoint
17
+ * @returns The url for the Relying Party to connect with
18
+ */
19
+ export type StartFlow<T extends Array<unknown> = []> = (...args: T) => Promise<{
20
+ requestURI: string;
21
+ clientId: string;
22
+ }>;
23
+
24
+ /**
25
+ * Start a presentation flow by decoding an incoming QR-code
26
+ *
27
+ * @param qrcode The encoded QR-code content
28
+ * @returns The url for the Relying Party to connect with
29
+ * @throws If the provided qr code fails to be decoded
30
+ */
31
+ export const startFlowFromQR: StartFlow<[string]> = async (qrcode) => {
32
+ const decoded = decodeBase64(qrcode);
33
+ const decodedUrl = new URL(decoded);
34
+ const protocol = decodedUrl.protocol;
35
+ const resource = decodedUrl.hostname;
36
+ const requestURI = decodedUrl.searchParams.get("request_uri");
37
+ const clientId = decodedUrl.searchParams.get("client_id");
38
+
39
+ const result = QRCodePayload.safeParse({
40
+ protocol,
41
+ resource,
42
+ requestURI,
43
+ clientId,
44
+ });
45
+
46
+ if (result.success) {
47
+ return result.data;
48
+ } else {
49
+ throw new AuthRequestDecodeError(result.error.message, `${decodedUrl}`);
50
+ }
51
+ };
@@ -0,0 +1,33 @@
1
+ import { getRelyingPartyEntityConfiguration } from "../../trust";
2
+ import { RelyingPartyEntityConfiguration } from "../../trust/types";
3
+ import type { StartFlow } from "../issuance/01-start-flow";
4
+ import type { Out } from "../../utils/misc";
5
+
6
+ export type EvaluateRelyingPartyTrust = (
7
+ rpUrl: Out<StartFlow>["issuerUrl"],
8
+ context?: {
9
+ appFetch?: GlobalFetch["fetch"];
10
+ }
11
+ ) => Promise<{
12
+ rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
13
+ }>;
14
+
15
+ /**
16
+ * The Relying Party trust evaluation phase.
17
+ * Fetch the Relying Party's configuration and verify trust.
18
+ *
19
+ * @param rpUrl The base url of the Issuer
20
+ * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
21
+ * @returns The Relying Party's configuration
22
+ */
23
+ export const evaluateRelyingPartyTrust: EvaluateRelyingPartyTrust = async (
24
+ rpUrl,
25
+ { appFetch = fetch } = {}
26
+ ) => {
27
+ const {
28
+ payload: { metadata: rpConf },
29
+ } = await getRelyingPartyEntityConfiguration(rpUrl, {
30
+ appFetch,
31
+ });
32
+ return { rpConf };
33
+ };
@@ -0,0 +1,85 @@
1
+ import uuid from "react-native-uuid";
2
+ import {
3
+ decode as decodeJwt,
4
+ sha256ToBase64,
5
+ verify,
6
+ type CryptoContext,
7
+ } from "@pagopa/io-react-native-jwt";
8
+
9
+ import { createDPopToken } from "../../utils/dpop";
10
+ import { NoSuitableKeysFoundInEntityConfiguration } from "../../utils/errors";
11
+ import type { EvaluateRelyingPartyTrust } from "./02-evaluate-rp-trust";
12
+ import { hasStatus, type Out } from "../../utils/misc";
13
+ import type { StartFlow } from "./01-start-flow";
14
+ import { RequestObject } from "./types";
15
+
16
+ export type GetRequestObject = (
17
+ requestUri: Out<StartFlow>["requestURI"],
18
+ rpConf: Out<EvaluateRelyingPartyTrust>["rpConf"],
19
+ context: {
20
+ wiaCryptoContext: CryptoContext;
21
+ appFetch?: GlobalFetch["fetch"];
22
+ walletInstanceAttestation: string;
23
+ }
24
+ ) => Promise<{ requestObject: RequestObject }>;
25
+
26
+ /**
27
+ * Obtain the Request Object for RP authentication
28
+ * @see https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/relying-party-solution.html
29
+ *
30
+ * @param requestUri The url for the Relying Party to connect with
31
+ * @param rpConf The Relying Party's configuration
32
+ * @param context.wiaCryptoContext The context to access the key associated with the Wallet Instance Attestation
33
+ * @param context.walletInstanceAttestation The Wallet Instance Attestation token
34
+ * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
35
+ * @returns The Request Object that describes the presentation
36
+ */
37
+ export const getRequestObject: GetRequestObject = async (
38
+ requestUri,
39
+ rpConf,
40
+ { wiaCryptoContext, appFetch = fetch, walletInstanceAttestation }
41
+ ) => {
42
+ const signedWalletInstanceDPoP = await createDPopToken(
43
+ {
44
+ jti: `${uuid.v4()}`,
45
+ htm: "GET",
46
+ htu: requestUri,
47
+ ath: await sha256ToBase64(walletInstanceAttestation),
48
+ },
49
+ wiaCryptoContext
50
+ );
51
+
52
+ const responseEncodedJwt = await appFetch(requestUri, {
53
+ method: "GET",
54
+ headers: {
55
+ Authorization: `DPoP ${walletInstanceAttestation}`,
56
+ DPoP: signedWalletInstanceDPoP,
57
+ },
58
+ })
59
+ .then(hasStatus(200))
60
+ .then((res) => res.json())
61
+ .then((responseJson) => responseJson.response);
62
+
63
+ const responseJwt = decodeJwt(responseEncodedJwt);
64
+
65
+ // verify token signature according to RP's entity configuration
66
+ // to ensure the request object is authentic
67
+ {
68
+ const pubKey = rpConf.wallet_relying_party.jwks.keys.find(
69
+ ({ kid }) => kid === responseJwt.protectedHeader.kid
70
+ );
71
+ if (!pubKey) {
72
+ throw new NoSuitableKeysFoundInEntityConfiguration(
73
+ "Request Object signature verification"
74
+ );
75
+ }
76
+ await verify(responseEncodedJwt, pubKey);
77
+ }
78
+
79
+ // Ensure that the request object conforms to the expected specification.
80
+ const requestObject = RequestObject.parse(responseJwt.payload);
81
+
82
+ return {
83
+ requestObject,
84
+ };
85
+ };
@@ -0,0 +1,168 @@
1
+ import { EncryptJwe, SignJWT } from "@pagopa/io-react-native-jwt";
2
+ import uuid from "react-native-uuid";
3
+ import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
4
+ import type { JWK } from "@pagopa/io-react-native-jwt/lib/typescript/types";
5
+ import { NoSuitableKeysFoundInEntityConfiguration } from "../../utils/errors";
6
+ import { hasStatus, type Out } from "../../utils/misc";
7
+ import type { GetRequestObject } from "./03-get-request-object";
8
+ import { disclose } from "../../sd-jwt";
9
+ import type { EvaluateRelyingPartyTrust } from "./02-evaluate-rp-trust";
10
+ import { type Presentation } from "./types";
11
+ import * as z from "zod";
12
+
13
+ export type AuthorizationResponse = z.infer<typeof AuthorizationResponse>;
14
+ export const AuthorizationResponse = z.object({
15
+ status: z.string(),
16
+ response_code: z
17
+ .string() /**
18
+ FIXME: [SIW-627] we expect this value from every RP implementation
19
+ Actually some RP does not return the value
20
+ We make it optional to not break the flow.
21
+ */
22
+ .optional(),
23
+ });
24
+
25
+ /**
26
+ * Choose an RSA public key from those offered by the RP for encryption.
27
+ *
28
+ * @param entity The RP entity configuration
29
+ * @returns A suitable public key with its compatible encryption algorithm
30
+ * @throws {NoSuitableKeysFoundInEntityConfiguration} If entity do not contain any public key suitable for encrypting
31
+ */
32
+ const chooseRSAPublicKeyToEncrypt = (
33
+ entity: Out<EvaluateRelyingPartyTrust>["rpConf"]
34
+ ): JWK => {
35
+ const [usingRsa256] = entity.wallet_relying_party.jwks.keys.filter(
36
+ (jwk) => jwk.use === "enc" && jwk.kty === "RSA"
37
+ );
38
+
39
+ if (usingRsa256) {
40
+ return usingRsa256;
41
+ }
42
+
43
+ // No suitable key has been found
44
+ throw new NoSuitableKeysFoundInEntityConfiguration(
45
+ "Encrypt with RP public key"
46
+ );
47
+ };
48
+
49
+ /**
50
+ * Generate a Verified Presentation token for a received request object within the context of an authorization request flow.
51
+ * The presentation is created by revealing data from the provided credentials based on the requested claims.
52
+ * Each Verified Credential is accompanied by the claims that the user consents to disclose from it.
53
+ *
54
+ * @todo: Allow for handling more than one Verified Credential.
55
+ */
56
+ const prepareVpToken = async (
57
+ requestObject: Out<GetRequestObject>["requestObject"],
58
+ walletInstanceAttestation: string,
59
+ [vc, claims, cryptoCtx]: Presentation // TODO: [SIW-353] support multiple presentations,
60
+ ): Promise<{
61
+ vp_token: string;
62
+ presentation_submission: Record<string, unknown>;
63
+ }> => {
64
+ // this throws if vc cannot satisfy all the requested claims
65
+ const { token: vp, paths } = await disclose(vc, claims);
66
+
67
+ // obtain issuer from Wallet Instance
68
+ const {
69
+ payload: { iss },
70
+ } = WalletInstanceAttestation.decode(walletInstanceAttestation);
71
+
72
+ const pidKid = await cryptoCtx.getPublicKey().then((_) => _.kid);
73
+
74
+ // TODO: [SIW-359] check all requeste claims of the requestedObj are satisfied
75
+ const vp_token = await new SignJWT(cryptoCtx)
76
+ .setProtectedHeader({
77
+ typ: "JWT",
78
+ kid: pidKid,
79
+ })
80
+ .setPayload({
81
+ vp: vp,
82
+ jti: `${uuid.v4()}`,
83
+ iss,
84
+ nonce: requestObject.nonce,
85
+ })
86
+ .setAudience(requestObject.response_uri)
87
+ .setIssuedAt()
88
+ .setExpirationTime("1h")
89
+ .sign();
90
+
91
+ const vc_scope = requestObject.scope;
92
+ const presentation_submission = {
93
+ definition_id: `${uuid.v4()}`,
94
+ id: `${uuid.v4()}`,
95
+ descriptor_map: paths.map((p) => ({
96
+ id: vc_scope,
97
+ path: `$.vp_token.${p.path}`,
98
+ format: "vc+sd-jwt",
99
+ })),
100
+ };
101
+
102
+ return { vp_token, presentation_submission };
103
+ };
104
+
105
+ export type SendAuthorizationResponse = (
106
+ requestObject: Out<GetRequestObject>["requestObject"],
107
+ rpConf: Out<EvaluateRelyingPartyTrust>["rpConf"],
108
+ presentation: Presentation, // TODO: [SIW-353] support multiple presentations
109
+ context: {
110
+ walletInstanceAttestation: string;
111
+ appFetch?: GlobalFetch["fetch"];
112
+ }
113
+ ) => Promise<AuthorizationResponse>;
114
+
115
+ /**
116
+ * Complete the presentation flow by sending the authorization response to the Relying Party
117
+ *
118
+ * @param requestObject The Request Object that describes the presentation
119
+ * @param rpConf The Relying Party's configuration
120
+ * @param presentation The presentation tuple consisting in the signed credential,
121
+ * the list of claims to be disclosed, and the context to access the key that proves the holder binding
122
+ * @param context.walletInstanceAttestation The Wallet Instance Attestation token
123
+ * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
124
+ * @returns The result of the presentation flow
125
+ */
126
+ export const sendAuthorizationResponse: SendAuthorizationResponse = async (
127
+ requestObject,
128
+ rpConf,
129
+ presentation,
130
+ { appFetch = fetch, walletInstanceAttestation }
131
+ ): Promise<AuthorizationResponse> => {
132
+ // the request is an unsigned jws without iss, aud, exp
133
+ // https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-signed-and-encrypted-respon
134
+ const rsaPublicJwk = chooseRSAPublicKeyToEncrypt(rpConf);
135
+
136
+ const { vp_token, presentation_submission } = await prepareVpToken(
137
+ requestObject,
138
+ walletInstanceAttestation,
139
+ presentation
140
+ );
141
+
142
+ const authzResponsePayload = JSON.stringify({
143
+ state: requestObject.state,
144
+ presentation_submission,
145
+ nonce: requestObject.nonce,
146
+ vp_token,
147
+ });
148
+
149
+ const encrypted = await new EncryptJwe(authzResponsePayload, {
150
+ alg: "RSA-OAEP-256",
151
+ enc: "A256CBC-HS512",
152
+ kid: rsaPublicJwk.kid,
153
+ }).encrypt(rsaPublicJwk);
154
+
155
+ const formBody = new URLSearchParams({ response: encrypted });
156
+ const body = formBody.toString();
157
+
158
+ return appFetch(requestObject.response_uri, {
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/x-www-form-urlencoded",
162
+ },
163
+ body,
164
+ })
165
+ .then(hasStatus(200))
166
+ .then((res) => res.json())
167
+ .then(AuthorizationResponse.parse);
168
+ };
@@ -0,0 +1,26 @@
1
+ import { startFlowFromQR, type StartFlow } from "./01-start-flow";
2
+ import {
3
+ evaluateRelyingPartyTrust,
4
+ type EvaluateRelyingPartyTrust,
5
+ } from "./02-evaluate-rp-trust";
6
+ import {
7
+ getRequestObject,
8
+ type GetRequestObject,
9
+ } from "./03-get-request-object";
10
+ import {
11
+ sendAuthorizationResponse,
12
+ type SendAuthorizationResponse,
13
+ } from "./04-send-authorization-response";
14
+
15
+ export {
16
+ startFlowFromQR,
17
+ evaluateRelyingPartyTrust,
18
+ getRequestObject,
19
+ sendAuthorizationResponse,
20
+ };
21
+ export type {
22
+ StartFlow,
23
+ EvaluateRelyingPartyTrust,
24
+ GetRequestObject,
25
+ SendAuthorizationResponse,
26
+ };
@@ -0,0 +1,27 @@
1
+ import type { CryptoContext } from "@pagopa/io-react-native-jwt";
2
+ import { UnixTime } from "../../sd-jwt/types";
3
+ import * as z from "zod";
4
+
5
+ /**
6
+ * A pair that associate a tokenized Verified Credential with the claims presented or requested to present.
7
+ */
8
+ export type Presentation = [
9
+ /* verified credential token */ string,
10
+ /* claims */ string[],
11
+ /* the context for the key associated to the credential */ CryptoContext
12
+ ];
13
+
14
+ export type RequestObject = z.infer<typeof RequestObject>;
15
+ export const RequestObject = z.object({
16
+ iss: z.string(),
17
+ iat: UnixTime,
18
+ exp: UnixTime,
19
+ state: z.string(),
20
+ nonce: z.string(),
21
+ response_uri: z.string(),
22
+ response_type: z.literal("vp_token"),
23
+ response_mode: z.literal("direct_post.jwt"),
24
+ client_id: z.string(),
25
+ client_id_scheme: z.literal("entity_id"),
26
+ scope: z.string(),
27
+ });
package/src/index.ts CHANGED
@@ -2,42 +2,21 @@
2
2
  // https://github.com/facebook/react-native/issues/24428
3
3
  import "react-native-url-polyfill/auto";
4
4
 
5
+ import * as Credential from "./credential";
5
6
  import * as PID from "./pid";
6
- import * as RP from "./rp";
7
7
  import * as Errors from "./utils/errors";
8
8
  import * as WalletInstanceAttestation from "./wallet-instance-attestation";
9
- import * as RelyingPartySolution from "./rp";
10
- import {
11
- verifyTrustChain,
12
- getEntityConfiguration,
13
- getCredentialIssuerEntityConfiguration,
14
- getRelyingPartyEntityConfiguration,
15
- getTrustAnchorEntityConfiguration,
16
- getWalletProviderEntityConfiguration,
17
- } from "./trust";
18
- import {
19
- RelyingPartyEntityConfiguration,
20
- WalletProviderEntityConfiguration,
21
- TrustAnchorEntityConfiguration,
22
- CredentialIssuerEntityConfiguration,
23
- } from "./trust/types";
9
+ import * as Trust from "./trust";
10
+ import { AuthorizationDetail, AuthorizationDetails } from "./utils/par";
24
11
  import { createCryptoContextFor } from "./utils/crypto";
25
12
 
26
13
  export {
27
14
  PID,
28
- RP,
15
+ Credential,
29
16
  WalletInstanceAttestation,
30
17
  Errors,
31
- RelyingPartySolution,
32
- verifyTrustChain,
33
- getEntityConfiguration,
34
- getCredentialIssuerEntityConfiguration,
35
- getRelyingPartyEntityConfiguration,
36
- getTrustAnchorEntityConfiguration,
37
- getWalletProviderEntityConfiguration,
18
+ Trust,
38
19
  createCryptoContextFor,
39
- RelyingPartyEntityConfiguration,
40
- WalletProviderEntityConfiguration,
41
- TrustAnchorEntityConfiguration,
42
- CredentialIssuerEntityConfiguration,
20
+ AuthorizationDetail,
21
+ AuthorizationDetails,
43
22
  };
package/src/pid/index.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  import * as SdJwt from "./sd-jwt";
2
- import * as Issuing from "./issuing";
3
- export { SdJwt, Issuing };
2
+ export { SdJwt };