@pagopa/io-react-native-wallet 0.11.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (217) hide show
  1. package/lib/commonjs/client/generated/wallet-provider.js +126 -0
  2. package/lib/commonjs/client/generated/wallet-provider.js.map +1 -0
  3. package/lib/commonjs/client/index.js +40 -0
  4. package/lib/commonjs/client/index.js.map +1 -0
  5. package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js +2 -1
  6. package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js.map +1 -1
  7. package/lib/commonjs/credential/issuance/03-start-credential-issuance.js +287 -0
  8. package/lib/commonjs/credential/issuance/03-start-credential-issuance.js.map +1 -0
  9. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +56 -83
  10. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
  11. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +88 -0
  12. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  13. package/lib/commonjs/credential/issuance/05-authorize-access.js +56 -33
  14. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
  15. package/lib/commonjs/credential/issuance/06-obtain-credential.js +51 -78
  16. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
  17. package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +21 -44
  18. package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
  19. package/lib/commonjs/credential/issuance/index.js +7 -0
  20. package/lib/commonjs/credential/issuance/index.js.map +1 -1
  21. package/lib/commonjs/credential/issuance/types.js +28 -0
  22. package/lib/commonjs/credential/issuance/types.js.map +1 -0
  23. package/lib/commonjs/index.js +10 -1
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/pid/sd-jwt/converters.js +5 -9
  26. package/lib/commonjs/pid/sd-jwt/converters.js.map +1 -1
  27. package/lib/commonjs/pid/sd-jwt/types.js +3 -3
  28. package/lib/commonjs/pid/sd-jwt/types.js.map +1 -1
  29. package/lib/commonjs/sd-jwt/__test__/converters.test.js +1 -1
  30. package/lib/commonjs/sd-jwt/__test__/converters.test.js.map +1 -1
  31. package/lib/commonjs/sd-jwt/__test__/index.test.js +30 -43
  32. package/lib/commonjs/sd-jwt/__test__/index.test.js.map +1 -1
  33. package/lib/commonjs/sd-jwt/__test__/types.test.js +16 -24
  34. package/lib/commonjs/sd-jwt/__test__/types.test.js.map +1 -1
  35. package/lib/commonjs/sd-jwt/index.js +3 -9
  36. package/lib/commonjs/sd-jwt/index.js.map +1 -1
  37. package/lib/commonjs/sd-jwt/types.js +11 -16
  38. package/lib/commonjs/sd-jwt/types.js.map +1 -1
  39. package/lib/commonjs/trust/types.js +70 -29
  40. package/lib/commonjs/trust/types.js.map +1 -1
  41. package/lib/commonjs/utils/auth.js +44 -0
  42. package/lib/commonjs/utils/auth.js.map +1 -0
  43. package/lib/commonjs/utils/errors.js +104 -1
  44. package/lib/commonjs/utils/errors.js.map +1 -1
  45. package/lib/commonjs/utils/integrity.js +2 -0
  46. package/lib/commonjs/utils/integrity.js.map +1 -0
  47. package/lib/commonjs/utils/misc.js +34 -1
  48. package/lib/commonjs/utils/misc.js.map +1 -1
  49. package/lib/commonjs/utils/par.js +23 -15
  50. package/lib/commonjs/utils/par.js.map +1 -1
  51. package/lib/commonjs/utils/pop.js +33 -0
  52. package/lib/commonjs/utils/pop.js.map +1 -0
  53. package/lib/commonjs/wallet-instance/index.js +29 -0
  54. package/lib/commonjs/wallet-instance/index.js.map +1 -0
  55. package/lib/commonjs/wallet-instance-attestation/issuing.js +62 -65
  56. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  57. package/lib/commonjs/wallet-instance-attestation/types.js +8 -8
  58. package/lib/commonjs/wallet-instance-attestation/types.js.map +1 -1
  59. package/lib/module/client/generated/wallet-provider.js +102 -0
  60. package/lib/module/client/generated/wallet-provider.js.map +1 -0
  61. package/lib/module/client/index.js +33 -0
  62. package/lib/module/client/index.js.map +1 -0
  63. package/lib/module/credential/issuance/02-evaluate-issuer-trust.js +2 -1
  64. package/lib/module/credential/issuance/02-evaluate-issuer-trust.js.map +1 -1
  65. package/lib/module/credential/issuance/03-start-credential-issuance.js +276 -0
  66. package/lib/module/credential/issuance/03-start-credential-issuance.js.map +1 -0
  67. package/lib/module/credential/issuance/03-start-user-authorization.js +56 -80
  68. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  69. package/lib/module/credential/issuance/04-complete-user-authorization.js +85 -1
  70. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  71. package/lib/module/credential/issuance/05-authorize-access.js +54 -33
  72. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  73. package/lib/module/credential/issuance/06-obtain-credential.js +50 -75
  74. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  75. package/lib/module/credential/issuance/07-verify-and-parse-credential.js +21 -44
  76. package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
  77. package/lib/module/credential/issuance/index.js +2 -1
  78. package/lib/module/credential/issuance/index.js.map +1 -1
  79. package/lib/module/credential/issuance/types.js +18 -0
  80. package/lib/module/credential/issuance/types.js.map +1 -0
  81. package/lib/module/index.js +3 -1
  82. package/lib/module/index.js.map +1 -1
  83. package/lib/module/pid/sd-jwt/converters.js +5 -9
  84. package/lib/module/pid/sd-jwt/converters.js.map +1 -1
  85. package/lib/module/pid/sd-jwt/types.js +3 -3
  86. package/lib/module/pid/sd-jwt/types.js.map +1 -1
  87. package/lib/module/sd-jwt/__test__/converters.test.js +1 -1
  88. package/lib/module/sd-jwt/__test__/converters.test.js.map +1 -1
  89. package/lib/module/sd-jwt/__test__/index.test.js +30 -43
  90. package/lib/module/sd-jwt/__test__/index.test.js.map +1 -1
  91. package/lib/module/sd-jwt/__test__/types.test.js +16 -24
  92. package/lib/module/sd-jwt/__test__/types.test.js.map +1 -1
  93. package/lib/module/sd-jwt/index.js +3 -9
  94. package/lib/module/sd-jwt/index.js.map +1 -1
  95. package/lib/module/sd-jwt/types.js +11 -16
  96. package/lib/module/sd-jwt/types.js.map +1 -1
  97. package/lib/module/trust/types.js +70 -29
  98. package/lib/module/trust/types.js.map +1 -1
  99. package/lib/module/utils/auth.js +35 -0
  100. package/lib/module/utils/auth.js.map +1 -0
  101. package/lib/module/utils/errors.js +98 -0
  102. package/lib/module/utils/errors.js.map +1 -1
  103. package/lib/module/utils/integrity.js +2 -0
  104. package/lib/module/utils/integrity.js.map +1 -0
  105. package/lib/module/utils/misc.js +31 -0
  106. package/lib/module/utils/misc.js.map +1 -1
  107. package/lib/module/utils/par.js +24 -16
  108. package/lib/module/utils/par.js.map +1 -1
  109. package/lib/module/utils/pop.js +24 -0
  110. package/lib/module/utils/pop.js.map +1 -0
  111. package/lib/module/wallet-instance/index.js +23 -0
  112. package/lib/module/wallet-instance/index.js.map +1 -0
  113. package/lib/module/wallet-instance-attestation/issuing.js +63 -67
  114. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  115. package/lib/module/wallet-instance-attestation/types.js +8 -8
  116. package/lib/module/wallet-instance-attestation/types.js.map +1 -1
  117. package/lib/typescript/client/generated/wallet-provider.d.ts +264 -0
  118. package/lib/typescript/client/generated/wallet-provider.d.ts.map +1 -0
  119. package/lib/typescript/client/index.d.ts +7 -0
  120. package/lib/typescript/client/index.d.ts.map +1 -0
  121. package/lib/typescript/credential/issuance/01-start-flow.d.ts +1 -0
  122. package/lib/typescript/credential/issuance/01-start-flow.d.ts.map +1 -1
  123. package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts +2 -1
  124. package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts.map +1 -1
  125. package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts +41 -0
  126. package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts.map +1 -0
  127. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +23 -18
  128. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  129. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +24 -12
  130. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  131. package/lib/typescript/credential/issuance/05-authorize-access.d.ts +22 -16
  132. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  133. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +19 -26
  134. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  135. package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts +10 -15
  136. package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
  137. package/lib/typescript/credential/issuance/index.d.ts +3 -4
  138. package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
  139. package/lib/typescript/credential/issuance/types.d.ts +63 -0
  140. package/lib/typescript/credential/issuance/types.d.ts.map +1 -0
  141. package/lib/typescript/credential/presentation/types.d.ts +6 -6
  142. package/lib/typescript/index.d.ts +6 -1
  143. package/lib/typescript/index.d.ts.map +1 -1
  144. package/lib/typescript/pid/sd-jwt/converters.d.ts.map +1 -1
  145. package/lib/typescript/pid/sd-jwt/types.d.ts +36 -36
  146. package/lib/typescript/pid/sd-jwt/types.d.ts.map +1 -1
  147. package/lib/typescript/sd-jwt/index.d.ts +40 -68
  148. package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
  149. package/lib/typescript/sd-jwt/types.d.ts +64 -121
  150. package/lib/typescript/sd-jwt/types.d.ts.map +1 -1
  151. package/lib/typescript/trust/index.d.ts +150 -48
  152. package/lib/typescript/trust/index.d.ts.map +1 -1
  153. package/lib/typescript/trust/types.d.ts +2838 -1740
  154. package/lib/typescript/trust/types.d.ts.map +1 -1
  155. package/lib/typescript/utils/auth.d.ts +52 -0
  156. package/lib/typescript/utils/auth.d.ts.map +1 -0
  157. package/lib/typescript/utils/errors.d.ts +48 -0
  158. package/lib/typescript/utils/errors.d.ts.map +1 -1
  159. package/lib/typescript/utils/integrity.d.ts +21 -0
  160. package/lib/typescript/utils/integrity.d.ts.map +1 -0
  161. package/lib/typescript/utils/misc.d.ts +18 -0
  162. package/lib/typescript/utils/misc.d.ts.map +1 -1
  163. package/lib/typescript/utils/par.d.ts +8 -31
  164. package/lib/typescript/utils/par.d.ts.map +1 -1
  165. package/lib/typescript/utils/pop.d.ts +26 -0
  166. package/lib/typescript/utils/pop.d.ts.map +1 -0
  167. package/lib/typescript/wallet-instance/index.d.ts +7 -0
  168. package/lib/typescript/wallet-instance/index.d.ts.map +1 -0
  169. package/lib/typescript/wallet-instance-attestation/issuing.d.ts +17 -4
  170. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  171. package/lib/typescript/wallet-instance-attestation/types.d.ts +64 -64
  172. package/lib/typescript/wallet-instance-attestation/types.d.ts.map +1 -1
  173. package/package.json +9 -5
  174. package/src/client/generated/wallet-provider.ts +173 -0
  175. package/src/client/index.ts +53 -0
  176. package/src/credential/issuance/01-start-flow.ts +1 -0
  177. package/src/credential/issuance/02-evaluate-issuer-trust.ts +2 -1
  178. package/src/credential/issuance/03-start-credential-issuance.ts +407 -0
  179. package/src/credential/issuance/03-start-user-authorization.ts +91 -92
  180. package/src/credential/issuance/04-complete-user-authorization.ts +114 -13
  181. package/src/credential/issuance/05-authorize-access.ts +74 -49
  182. package/src/credential/issuance/06-obtain-credential.ts +77 -111
  183. package/src/credential/issuance/07-verify-and-parse-credential.ts +30 -67
  184. package/src/credential/issuance/index.ts +6 -4
  185. package/src/credential/issuance/types.ts +25 -0
  186. package/src/index.ts +8 -0
  187. package/src/pid/sd-jwt/converters.ts +5 -11
  188. package/src/pid/sd-jwt/types.ts +8 -6
  189. package/src/sd-jwt/__test__/converters.test.ts +1 -1
  190. package/src/sd-jwt/__test__/index.test.ts +45 -74
  191. package/src/sd-jwt/__test__/types.test.ts +21 -33
  192. package/src/sd-jwt/index.ts +3 -12
  193. package/src/sd-jwt/types.ts +17 -22
  194. package/src/trust/types.ts +64 -32
  195. package/src/utils/auth.ts +37 -0
  196. package/src/utils/errors.ts +112 -0
  197. package/src/utils/integrity.ts +23 -0
  198. package/src/utils/misc.ts +43 -0
  199. package/src/utils/par.ts +29 -17
  200. package/src/utils/pop.ts +34 -0
  201. package/src/wallet-instance/index.ts +29 -0
  202. package/src/wallet-instance-attestation/issuing.ts +101 -97
  203. package/src/wallet-instance-attestation/types.ts +12 -8
  204. package/lib/commonjs/credential/issuance/07-confirm-credential.js +0 -6
  205. package/lib/commonjs/credential/issuance/07-confirm-credential.js.map +0 -1
  206. package/lib/commonjs/credential/issuance/08-confirm-credential.js +0 -6
  207. package/lib/commonjs/credential/issuance/08-confirm-credential.js.map +0 -1
  208. package/lib/module/credential/issuance/07-confirm-credential.js +0 -2
  209. package/lib/module/credential/issuance/07-confirm-credential.js.map +0 -1
  210. package/lib/module/credential/issuance/08-confirm-credential.js +0 -2
  211. package/lib/module/credential/issuance/08-confirm-credential.js.map +0 -1
  212. package/lib/typescript/credential/issuance/07-confirm-credential.d.ts +0 -11
  213. package/lib/typescript/credential/issuance/07-confirm-credential.d.ts.map +0 -1
  214. package/lib/typescript/credential/issuance/08-confirm-credential.d.ts +0 -11
  215. package/lib/typescript/credential/issuance/08-confirm-credential.d.ts.map +0 -1
  216. package/src/credential/issuance/07-confirm-credential.ts +0 -14
  217. package/src/credential/issuance/08-confirm-credential.ts +0 -14
