@pagopa/io-react-native-wallet 0.4.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. package/README.md +128 -19
  2. package/lib/commonjs/index.js +16 -23
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/pid/index.js +3 -8
  5. package/lib/commonjs/pid/index.js.map +1 -1
  6. package/lib/commonjs/pid/issuing.js +153 -176
  7. package/lib/commonjs/pid/issuing.js.map +1 -1
  8. package/lib/commonjs/rp/__test__/index.test.js +7 -5
  9. package/lib/commonjs/rp/__test__/index.test.js.map +1 -1
  10. package/lib/commonjs/rp/index.js +145 -155
  11. package/lib/commonjs/rp/index.js.map +1 -1
  12. package/lib/commonjs/rp/types.js +1 -21
  13. package/lib/commonjs/rp/types.js.map +1 -1
  14. package/lib/commonjs/trust/index.js +24 -5
  15. package/lib/commonjs/trust/index.js.map +1 -1
  16. package/lib/commonjs/trust/types.js +102 -9
  17. package/lib/commonjs/trust/types.js.map +1 -1
  18. package/lib/commonjs/utils/crypto.js +46 -0
  19. package/lib/commonjs/utils/crypto.js.map +1 -0
  20. package/lib/commonjs/utils/dpop.js +14 -7
  21. package/lib/commonjs/utils/dpop.js.map +1 -1
  22. package/lib/commonjs/wallet-instance-attestation/index.js +3 -3
  23. package/lib/commonjs/wallet-instance-attestation/issuing.js +42 -60
  24. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  25. package/lib/module/index.js +4 -6
  26. package/lib/module/index.js.map +1 -1
  27. package/lib/module/pid/index.js +1 -1
  28. package/lib/module/pid/index.js.map +1 -1
  29. package/lib/module/pid/issuing.js +152 -180
  30. package/lib/module/pid/issuing.js.map +1 -1
  31. package/lib/module/rp/__test__/index.test.js +3 -3
  32. package/lib/module/rp/__test__/index.test.js.map +1 -1
  33. package/lib/module/rp/index.js +141 -154
  34. package/lib/module/rp/index.js.map +1 -1
  35. package/lib/module/rp/types.js +0 -20
  36. package/lib/module/rp/types.js.map +1 -1
  37. package/lib/module/trust/index.js +19 -5
  38. package/lib/module/trust/index.js.map +1 -1
  39. package/lib/module/trust/types.js +100 -7
  40. package/lib/module/trust/types.js.map +1 -1
  41. package/lib/module/utils/crypto.js +40 -0
  42. package/lib/module/utils/crypto.js.map +1 -0
  43. package/lib/module/utils/dpop.js +13 -5
  44. package/lib/module/utils/dpop.js.map +1 -1
  45. package/lib/module/wallet-instance-attestation/index.js +2 -2
  46. package/lib/module/wallet-instance-attestation/index.js.map +1 -1
  47. package/lib/module/wallet-instance-attestation/issuing.js +40 -58
  48. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  49. package/lib/typescript/index.d.ts +4 -6
  50. package/lib/typescript/index.d.ts.map +1 -1
  51. package/lib/typescript/pid/index.d.ts +1 -1
  52. package/lib/typescript/pid/index.d.ts.map +1 -1
  53. package/lib/typescript/pid/issuing.d.ts +43 -88
  54. package/lib/typescript/pid/issuing.d.ts.map +1 -1
  55. package/lib/typescript/rp/index.d.ts +41 -87
  56. package/lib/typescript/rp/index.d.ts.map +1 -1
  57. package/lib/typescript/rp/types.d.ts +10 -906
  58. package/lib/typescript/rp/types.d.ts.map +1 -1
  59. package/lib/typescript/sd-jwt/index.d.ts +1 -1
  60. package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
  61. package/lib/typescript/trust/index.d.ts +806 -3
  62. package/lib/typescript/trust/index.d.ts.map +1 -1
  63. package/lib/typescript/trust/types.d.ts +9655 -297
  64. package/lib/typescript/trust/types.d.ts.map +1 -1
  65. package/lib/typescript/utils/crypto.d.ts +10 -0
  66. package/lib/typescript/utils/crypto.d.ts.map +1 -0
  67. package/lib/typescript/utils/dpop.d.ts +10 -2
  68. package/lib/typescript/utils/dpop.d.ts.map +1 -1
  69. package/lib/typescript/wallet-instance-attestation/index.d.ts +2 -2
  70. package/lib/typescript/wallet-instance-attestation/index.d.ts.map +1 -1
  71. package/lib/typescript/wallet-instance-attestation/issuing.d.ts +18 -31
  72. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  73. package/lib/typescript/wallet-instance-attestation/types.d.ts +4 -4
  74. package/package.json +2 -2
  75. package/src/index.ts +14 -13
  76. package/src/pid/index.ts +1 -1
  77. package/src/pid/issuing.ts +233 -232
  78. package/src/rp/__test__/index.test.ts +3 -3
  79. package/src/rp/index.ts +172 -194
  80. package/src/rp/types.ts +0 -24
  81. package/src/sd-jwt/index.ts +1 -1
  82. package/src/trust/index.ts +106 -5
  83. package/src/trust/types.ts +152 -34
  84. package/src/utils/crypto.ts +41 -0
  85. package/src/utils/dpop.ts +17 -7
  86. package/src/wallet-instance-attestation/index.ts +2 -2
  87. package/src/wallet-instance-attestation/issuing.ts +51 -63
  88. package/lib/commonjs/pid/metadata.js +0 -49
  89. package/lib/commonjs/pid/metadata.js.map +0 -1
  90. package/lib/module/pid/metadata.js +0 -41
  91. package/lib/module/pid/metadata.js.map +0 -1
  92. package/lib/typescript/pid/metadata.d.ts +0 -482
  93. package/lib/typescript/pid/metadata.d.ts.map +0 -1
  94. package/src/pid/metadata.ts +0 -46
