@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
@@ -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 };