@@ -1,17 +1,118 @@
1
- import type { Out } from "../../utils/misc";
1
+ import {
2
+ AuthorizationErrorShape,
3
+ AuthorizationResultShape,
4
+ type AuthorizationContext,
5
+ type AuthorizationResult,
6
+ } from "../../../src/utils/auth";
7
+ import { until, type Out } from "../../utils/misc";
2
8
  import type { StartUserAuthorization } from "./03-start-user-authorization";
9
+ import parseUrl from "parse-url";
10
+ import { AuthorizationError, AuthorizationIdpError } from "../../utils/errors";
11
+ import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
12
+ import { Linking } from "react-native";
3
13
 
4
14
  /**
5
- * The interface of the phase to complete User authorization.
6
- * It may be implemented as a Credential presentation
7
- * or with a strong User identification
8
- *
9
- * @param requestUri The url to reach to complete the user authorization.
10
- * @param cliendId Identifies the current client across all the requests of the issuing flow
11
- *
12
- * @returns the access code to use to request the credental
15
+ * The interface of the phase to complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
13
16
  */
14
- export type CompleteUserAuthorization = (
15
- requestUri: Out<StartUserAuthorization>["requestUri"],
16
- clientId: Out<StartUserAuthorization>["clientId"]
17
- ) => Promise<{ code: string }>;
17
+ export type CompleteUserAuthorizationWithQueryMode = (
18
+ issuerRequestUri: Out<StartUserAuthorization>["issuerRequestUri"],
19
+ clientId: Out<StartUserAuthorization>["clientId"],
20
+ issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
21
+ idpHint: string,
22
+ redirectUri: string,
23
+ authorizationContext?: AuthorizationContext
24
+ ) => Promise<AuthorizationResult>;
25
+
26
+ /**
27
+ * WARNING: This function must be called after {@link startUserAuthorization}. The next function to be called is {@link authorizeAccess}.
28
+ * The interface of the phase to complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
29
+ * It is used to complete the user authorization by catching the redirectSchema from the authorization server which then contains the authorization response.
30
+ * This function utilizes the authorization context to open an in-app browser capable of catching the redirectSchema to perform a get request to the authorization endpoint.
31
+ * If the 302 redirect happens and the redirectSchema is caught, the function will return the authorization response after parsing it from the query string.
32
+ * @param issuerRequestUri the URI of the issuer where the request is sent
33
+ * @param clientId Identifies the current client across all the requests of the issuing flow returned by {@link startUserAuthorization}
34
+ * @param issuerConf The issuer configuration returned by {@link evaluateIssuerTrust}
35
+ * @param authorizationContext The context to identify the user which will be used to start the authorization. It's needed only when requesting a PersonalIdentificationData credential. The implementantion should open an in-app browser capable of catching the redirectSchema.
36
+ * If not specified, the default browser is used
37
+ * @param idphint Unique identifier of the SPID IDP selected by the user
38
+ * @param redirectUri The url to reach to complete the user authorization which is the custom URL scheme that the Wallet Instance is registered to handle, usually a custom URL or deeplink
39
+ * @throws {AuthorizationError} if an error occurs during the authorization process
40
+ * @throws {AuthorizationIdpError} if an error occurs during the authorization process and the error is related to the IDP
41
+ * @returns the authorization response which contains code, state and iss
42
+ */
43
+ export const completeUserAuthorizationWithQueryMode: CompleteUserAuthorizationWithQueryMode =
44
+ async (
45
+ issuerRequestUri,
46
+ clientId,
47
+ issuerConf,
48
+ idpHint,
49
+ redirectUri,
50
+ authorizationContext
51
+ ) => {
52
+ /**
53
+ * Starts the authorization flow which dependes on the response mode and the request credential.
54
+ * If the response mode is "query" the authorization flow is handled differently via the authorization context which opens an in-app browser capable of catching the redirectSchema.
55
+ * The form_post.jwt mode is not currently supported.
56
+ */
57
+ const authzRequestEndpoint =
58
+ issuerConf.oauth_authorization_server.authorization_endpoint;
59
+ const params = new URLSearchParams({
60
+ client_id: clientId,
61
+ request_uri: issuerRequestUri,
62
+ idphint: idpHint,
63
+ });
64
+ const authUrl = `${authzRequestEndpoint}?${params}`;
65
+ var authRedirectUrl: string | undefined;
66
+
67
+ if (authorizationContext) {
68
+ const redirectSchema = new URL(redirectUri).protocol.replace(":", "");
69
+ authRedirectUrl = await authorizationContext
70
+ .authorize(authUrl, redirectSchema)
71
+ .catch((e) => {
72
+ throw new AuthorizationError(e.message);
73
+ });
74
+ } else {
75
+ // handler for redirectUri
76
+ Linking.addEventListener("url", ({ url }) => {
77
+ if (url.includes(redirectUri)) {
78
+ authRedirectUrl = url;
79
+ }
80
+ });
81
+
82
+ const openAuthUrlInBrowser = Linking.openURL(authUrl);
83
+
84
+ /*
85
+ * Waits for 120 seconds for the identificationRedirectUrl variable to be set
86
+ * by the custom url handler. If the timeout is exceeded, throw an exception
87
+ */
88
+ const unitAuthRedirectIsNotUndefined = until(
89
+ () => authRedirectUrl !== undefined,
90
+ 120
91
+ );
92
+
93
+ await Promise.all([openAuthUrlInBrowser, unitAuthRedirectIsNotUndefined]);
94
+
95
+ if (authRedirectUrl === undefined) {
96
+ throw new AuthorizationError("Invalid authentication redirect url");
97
+ }
98
+ }
99
+
100
+ const urlParse = parseUrl(authRedirectUrl);
101
+ const authRes = AuthorizationResultShape.safeParse(urlParse.query);
102
+ if (!authRes.success) {
103
+ const authErr = AuthorizationErrorShape.safeParse(urlParse.query);
104
+ if (!authErr.success) {
105
+ throw new AuthorizationError(authRes.error.message); // an error occured while parsing the result and the error
106
+ }
107
+ throw new AuthorizationIdpError(
108
+ authErr.data.error,
109
+ authErr.data.error_description
110
+ );
111
+ }
112
+ return authRes.data;
113
+ };
114
+
115
+ // TODO: SIW-1120 implement generic credential issuance flow
116
+ export const completeUserAuthorizationWithFormPostJwtMode = () => {
117
+ throw new Error("Not implemented");
118
+ };
@@ -1,92 +1,117 @@
1
- import uuid from "react-native-uuid";
2
- import { withEphemeralKey } from "../../utils/crypto";
3
- import { createDPopToken } from "../../utils/dpop";
4
- import type { StartUserAuthorization } from "./03-start-user-authorization";
5
- import { hasStatus, type Out } from "../../utils/misc";
1
+ import { hasStatus, type Out } from "../../../src/utils/misc";
6
2
  import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