package/src/rp/index.ts CHANGED
@@ -10,113 +10,109 @@ import {
10
10
  SignJWT,
11
11
  EncryptJwe,
12
12
  verify,
13
+ type CryptoContext,
13
14
  } from "@pagopa/io-react-native-jwt";
14
- import {
15
- QRCodePayload,
16
- RequestObject,
17
- RpEntityConfiguration,
18
- type Presentation,
19
- } from "./types";
15
+ import { QRCodePayload, RequestObject, type Presentation } from "./types";
20
16
 
21
17
  import uuid from "react-native-uuid";
22
18
  import type { JWK } from "@pagopa/io-react-native-jwt/lib/typescript/types";
23
19
  import { disclose } from "../sd-jwt";
24
- import { getEntityConfiguration } from "../trust";
20
+ import { createDPopToken } from "../utils/dpop";
21
+ import { RelyingPartyEntityConfiguration } from "../trust/types";
22
+ import * as WalletInstanceAttestation from "../wallet-instance-attestation";
25
23
 
26
- export class RelyingPartySolution {
27
- relyingPartyBaseUrl: string;
28
- walletInstanceAttestation: string;
29
- appFetch: GlobalFetch["fetch"];
24
+ /**
25
+ * Select a RSA public key from those provided by the RP to encrypt.
26
+ *
27
+ * @param entity The RP entity configuration
28
+ * @returns A suitable public key with its compatible encryption algorithm
29
+ * @throws {NoSuitableKeysFoundInEntityConfiguration} If entity do not contain any public key suitable for encrypting
30
+ */
31
+ const chooseRSAPublicKeyToEncrypt = (
32
+ entity: RelyingPartyEntityConfiguration
33
+ ): JWK => {
34
+ const [usingRsa256] =
35
+ entity.payload.metadata.wallet_relying_party.jwks.filter(
36
+ (jwk) => jwk.use === "enc" && jwk.kty === "RSA"
37
+ );
30
38
 
31
- constructor(
32
- relyingPartyBaseUrl: string,
33
- walletInstanceAttestation: string,
34
- appFetch: GlobalFetch["fetch"] = fetch
35
- ) {
36
- this.relyingPartyBaseUrl = relyingPartyBaseUrl;
37
- this.walletInstanceAttestation = walletInstanceAttestation;
38
- this.appFetch = appFetch;
39
+ if (usingRsa256) {
40
+ return usingRsa256;
39
41
  }
40
42
 
41
- /**
42
- * Decode a QR code content to an authentication request url.
43
- * @function
44
- * @param qrcode QR code content
45
- *
46
- * @returns The authentication request url
47
- *
48
- */
49
- static decodeAuthRequestQR(qrcode: string): QRCodePayload {
50
- const decoded = decodeBase64(qrcode);
51
- const decodedUrl = new URL(decoded);
52
- const protocol = decodedUrl.protocol;
53
- const resource = decodedUrl.hostname;
54
- const requestURI = decodedUrl.searchParams.get("request_uri");
55
- const clientId = decodedUrl.searchParams.get("client_id");
43
+ // No suitable key has been found
44
+ throw new NoSuitableKeysFoundInEntityConfiguration(
45
+ "Encrypt with RP public key"
46
+ );
47
+ };
56
48
 
57
- const result = QRCodePayload.safeParse({
58
- protocol,
59
- resource,
60
- requestURI,
61
- clientId,
62
- });
49
+ /**
50
+ * Decode a QR code content to an authentication request url.
51
+ * @function
52
+ * @param qrcode QR code content
53
+ *
54
+ * @returns The authentication request url
55
+ *
56
+ */
57
+ export const decodeAuthRequestQR = (qrcode: string): QRCodePayload => {
58
+ const decoded = decodeBase64(qrcode);
59
+ const decodedUrl = new URL(decoded);
60
+ const protocol = decodedUrl.protocol;
61
+ const resource = decodedUrl.hostname;
62
+ const requestURI = decodedUrl.searchParams.get("request_uri");
63
+ const clientId = decodedUrl.searchParams.get("client_id");
63
64
 
64
- if (result.success) {
65
- return result.data;
66
- } else {
67
- throw new AuthRequestDecodeError(result.error.message, `${decodedUrl}`);
68
- }
69
- }
70
- /**
71
- * Obtain the unsigned wallet instance DPoP for authentication request
72
- *
73
- * @function
74
- * @param walletInstanceAttestationJwk JWT of the Wallet Instance Attestation
75
- * @param authRequestUrl authentication request url
76
- *
77
- * @returns The unsigned wallet instance DPoP
78
- *
79
- */
80
- async getUnsignedWalletInstanceDPoP(
81
- walletInstanceAttestationJwk: any,
82
- authRequestUrl: string
83
- ): Promise<string> {
84
- return await new SignJWT({
85
- jti: `${uuid.v4()}`,
86
- htm: "GET",
87
- htu: authRequestUrl,
88
- ath: await sha256ToBase64(this.walletInstanceAttestation),
89
- })
90
- .setProtectedHeader({
91
- alg: "ES256",
92
- jwk: walletInstanceAttestationJwk,
93
- typ: "dpop+jwt",
94
- })
95
- .setIssuedAt()
96
- .setExpirationTime("1h")
97
- .toSign();
65
+ const result = QRCodePayload.safeParse({
66
+ protocol,
67
+ resource,
68
+ requestURI,
69
+ clientId,
70
+ });
71
+
72
+ if (result.success) {
73
+ return result.data;
74
+ } else {
75
+ throw new AuthRequestDecodeError(result.error.message, `${decodedUrl}`);
98
76
  }
77
+ };
78
+
79
+ export type RequestObjectConf = {
80
+ requestObject: RequestObject;
81
+ rpEntityConfiguration: RelyingPartyEntityConfiguration;
82
+ walletInstanceAttestation: string;
83
+ };
99
84
 
100
- /**
101
- * Obtain the Request Object for RP authentication
102
- * @see https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/relying-party-solution.html
103
- *
104
- * @async @function
105
- * @param signedWalletInstanceDPoP JWT of the Wallet Instance Attestation DPoP
106
- *
107
- * @returns The Request Object JWT
108
- * @throws {NoSuitableKeysFoundInEntityConfiguration} When the Request Object is signed with a key not listed in RP's entity configuration
109
- *
110
- */
111
- async getRequestObject(
112
- signedWalletInstanceDPoP: string,
85
+ /**
86
+ * Obtain the Request Object for RP authentication
87
+ * @see https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/relying-party-solution.html
88
+ */
89
+ export const getRequestObject =
90
+ ({
91
+ wiaCryptoContext,
92
+ appFetch = fetch,
93
+ }: {
94
+ wiaCryptoContext: CryptoContext;
95
+ appFetch?: GlobalFetch["fetch"];
96
+ }) =>
97
+ async (
98
+ walletInstanceAttestation: string,
113
99
  requestUri: string,
114
- entity: RpEntityConfiguration
115
- ): Promise<RequestObject> {
116
- const response = await this.appFetch(requestUri, {
100
+ rpEntityConfiguration: RelyingPartyEntityConfiguration
101
+ ): Promise<RequestObjectConf> => {
102
+ const signedWalletInstanceDPoP = await createDPopToken(
103
+ {
104
+ jti: `${uuid.v4()}`,
105
+ htm: "GET",
106
+ htu: requestUri,
107
+ ath: await sha256ToBase64(walletInstanceAttestation),
108
+ },
109
+ wiaCryptoContext
110
+ );
111
+
112
+ const response = await appFetch(requestUri, {
117
113
  method: "GET",
118
114
  headers: {
119
- Authorization: `DPoP ${this.walletInstanceAttestation}`,
115
+ Authorization: `DPoP ${walletInstanceAttestation}`,
120
116
  DPoP: signedWalletInstanceDPoP,
121
117
  },
122
118
  });
@@ -130,9 +126,10 @@ export class RelyingPartySolution {
130
126
  // verify token signature according to RP's entity configuration
131
127
  // to ensure the request object is authentic
132
128
  {
133
- const pubKey = entity.payload.metadata.wallet_relying_party.jwks.find(
134
- ({ kid }) => kid === responseJwt.protectedHeader.kid
135
- );
129
+ const pubKey =
130
+ rpEntityConfiguration.payload.metadata.wallet_relying_party.jwks.find(
131
+ ({ kid }) => kid === responseJwt.protectedHeader.kid
132
+ );
136
133
  if (!pubKey) {
137
134
  throw new NoSuitableKeysFoundInEntityConfiguration(
138
135
  "Request Object signature verification"
@@ -142,67 +139,68 @@ export class RelyingPartySolution {
142
139
  }
143
140
 
144
141
  // parse request object it has the expected shape by specification
145
- const requestObj = RequestObject.parse({
142
+ const requestObject = RequestObject.parse({
146
143
  header: responseJwt.protectedHeader,
147
144
  payload: responseJwt.payload,
148
145
  });
149
146
 
150
- return requestObj;
147
+ return {
148
+ requestObject,
149
+ rpEntityConfiguration,
150
+ walletInstanceAttestation,
151
+ };
151
152
  }
152
153
 
153
154
  throw new IoWalletError(
154
- `Unable to obtain Request Object. Response code: ${response.status}`
155
+ `Unable to obtain Request Object. Response code: ${response.status}
156
+ ${await response.text()}`
155
157
  );
156
- }
158
+ };
157
159
 
158
- /**
159
- * Prepare the Verified Presentation token for a received request object in the context of an authorization request flow.
160
- * The presentation is prepared by disclosing data from provided credentials, according to requested claims
161
- * Each Verified Credential come along with the claims the user accepts to disclose from it.
162
- *
163
- * The returned token is unsigned (sign should be apply by the caller).
164
- *
165
- * @todo accept more than a Verified Credential
166
- *
167
- * @param requestObj The incoming request object, which the requirements for the requested authorization
168
- * @param walletInstanceIdentifier The identifies of the wallt instance that is presenting
169
- * @param presentation The Verified Credential containing user data along with the list of claims to be disclosed.
170
- * @param signKeyId The kid of the key that will be used to sign
171
- * @returns The unsigned Verified Presentation token
172
- * @throws {ClaimsNotFoundBetweenDislosures} If the Verified Credential does not contain one or more requested claims.
173
- *
174
- */
175
- async prepareVpToken(
176
- requestObj: RequestObject,
177
- walletInstanceIdentifier: string,
178
- [vc, claims]: Presentation, // TODO: [SIW-353] support multiple presentations,
179
- signKeyId: string
160
+ /**
161
+ * Prepare the Verified Presentation token for a received request object in the context of an authorization request flow.
162
+ * The presentation is prepared by disclosing data from provided credentials, according to requested claims
163
+ * Each Verified Credential come along with the claims the user accepts to disclose from it.
164
+ *
165
+ * @todo accept more than a Verified Credential
166
+ */
167
+ const prepareVpToken =
168
+ ({ pidCryptoContext }: { pidCryptoContext: CryptoContext }) =>
169
+ async (
170
+ { requestObject, walletInstanceAttestation }: RequestObjectConf,
171
+ [vc, claims]: Presentation // TODO: [SIW-353] support multiple presentations,
180
172
  ): Promise<{
181
173
  vp_token: string;
182
174
  presentation_submission: Record<string, unknown>;
183
- }> {
175
+ }> => {
184
176
  // this throws if vc cannot satisfy all the requested claims
185
177
  const { token: vp, paths } = await disclose(vc, claims);
186
178
 
187
- // TODO: [SIW-359] check all requeste claims of the requestedObj are satisfied
179
+ // obtain issuer from Wallet Instance
180
+ const {
181
+ payload: { iss },
182
+ } = WalletInstanceAttestation.decode(walletInstanceAttestation);
188
183
 
189
- const vp_token = new SignJWT({
190
- vp: vp,
191
- jti: `${uuid.v4()}`,
192
- iss: walletInstanceIdentifier,
193
- nonce: requestObj.payload.nonce,
194
- })
195
- .setAudience(requestObj.payload.response_uri)
196
- .setIssuedAt()
197
- .setExpirationTime("1h")
184
+ const pidKid = await pidCryptoContext.getPublicKey().then((_) => _.kid);
185
+
186
+ // TODO: [SIW-359] check all requeste claims of the requestedObj are satisfied
187
+ const vp_token = await new SignJWT(pidCryptoContext)
198
188
  .setProtectedHeader({
199
189
  typ: "JWT",
200
- alg: "ES256",
201
- kid: signKeyId,
190
+ kid: pidKid,
191
+ })
192
+ .setPayload({
193
+ vp: vp,
194
+ jti: `${uuid.v4()}`,
195
+ iss,
196
+ nonce: requestObject.payload.nonce,
202
197
  })
203
- .toSign();
198
+ .setAudience(requestObject.payload.response_uri)
199
+ .setIssuedAt()
200
+ .setExpirationTime("1h")
201
+ .sign();
204
202
 
205
- const vc_scope = requestObj.payload.scope;
203
+ const vc_scope = requestObject.payload.scope;
206
204
  const presentation_submission = {
207
205
  definition_id: `${uuid.v4()}`,
208
206
  id: `${uuid.v4()}`,
@@ -214,36 +212,49 @@ export class RelyingPartySolution {
214
212
  };
215
213
 
216
214
  return { vp_token, presentation_submission };
217
- }
215
+ };
218
216
 
219
- /**
220
- * Compose and send an Authorization Response in the context of an authorization request flow.
221
- *
222
- * @todo MUST add presentation_submission
223
- *
224
- * @param requestObj The incoming request object, which the requirements for the requested authorization
225
- * @param vp_token The signed Verified Presentation token with data to send.
226
- * @param presentation_submission
227
- * @param entity The RP entity configuration
228
- * @returns The response from the RP
229
- * @throws {IoWalletError} if the submission fails.
230
- * @throws {NoSuitableKeysFoundInEntityConfiguration} If entity do not contain any public key
231
- *
232
- */
233
- async sendAuthorizationResponse(
234
- requestObj: RequestObject,
235
- vp_token: string,
236
- presentation_submission: Record<string, unknown>,
237
- entity: RpEntityConfiguration
238
- ): Promise<string> {
217
+ /**
218
+ * Compose and send an Authorization Response in the context of an authorization request flow.
219
+ *
220
+ * @todo MUST add presentation_submission
221
+ *
222
+ */
223
+ export const sendAuthorizationResponse =
224
+ ({
225
+ pidCryptoContext,
226
+ appFetch = fetch,
227
+ }: {
228
+ pidCryptoContext: CryptoContext;
229
+ appFetch?: GlobalFetch["fetch"];
230
+ }) =>
231
+ async (
232
+ {
233
+ requestObject,
234
+ rpEntityConfiguration,
235
+ walletInstanceAttestation,
236
+ }: RequestObjectConf,
237
+ presentation: Presentation // TODO: [SIW-353] support multiple presentations,
238
+ ): Promise<string> => {
239
239
  // the request is an unsigned jws without iss, aud, exp
240
240
  // https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-signed-and-encrypted-respon
241
- const jwk = this.chooseRSAPublicKeyToEncrypt(entity);
241
+ const jwk = chooseRSAPublicKeyToEncrypt(rpEntityConfiguration);
242
+
243
+ const { vp_token, presentation_submission } = await prepareVpToken({
244
+ pidCryptoContext,
245
+ })(
246
+ {
247
+ requestObject,
248
+ rpEntityConfiguration,
249
+ walletInstanceAttestation,
250
+ },
251
+ presentation
252
+ );
242
253
 
243
254
  const authzResponsePayload = JSON.stringify({
244
- state: requestObj.payload.state,
255
+ state: requestObject.payload.state,
245
256
  presentation_submission,
246
- nonce: requestObj.payload.nonce,
257
+ nonce: requestObject.payload.nonce,
247
258
  vp_token,
248
259
  });
249
260
 
@@ -256,7 +267,7 @@ export class RelyingPartySolution {
256
267
  const formBody = new URLSearchParams({ response: encrypted });
257
268
  const body = formBody.toString();
258
269
 
259
- const response = await this.appFetch(requestObj.payload.response_uri, {
270
+ const response = await appFetch(requestObject.payload.response_uri, {
260
271
  method: "POST",
261
272
  headers: {
262
273
  "Content-Type": "application/x-www-form-urlencoded",
@@ -273,37 +284,4 @@ export class RelyingPartySolution {
273
284
  response.status
274
285
  }`
275
286
  );
276
- }
277
-
278
- /**
279
- * Select a RSA public key from those provided by the RP to encrypt.
280
- *
281
- * @param entity The RP entity configuration
282
- * @returns A suitable public key with its compatible encryption algorithm
283
- * @throws {NoSuitableKeysFoundInEntityConfiguration} If entity do not contain any public key suitable for encrypting
284
- */
285
- private chooseRSAPublicKeyToEncrypt(entity: RpEntityConfiguration): JWK {
286
- const [usingRsa256] =
287
- entity.payload.metadata.wallet_relying_party.jwks.filter(
288
- (jwk) => jwk.use === "enc" && jwk.kty === "RSA"
289
- );
290
-
291
- if (usingRsa256) {
292
- return usingRsa256;
293
- }
294
-
295
- // No suitable key has been found
296
- throw new NoSuitableKeysFoundInEntityConfiguration(
297
- "Encrypt with RP public key"
298
- );
299
- }
300
-
301
- /**
302
- * Obtain the relying party entity configuration.
303
- */
304
- async getEntityConfiguration(): Promise<RpEntityConfiguration> {
305
- return getEntityConfiguration(this.relyingPartyBaseUrl, {
306
- appFetch: this.appFetch,
307
- }).then(RpEntityConfiguration.parse);
308
- }
309
- }
287
+ };
package/src/rp/types.ts CHANGED
@@ -1,7 +1,5 @@
1
- import { JWK } from "../utils/jwk";
2
1
  import { UnixTime } from "../sd-jwt/types";
3
2
  import * as z from "zod";
4
- import { EntityConfiguration } from "../trust/types";
5
3
 
6
4
  export type RequestObject = z.infer<typeof RequestObject>;
7
5
  export const RequestObject = z.object({
@@ -27,28 +25,6 @@ export const RequestObject = z.object({
27
25
  }),
28
26
  });
29
27
 
30
- /**
31
- * EntityConfiguration plus the metadata specific for a Relying Party entity.
32
- */
33
- export type RpEntityConfiguration = z.infer<typeof RpEntityConfiguration>;
34
- export const RpEntityConfiguration = EntityConfiguration.and(
35
- z.object({
36
- payload: z.object({
37
- metadata: z.object({
38
- wallet_relying_party: z
39
- .object({
40
- application_type: z.string().optional(),
41
- client_id: z.string().optional(),
42
- client_name: z.string().optional(),
43
- jwks: z.array(JWK),
44
- contacts: z.array(z.string()).optional(),
45
- })
46
- .passthrough(),
47
- }),
48
- }),
49
- })
50
- );
51
-
52
28
  export type QRCodePayload = z.infer<typeof QRCodePayload>;
53
29
  export const QRCodePayload = z.object({
54
30
  protocol: z.string(),
@@ -7,7 +7,7 @@ import { sha256ToBase64 } from "@pagopa/io-react-native-jwt";
7
7
  import { decodeBase64 } from "@pagopa/io-react-native-jwt";
8
8
  import { Disclosure, SdJwt4VC, type DisclosureWithEncoded } from "./types";
9
9
  import { verifyDisclosure } from "./verifier";
10
- import type { JWK } from "src/utils/jwk";
10
+ import type { JWK } from "../utils/jwk";
11
11
  import {
12
12
  ClaimsNotFoundBetweenDislosures,
13
13
  ClaimsNotFoundInToken,
@@ -1,27 +1,82 @@
1
1
  import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
2
- import { EntityConfiguration } from "./types";
2
+ import {
3
+ WalletProviderEntityConfiguration,
4
+ TrustAnchorEntityConfiguration,
5
+ CredentialIssuerEntityConfiguration,
6
+ RelyingPartyEntityConfiguration,
7
+ EntityConfiguration,
8
+ } from "./types";
3
9
  import { IoWalletError } from "../utils/errors";
4
10
  import { verifyTrustChain } from "./chain";
5
11
 
6
12
  export { verifyTrustChain };
7
13
 
8
14
  /**
9
- * Fetch and parse teh entity configuration document for a given federation entity
15
+ * Fetch and parse the entity configuration document for a given federation entity.
16
+ * This is an inner method to serve public interfaces.
17
+ *
18
+ * To add another entity configuration type (example: Foo entity type):
19
+ * - create its zod schema and type by inherit from the base type (example: FooEntityConfiguration = BaseEntityConfiguration.and(...))
20
+ * - add such type to EntityConfiguration union
21
+ * - add an overload to this function
22
+ * - create a public function which use such type (example: getFooEntityConfiguration = (url, options) => Promise<FooEntityConfiguration>)
10
23
  *
11
24
  * @param entityBaseUrl The base url of the entity.
25
+ * @param schema The expected schema of the entity configuration, according to the kind of entity we are fetching from.
12
26
  * @param options.appFetch An optional instance of the http client to be used.
13
27
  * @returns The parsed entity configuration object
14
28
  * @throws {IoWalletError} If the http request fails
15
29
  * @throws Parse error if the document is not in the expected shape.
16
30
  */
17
- export async function getEntityConfiguration(
31
+ async function fetchAndParseEntityConfiguration(
32
+ entityBaseUrl: string,
33
+ schema: typeof WalletProviderEntityConfiguration,
34
+ options?: {
35
+ appFetch?: GlobalFetch["fetch"];
36
+ }
37
+ ): Promise<WalletProviderEntityConfiguration>;
38
+ async function fetchAndParseEntityConfiguration(
39
+ entityBaseUrl: string,
40
+ schema: typeof RelyingPartyEntityConfiguration,
41
+ options?: {
42
+ appFetch?: GlobalFetch["fetch"];
43
+ }
44
+ ): Promise<RelyingPartyEntityConfiguration>;
45
+ async function fetchAndParseEntityConfiguration(
46
+ entityBaseUrl: string,
47
+ schema: typeof TrustAnchorEntityConfiguration,
48
+ options?: {
49
+ appFetch?: GlobalFetch["fetch"];
50
+ }
51
+ ): Promise<TrustAnchorEntityConfiguration>;
52
+ async function fetchAndParseEntityConfiguration(
53
+ entityBaseUrl: string,
54
+ schema: typeof CredentialIssuerEntityConfiguration,
55
+ options?: {
56
+ appFetch?: GlobalFetch["fetch"];
57
+ }
58
+ ): Promise<CredentialIssuerEntityConfiguration>;
59
+ async function fetchAndParseEntityConfiguration(
18
60
  entityBaseUrl: string,
61
+ schema: typeof EntityConfiguration,
62
+ options?: {
63
+ appFetch?: GlobalFetch["fetch"];
64
+ }
65
+ ): Promise<EntityConfiguration>;
66
+ async function fetchAndParseEntityConfiguration(
67
+ entityBaseUrl: string,
68
+ schema: /* FIXME: why is it different from "typeof EntityConfiguration"? */
69
+ | typeof CredentialIssuerEntityConfiguration
70
+ | typeof WalletProviderEntityConfiguration
71
+ | typeof RelyingPartyEntityConfiguration
72
+ | typeof TrustAnchorEntityConfiguration
73
+ | typeof EntityConfiguration,
19
74
  {
20
75
  appFetch = fetch,
21
76
  }: {
22
77
  appFetch?: GlobalFetch["fetch"];
23
78
  } = {}
24
- ): Promise<EntityConfiguration> {
79
+ ) {
25
80
  const wellKnownUrl = `${entityBaseUrl}/.well-known/openid-federation`;
26
81
 
27
82
  const response = await appFetch(wellKnownUrl, {
@@ -31,7 +86,7 @@ export async function getEntityConfiguration(
31
86
  if (response.status === 200) {
32
87
  const responseText = await response.text();
33
88
  const responseJwt = decodeJwt(responseText);
34
- return EntityConfiguration.parse({
89
+ return schema.parse({
35
90
  header: responseJwt.protectedHeader,
36
91
  payload: responseJwt.payload,
37
92
  });
@@ -41,3 +96,49 @@ export async function getEntityConfiguration(
41
96
  `Unable to obtain Entity Configuration at ${wellKnownUrl}. Response code: ${response.status}`
42
97
  );
43
98
  }
99
+
100
+ export const getWalletProviderEntityConfiguration = (
101
+ entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
102
+ options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
103
+ ) =>
104
+ fetchAndParseEntityConfiguration(
105
+ entityBaseUrl,
106
+ WalletProviderEntityConfiguration,
107
+ options
108
+ );
109
+
110
+ export const getCredentialIssuerEntityConfiguration = (
111
+ entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
112
+ options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
113
+ ) =>
114
+ fetchAndParseEntityConfiguration(
115
+ entityBaseUrl,
116
+ CredentialIssuerEntityConfiguration,
117
+ options
118
+ );
119
+
120
+ export const getTrustAnchorEntityConfiguration = (
121
+ entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
122
+ options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
123
+ ) =>
124
+ fetchAndParseEntityConfiguration(
125
+ entityBaseUrl,
126
+ TrustAnchorEntityConfiguration,
127
+ options
128
+ );
129
+
130
+ export const getRelyingPartyEntityConfiguration = (
131
+ entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
132
+ options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
133
+ ) =>
134
+ fetchAndParseEntityConfiguration(
135
+ entityBaseUrl,
136
+ RelyingPartyEntityConfiguration,
137
+ options
138
+ );
139
+
140
+ export const getEntityConfiguration = (
141
+ entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
142
+ options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
143
+ ) =>
144
+ fetchAndParseEntityConfiguration(entityBaseUrl, EntityConfiguration, options);