3
+ import type { StartUserAuthorization } from "./03-start-user-authorization";
4
+ import { withEphemeralKey } from "../../../src/utils/crypto";
5
+ import { createDPopToken } from "../../../src/utils/dpop";
6
+ import uuid from "react-native-uuid";
7
+ import { createPopToken } from "../../../src/utils/pop";
8
+ import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
9
+ import type { CryptoContext } from "@pagopa/io-react-native-jwt";
7
10
  import { ASSERTION_TYPE } from "./const";
8
- import type { CompleteUserAuthorization } from "./04-complete-user-authorization";
11
+ import { TokenResponse } from "./types";
12
+ import { ValidationFailed } from "../../../src/utils/errors";
13
+ import type { CompleteUserAuthorizationWithQueryMode } from "./04-complete-user-authorization";
9
14
 
10
15
  export type AuthorizeAccess = (
11
16
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
12
- code: Out<CompleteUserAuthorization>["code"],
17
+ code: Out<CompleteUserAuthorizationWithQueryMode>["code"],
18
+ redirectUri: string,
13
19
  clientId: Out<StartUserAuthorization>["clientId"],
20
+ codeVerifier: Out<StartUserAuthorization>["codeVerifier"],
14
21
  context: {
15
22
  walletInstanceAttestation: string;
16
- walletProviderBaseUrl: string;
17
23
  appFetch?: GlobalFetch["fetch"];
24
+ wiaCryptoContext: CryptoContext;
18
25
  }
19
- ) => Promise<{
20
- // The access token to grant access to the credential
21
- accessToken: string;
22
- // The nonce, to prevent reply attacks
23
- nonce: string;
24
- // Same as input
25
- clientId: string;
26
- }>;
26
+ ) => Promise<{ accessToken: TokenResponse; tokenRequestSignedDPop: string }>;
27
27
 
28
28
  /**
29
- * Obtain the access token to finally request the credential
30
- *
31
- * @param issuerConf The Issuer configuration
32
- * @param code The access code from the User authorization phase
33
- * @param clientId Identifies the current client across all the requests of the issuing flow
34
- * @param context.walletInstanceAttestation The Wallet Instance Attestation token
35
- * @param context.walletProviderBaseUrl The base url of the Wallet Provider
29
+ * Creates and sends the DPoP Proof JWT to be presented with the authorization code to the /token endpoint of the authorization server
30
+ * for requesting the issuance of an access token bound to the public key of the Wallet Instance contained within the DPoP.
31
+ * This enables the Wallet Instance to request a digital credential.
32
+ * The DPoP Proof JWT is generated according to the section 4.3 of the DPoP RFC 9449 specification.
33
+ * @param issuerConf The issuer configuration returned by {@link evaluateIssuerTrust}
34
+ * @param code The authorization code returned by {@link completeUserAuthorizationWithQueryMode} or {@link completeUserAuthorizationWithFormPost}
35
+ * @param redirectUri The redirect URI which is the custom URL scheme that the Wallet Instance is registered to handle
36
+ * @param clientId The client id returned by {@link startUserAuthorization}
37
+ * @param codeVerifier The code verifier returned by {@link startUserAuthorization}
38
+ * @param context.walletInstanceAttestation The Wallet Instance's attestation
39
+ * @param context.wiaCryptoContext The Wallet Instance's crypto context
36
40
  * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
37
- * @returns
41
+ * @throws {ValidationFailed} if an error occurs while parsing the token response
42
+ * @return The token response containing the access token along with the token request signed with DPoP which has to be used in the {@link obtainCredential} step.
38
43
  */
39
44
  export const authorizeAccess: AuthorizeAccess = async (
40
45
  issuerConf,
41
46
  code,
42
47
  clientId,
48
+ redirectUri,
49
+ codeVerifier,
43
50
  context
44
- ): Promise<{ accessToken: string; nonce: string; clientId: string }> => {
51
+ ) => {
45
52
  const {
46
53
  appFetch = fetch,
47
54
  walletInstanceAttestation,
48
- walletProviderBaseUrl,
55
+ wiaCryptoContext,
49
56
  } = context;
50
57
 
51
- const tokenUrl = issuerConf.openid_credential_issuer.token_endpoint;
58
+ const parEndpoint =
59
+ issuerConf.oauth_authorization_server.pushed_authorization_request_endpoint;
60
+ const parUrl = new URL(parEndpoint);
61
+ const aud = `${parUrl.protocol}//${parUrl.hostname}`;
62
+ const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
63
+ .payload.cnf.jwk.kid;
52
64
 
65
+ const tokenUrl = issuerConf.oauth_authorization_server.token_endpoint;
53
66
  // Use an ephemeral key to be destroyed after use
54
- const signedDPop = await withEphemeralKey((ephemeralContext) =>
55
- createDPopToken(
56
- {
57
- htm: "POST",
58
- htu: tokenUrl,
59
- jti: `${uuid.v4()}`,
60
- },
61
- ephemeralContext
62
- )
67
+ const tokenRequestSignedDPop = await withEphemeralKey(
68
+ async (ephimeralContext) => {
69
+ return await createDPopToken(
70
+ {
71
+ htm: "POST",
72
+ htu: tokenUrl,
73
+ jti: `${uuid.v4()}`,
74
+ },
75
+ ephimeralContext
76
+ );
77
+ }
78
+ );
79
+
80
+ const signedWiaPoP = await createPopToken(
81
+ {
82
+ jti: `${uuid.v4()}`,
83
+ aud,
84
+ iss,
85
+ },
86
+ wiaCryptoContext
63
87
  );
64
88
 
65
- const codeVerifier = `${uuid.v4()}`;
66
89
  const requestBody = {
67
- grant_type: "authorization code",
90
+ grant_type: "authorization_code",
68
91
  client_id: clientId,
69
92
  code,
93
+ redirect_uri: redirectUri,
70
94
  code_verifier: codeVerifier,
71
95
  client_assertion_type: ASSERTION_TYPE,
72
- client_assertion: walletInstanceAttestation,
73
- redirect_uri: walletProviderBaseUrl,
96
+ client_assertion: walletInstanceAttestation + "~" + signedWiaPoP,
74
97
  };
75
- var formBody = new URLSearchParams(requestBody);
76
98
 
77
- return appFetch(tokenUrl, {
99
+ const authorizationRequestFormBody = new URLSearchParams(requestBody);
100
+ const tokenRes = await appFetch(tokenUrl, {
78
101
  method: "POST",
79
102
  headers: {
80
103
  "Content-Type": "application/x-www-form-urlencoded",
81
- DPoP: signedDPop,
104
+ DPoP: tokenRequestSignedDPop,
82
105
  },
83
- body: formBody.toString(),
106
+ body: authorizationRequestFormBody.toString(),
84
107
  })
85
108
  .then(hasStatus(200))
86
109
  .then((res) => res.json())
87
- .then((body) => ({
88
- accessToken: body.access_token,
89
- nonce: body.c_nonce,
90
- clientId,
91
- }));
110
+ .then((body) => TokenResponse.safeParse(body));
111
+
112
+ if (!tokenRes.success) {
113
+ throw new ValidationFailed(tokenRes.error.message);
114
+ }
115
+
116
+ return { accessToken: tokenRes.data, tokenRequestSignedDPop };
92
117
  };
@@ -1,161 +1,127 @@
1
- import * as z from "zod";
2
- import uuid from "react-native-uuid";
3
1
  import { SignJWT, type CryptoContext } from "@pagopa/io-react-native-jwt";
4
- import { createDPopToken } from "../../utils/dpop";
5
-
6
- import type { StartFlow } from "./01-start-flow";
7
- import { hasStatus, type Out } from "../../utils/misc";
8
- import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
9
2
  import type { AuthorizeAccess } from "./05-authorize-access";
10
- import { SupportedCredentialFormat } from "./const";
3
+ import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
4
+ import { hasStatus, type Out } from "../../../src/utils/misc";
5
+ import type { StartUserAuthorization } from "./03-start-user-authorization";
6
+ import { ValidationFailed } from "../../../src/utils/errors";
7
+ import { CredentialResponse } from "./types";
8
+
9
+ export type ObtainCredential = (
10
+ issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
11
+ accessToken: Out<AuthorizeAccess>["accessToken"],
12
+ clientId: Out<StartUserAuthorization>["clientId"],
13
+ credentialDefinition: Out<StartUserAuthorization>["credentialDefinition"],
14
+ tokenRequestSignedDPop: Out<AuthorizeAccess>["tokenRequestSignedDPop"],
15
+ context: {
16
+ credentialCryptoContext: CryptoContext;
17
+ appFetch?: GlobalFetch["fetch"];
18
+ }
19
+ ) => Promise<CredentialResponse>;
11
20
 
12
- /**
13
- * Return the signed jwt for nonce proof of possession
14
- */
15
21
  export const createNonceProof = async (
16
22
  nonce: string,
17
23
  issuer: string,
18
24
  audience: string,
19
25
  ctx: CryptoContext
20
26
  ): Promise<string> => {
27
+ const jwk = await ctx.getPublicKey();
21
28
  return new SignJWT(ctx)
22
29
  .setPayload({
23
30
  nonce,
24
- jwk: await ctx.getPublicKey(),
25
31
  })
26
32
  .setProtectedHeader({
27
- type: "openid4vci-proof+jwt",
33
+ typ: "openid4vci-proof+jwt",
34
+ jwk,
28
35
  })
29
36
  .setAudience(audience)
30
37
  .setIssuer(issuer)
31
38
  .setIssuedAt()
32
- .setExpirationTime("1h")
39
+ .setExpirationTime("5min")
33
40
  .sign();
34
41
  };
35
42
 
36
- const CredentialEndpointResponse = z.object({
37
- credential: z.string(),
38
- format: SupportedCredentialFormat,
39
- // nonce used to perform multiple credential requests
40
- // re-using the same authorization profile
41
- c_nonce: z.string(),
42
- c_nonce_expires_in: z.number(),
43
- });
44
-
45
- export type ObtainCredential = (
46
- issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
47
- accessToken: Out<AuthorizeAccess>["accessToken"],
48
- nonce: Out<AuthorizeAccess>["nonce"],
49
- clientId: Out<AuthorizeAccess>["clientId"],
50
- credentialType: Out<StartFlow>["credentialType"],
51
- credentialFormat: SupportedCredentialFormat,
52
- context: {
53
- credentialCryptoContext: CryptoContext;
54
- walletProviderBaseUrl: string;
55
- appFetch?: GlobalFetch["fetch"];
56
- }
57
- ) => Promise<{
58
- credential: string;
59
- format: SupportedCredentialFormat;
60
- nonce: string;
61
- }>;
62
-
63
- // Checks whether in the Entity confoguration at least one credential
64
- // is defined for the given type and format
65
- const isCredentialAvailable = (
66
- issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
67
- credentialType: Out<StartFlow>["credentialType"],
68
- credentialFormat: SupportedCredentialFormat
69
- ): boolean =>
70
- issuerConf.openid_credential_issuer.credentials_supported.some(
71
- (c) =>
72
- c.format === credentialFormat &&
73
- c.credential_definition.type.includes(credentialType)
74
- );
75
-
76
43
  /**
77
- * Fetch a credential from the issuer
78
- *
79
- * @param issuerConf The Issuer configuration
80
- * @param accessToken The access token to grant access to the credential, obtained with the access authorization step
81
- * @param nonce The nonce value to prevent reply attacks, obtained with the access authorization step
82
- * @param clientId Identifies the current client across all the requests of the issuing flow
83
- * @param credentialType The type of the credential to be requested
84
- * @param credentialFormat The format of the requested credential. @see {SupportedCredentialFormat}
85
- * @param context.credentialCryptoContext The context to access the key the Credential will be bound to
86
- * @param context.walletProviderBaseUrl The base url of the Wallet Provider
44
+ * Obtains the credential from the issuer.
45
+ * The key pair of the credentialCryptoContext is used for Openid4vci proof JWT to be presented with the Access Token and the DPoP Proof JWT at the Credential Endpoint
46
+ * of the Credential Issuer to request the issuance of a credential linked to the public key contained in the JWT proof.
47
+ * The Openid4vci proof JWT incapsulates the nonce extracted from the token response from the {@link authorizeAccess} step.
48
+ * The credential request is sent to the Credential Endpoint of the Credential Issuer via HTTP POST with the type of the credential, its format, the access token and the JWT proof.
49
+ * @param issuerConf The issuer configuration returned by {@link evaluateIssuerTrust}
50
+ * @param accessToken The access token response returned by {@link authorizeAccess}
51
+ * @param clientId The client id returned by {@link startUserAuthorization}
52
+ * @param credentialDefinition The credential definition of the credential to be obtained returned by {@link startUserAuthorization}
53
+ * @param tokenRequestSignedDPop The DPoP signed token request returned by {@link authorizeAccess}
54
+ * @param context.credentialCryptoContext The crypto context used to obtain the credential
87
55
  * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
88
- * @returns The signed credential token
56
+ * @returns The credential response containing the credential
89
57
  */
90
58
  export const obtainCredential: ObtainCredential = async (
91
59
  issuerConf,
92
60
  accessToken,
93
- nonce,
94
61
  clientId,
95
- credentialType,
96
- credentialFormat,
62
+ credentialDefinition,
63
+ tokenRequestSignedDPop,
97
64
  context
98
65
  ) => {
99
- const {
100
- credentialCryptoContext,
101
- walletProviderBaseUrl,
102
- appFetch = fetch,
103
- } = context;
104
-
105
- if (!isCredentialAvailable(issuerConf, credentialType, credentialFormat)) {
106
- throw new Error(
107
- `The Issuer provides no credential for type ${credentialType} and format ${credentialFormat}`
108
- );
109
- }
66
+ const { credentialCryptoContext, appFetch = fetch } = context;
110
67
 
111
68
  const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
112
69
 
113
- /** DPoP token for demonstating the possession
114
- of the key that will bind the holder User with the Credential
115
- @see https://datatracker.ietf.org/doc/html/rfc9449 */
116
- const signedDPopForPid = await createDPopToken(
117
- {
118
- htm: "POST",
119
- htu: credentialUrl,
120
- jti: `${uuid.v4()}`,
121
- },
122
- credentialCryptoContext
123
- );
124
-
125
- /** JWT proof token to bind the request nonce
126
- to the key that will bind the holder User with the Credential
127
- @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-proof-types */
70
+ /**
71
+ * JWT proof token to bind the request nonce to the key that will bind the holder User with the Credential
72
+ * This is presented along with the access token to the Credential Endpoint as proof of possession of the private key used to sign the Access Token.
73
+ * @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-proof-types
74
+ */
128
75
  const signedNonceProof = await createNonceProof(
129
- nonce,
76
+ accessToken.c_nonce,
130
77
  clientId,
131
- walletProviderBaseUrl,
78
+ credentialUrl,
132
79
  credentialCryptoContext
133
80
  );
134
81
 
82
+ // Validation of accessTokenResponse.authorization_details if contain credentialDefinition
83
+ const constainsCredentialDefinition = accessToken.authorization_details.some(
84
+ (c) =>
85
+ c.credential_configuration_id ===
86
+ credentialDefinition.credential_configuration_id &&
87
+ c.format === credentialDefinition.format &&
88
+ c.type === credentialDefinition.type
89
+ );
90
+
91
+ if (!constainsCredentialDefinition) {
92
+ throw new ValidationFailed(
93
+ "The access token response does not contain the requested credential"
94
+ );
95
+ }
96
+
135
97
  /** The credential request body */
136
- const formBody = new URLSearchParams({
137
- credential_definition: JSON.stringify({
138
- type: [credentialType],
139
- }),
140
- format: credentialFormat,
141
- proof: JSON.stringify({
98
+ const credentialRequestFormBody = {
99
+ credential_definition: {
100
+ type: [credentialDefinition.credential_configuration_id],
101
+ },
102
+ format: credentialDefinition.format,
103
+ proof: {
142
104
  jwt: signedNonceProof,
143
105
  proof_type: "jwt",
144
- }),
145
- });
106
+ },
107
+ };
146
108
 
147
- const { credential, format, c_nonce } = await appFetch(credentialUrl, {
109
+ const credentialRes = await appFetch(credentialUrl, {
148
110
  method: "POST",
149
111
  headers: {
150
- "Content-Type": "application/x-www-form-urlencoded",
151
- DPoP: signedDPopForPid,
152
- Authorization: accessToken,
112
+ "Content-Type": "application/json",
113
+ DPoP: tokenRequestSignedDPop,
114
+ Authorization: `${accessToken.token_type} ${accessToken.access_token}`,
153
115
  },
154
- body: formBody.toString(),
116
+ body: JSON.stringify(credentialRequestFormBody),
155
117
  })
156
118
  .then(hasStatus(200))
157
119
  .then((res) => res.json())
158
- .then(CredentialEndpointResponse.parse);
120
+ .then((body) => CredentialResponse.safeParse(body));
121
+
122
+ if (!credentialRes.success) {
123
+ throw new ValidationFailed(credentialRes.error.message);
124
+ }
159
125
 
160
- return { credential, format, nonce: c_nonce };
126
+ return credentialRes.data;
161
127
  };