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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -1,18 +1,18 @@
1
1
  import {
2
- decode as decodeJwt,
3
- verify as verifyJwt,
4
2
  sha256ToBase64,
3
+ type CryptoContext,
4
+ SignJWT,
5
+ thumbprint,
5
6
  } from "@pagopa/io-react-native-jwt";
6
-
7
- import { SignJWT, thumbprint } from "@pagopa/io-react-native-jwt";
8
7
  import { JWK } from "../utils/jwk";
9
8
  import uuid from "react-native-uuid";
10
- import { PidIssuingError, PidMetadataError } from "../utils/errors";
11
- import { getUnsignedDPop } from "../utils/dpop";
12
- import { sign, generate, deleteKey } from "@pagopa/io-react-native-crypto";
13
- import { PidIssuerEntityConfiguration } from "./metadata";
14
- import { fixBase64EncodingOnKey } from "./../utils/jwk";
15
-
9
+ import { PidIssuingError } from "../utils/errors";
10
+ import { createDPopToken } from "../utils/dpop";
11
+ import { CredentialIssuerEntityConfiguration } from "../trust/types";
12
+ import * as WalletInstanceAttestation from "../wallet-instance-attestation";
13
+ import { generate, deleteKey } from "@pagopa/io-react-native-crypto";
14
+ import { SdJwt } from ".";
15
+ import { createCryptoContextFor } from "../utils/crypto";
16
16
  // This is a temporary type that will be used for demo purposes only
17
17
  export type CieData = {
18
18
  birthDate: string;
@@ -21,7 +21,15 @@ export type CieData = {
21
21
  surname: string;
22
22
  };
23
23
 
24
- export type TokenResponse = { access_token: string; c_nonce: string };
24
+ export type AuthorizationConf = {
25
+ accessToken: string;
26
+ nonce: string;
27
+ clientId: string;
28
+ authorizationCode: string;
29
+ codeVerifier: string;
30
+ walletProviderBaseUrl: string;
31
+ };
32
+
25
33
  export type PidResponse = {
26
34
  credential: string;
27
35
  c_nonce: string;
@@ -29,111 +37,87 @@ export type PidResponse = {
29
37
  format: string;
30
38
  };
31
39
 
32
- export class Issuing {
33
- pidProviderBaseUrl: string;
34
- walletProviderBaseUrl: string;
35
- walletInstanceAttestation: string;
36
- codeVerifier: string;
37
- clientId: string;
38
- state: string;
39
- authorizationCode: string;
40
- appFetch: GlobalFetch["fetch"];
40
+ const assertionType =
41
+ "urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation";
41
42
 
42
- constructor(
43
- pidProviderBaseUrl: string,
44
- walletProviderBaseUrl: string,
45
- walletInstanceAttestation: string,
43
+ /**
44
+ * Make a PAR request to the PID issuer and return the response url
45
+ */
46
+ const getPar =
47
+ ({
48
+ wiaCryptoContext,
49
+ appFetch = fetch,
50
+ }: {
51
+ wiaCryptoContext: CryptoContext;
52
+ appFetch?: GlobalFetch["fetch"];
53
+ }) =>
54
+ async (
46
55
  clientId: string,
47
- appFetch: GlobalFetch["fetch"] = fetch
48
- ) {
49
- this.pidProviderBaseUrl = pidProviderBaseUrl;
50
- this.walletProviderBaseUrl = walletProviderBaseUrl;
51
- this.state = `${uuid.v4()}`;
52
- this.codeVerifier = `${uuid.v4()}`;
53
- this.authorizationCode = `${uuid.v4()}`;
54
- this.walletInstanceAttestation = walletInstanceAttestation;
55
- this.clientId = clientId;
56
- this.appFetch = appFetch;
57
- }
56
+ codeVerifier: string,
57
+ walletProviderBaseUrl: string,
58
+ pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration,
59
+ walletInstanceAttestation: string
60
+ ): Promise<string> => {
61
+ // Calculate the thumbprint of the public key of the Wallet Instance Attestation.
62
+ // The PAR request token is signed used the Wallet Instance Attestation key.
63
+ // The signature can be verified by reading the public key from the key set shippet with the it will ship the Wallet Instance Attestation;
64
+ // key is matched by its kid, which is supposed to be the thumbprint of its public key.
65
+ const keyThumbprint = await wiaCryptoContext
66
+ .getPublicKey()
67
+ .then(JWK.parse)
68
+ .then(thumbprint);
58
69
 
59
- /**
60
- * Return the unsigned jwt to call the PAR request.
61
- *
62
- * @function
63
- * @param jwk The wallet instance attestation public JWK
64
- *
65
- * @returns Unsigned jwt
66
- *
67
- */
68
- async getUnsignedJwtForPar(jwk: JWK): Promise<string> {
69
- const parsedJwk = JWK.parse(jwk);
70
- const keyThumbprint = await thumbprint(parsedJwk);
71
- const publicKey = { ...parsedJwk, kid: keyThumbprint };
72
- const codeChallenge = await sha256ToBase64(this.codeVerifier);
70
+ const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
71
+ .payload.cnf.jwk.kid;
73
72
 
74
- const unsignedJwtForPar = new SignJWT({
75
- client_assertion_type:
76
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
77
- authorization_details: [
78
- {
79
- credentialDefinition: {
80
- type: ["eu.eudiw.pid.it"],
81
- },
82
- format: "vc+sd-jwt",
83
- type: "type",
84
- },
85
- ],
86
- response_type: "code",
87
- code_challenge_method: "s256",
88
- redirect_uri: this.walletProviderBaseUrl,
89
- state: this.state,
90
- client_id: this.clientId,
91
- code_challenge: codeChallenge,
92
- })
73
+ const codeChallenge = await sha256ToBase64(codeVerifier);
74
+
75
+ const signedJwtForPar = await new SignJWT(wiaCryptoContext)
93
76
  .setProtectedHeader({
94
- alg: "ES256",
95
- kid: publicKey.kid,
77
+ kid: keyThumbprint,
78
+ })
79
+ .setPayload({
80
+ iss,
81
+ aud: pidProviderEntityConfiguration.payload.iss,
82
+ jti: `${uuid.v4()}`,
83
+ client_assertion_type: assertionType,
84
+ authorization_details: [
85
+ {
86
+ credential_definition: {
87
+ type: "PersonIdentificationData",
88
+ },
89
+ format: "vc+sd-jwt",
90
+ type: "openid_credential",
91
+ },
92
+ ],
93
+ response_type: "code",
94
+ code_challenge_method: "s256",
95
+ redirect_uri: walletProviderBaseUrl,
96
+ state: `${uuid.v4()}`,
97
+ client_id: clientId,
98
+ code_challenge: codeChallenge,
96
99
  })
97
100
  .setIssuedAt()
98
101
  .setExpirationTime("1h")
99
- .toSign();
102
+ .sign();
100
103
 
101
- return unsignedJwtForPar;
102
- }
103
-
104
- /**
105
- * Make a PAR request to the PID issuer and return the response url
106
- *
107
- * @function
108
- * @param unsignedJwtForPar The unsigned JWT for PAR
109
- * @param signature The JWT for PAR signature
110
- *
111
- * @returns Unsigned PAR url
112
- *
113
- */
114
- async getPar(unsignedJwtForPar: string, signature: string): Promise<string> {
115
- const codeChallenge = await sha256ToBase64(this.codeVerifier);
116
- const signedJwtForPar = await SignJWT.appendSignature(
117
- unsignedJwtForPar,
118
- signature
119
- );
120
-
121
- const parUrl = new URL("/as/par", this.pidProviderBaseUrl).href;
104
+ const parUrl =
105
+ pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
106
+ .pushed_authorization_request_endpoint;
122
107
 
123
108
  const requestBody = {
124
109
  response_type: "code",
125
- client_id: this.clientId,
110
+ client_id: clientId,
126
111
  code_challenge: codeChallenge,
127
112
  code_challenge_method: "S256",
128
- client_assertion_type:
129
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
130
- client_assertion: this.walletInstanceAttestation,
113
+ client_assertion_type: assertionType,
114
+ client_assertion: walletInstanceAttestation,
131
115
  request: signedJwtForPar,
132
116
  };
133
117
 
134
118
  var formBody = new URLSearchParams(requestBody);
135
119
 
136
- const response = await this.appFetch(parUrl, {
120
+ const response = await appFetch(parUrl, {
137
121
  method: "POST",
138
122
  headers: {
139
123
  "Content-Type": "application/x-www-form-urlencoded",
@@ -149,61 +133,75 @@ export class Issuing {
149
133
  throw new PidIssuingError(
150
134
  `Unable to obtain PAR. Response code: ${await response.text()}`
151
135
  );
152
- }
136
+ };
153
137
 
154
- /**
155
- * Return the unsigned jwt for a generic DPoP
156
- *
157
- * @function
158
- * @param jwk the public key for which the DPoP is to be created
159
- *
160
- * @returns Unsigned JWT for DPoP
161
- *
162
- */
163
- async getUnsignedDPoP(jwk: JWK): Promise<string> {
164
- const tokenUrl = new URL("/token", this.pidProviderBaseUrl).href;
165
- const dPop = getUnsignedDPop(fixBase64EncodingOnKey(jwk), {
166
- htm: "POST",
167
- htu: tokenUrl,
168
- jti: `${uuid.v4()}`,
169
- });
170
- return dPop;
171
- }
138
+ /**
139
+ * Start the issuing flow by generating an authorization request to the PID Provider. Obtain from the PID Provider an access token to be used to complete the issuing flow.
140
+ *
141
+ * @param params.wiaCryptoContext The key pair associated with the WIA. Will be use to prove the ownership of the attestation.
142
+ * @param params.appFetch (optional) Http client
143
+ * @param walletInstanceAttestation Wallet Instance Attestation token.
144
+ * @param walletProviderBaseUrl Base url for the Wallet Provider
145
+ * @param pidProviderEntityConfiguration The Entity Configuration of the PID Provider, from which discover public endooints.
146
+ * @returns The access token along with the values that identify the issuing session.
147
+ */
148
+ export const authorizeIssuing =
149
+ ({
150
+ wiaCryptoContext,
151
+ appFetch = fetch,
152
+ }: {
153
+ wiaCryptoContext: CryptoContext;
154
+ appFetch?: GlobalFetch["fetch"];
155
+ }) =>
156
+ async (
157
+ walletInstanceAttestation: string,
158
+ walletProviderBaseUrl: string,
159
+ pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration
160
+ ): Promise<AuthorizationConf> => {
161
+ // FIXME: do better
162
+ const clientId = await wiaCryptoContext.getPublicKey().then((_) => _.kid);
163
+ const codeVerifier = `${uuid.v4()}`;
164
+ const authorizationCode = `${uuid.v4()}`;
165
+ const tokenUrl =
166
+ pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
167
+ .token_endpoint;
168
+
169
+ await getPar({ wiaCryptoContext, appFetch })(
170
+ clientId,
171
+ codeVerifier,
172
+ walletProviderBaseUrl,
173
+ pidProviderEntityConfiguration,
174
+ walletInstanceAttestation
175
+ );
172
176
 
173
- /**
174
- * Make an auth token request to the PID issuer
175
- *
176
- * @function
177
- * @returns a token response
178
- *
179
- */
180
- async getAuthToken(): Promise<TokenResponse> {
181
- //Generate fresh keys for DPoP
182
- const dPopKeyTag = `${uuid.v4()}`;
183
- const dPopKey = await generate(dPopKeyTag);
184
- const unsignedDPopForToken = await this.getUnsignedDPoP(dPopKey);
185
- const dPopTokenSignature = await sign(unsignedDPopForToken, dPopKeyTag);
186
- await deleteKey(dPopKeyTag);
177
+ // Use an ephemeral key to be destroyed after use
178
+ const keytag = `ephemeral-${uuid.v4()}`;
179
+ await generate(keytag);
180
+ const ephemeralContext = createCryptoContextFor(keytag);
187
181
 
188
- const signedDPop = await SignJWT.appendSignature(
189
- unsignedDPopForToken,
190
- dPopTokenSignature
182
+ const signedDPop = await createDPopToken(
183
+ {
184
+ htm: "POST",
185
+ htu: tokenUrl,
186
+ jti: `${uuid.v4()}`,
187
+ },
188
+ ephemeralContext
191
189
  );
192
- const decodedJwtDPop = decodeJwt(signedDPop);
193
- const tokenUrl = decodedJwtDPop.payload.htu as string;
190
+
191
+ await deleteKey(keytag);
192
+
194
193
  const requestBody = {
195
194
  grant_type: "authorization code",
196
- client_id: this.clientId,
197
- code: this.authorizationCode,
198
- code_verifier: this.codeVerifier,
199
- client_assertion_type:
200
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
201
- client_assertion: this.walletInstanceAttestation,
202
- redirect_uri: this.walletProviderBaseUrl,
195
+ client_id: clientId,
196
+ code: authorizationCode,
197
+ code_verifier: codeVerifier,
198
+ client_assertion_type: assertionType,
199
+ client_assertion: walletInstanceAttestation,
200
+ redirect_uri: walletProviderBaseUrl,
203
201
  };
204
202
  var formBody = new URLSearchParams(requestBody);
205
203
 
206
- const response = await this.appFetch(tokenUrl, {
204
+ const response = await appFetch(tokenUrl, {
207
205
  method: "POST",
208
206
  headers: {
209
207
  "Content-Type": "application/x-www-form-urlencoded",
@@ -213,73 +211,92 @@ export class Issuing {
213
211
  });
214
212
 
215
213
  if (response.status === 200) {
216
- return await response.json();
214
+ const { c_nonce, access_token } = await response.json();
215
+ return {
216
+ accessToken: access_token,
217
+ nonce: c_nonce,
218
+ clientId,
219
+ codeVerifier,
220
+ authorizationCode,
221
+ walletProviderBaseUrl,
222
+ };
217
223
  }
218
224
 
219
225
  throw new PidIssuingError(
220
226
  `Unable to obtain token. Response code: ${await response.text()}`
221
227
  );
222
- }
228
+ };
223
229
 
224
- /**
225
- * Return the unsigned jwt for nonce proof of possession
226
- *
227
- * @function
228
- * @param nonce the nonce
229
- *
230
- * @returns Unsigned JWT for nonce proof
231
- *
232
- */
233
- async getUnsignedNonceProof(nonce: string): Promise<string> {
234
- const unsignedProof = new SignJWT({
230
+ /**
231
+ * Return the signed jwt for nonce proof of possession
232
+ */
233
+ const createNonceProof = async (
234
+ nonce: string,
235
+ issuer: string,
236
+ audience: string,
237
+ ctx: CryptoContext
238
+ ): Promise<string> => {
239
+ return new SignJWT(ctx)
240
+ .setPayload({
235
241
  nonce,
242
+ jwk: await ctx.getPublicKey(),
236
243
  })
237
- .setProtectedHeader({
238
- alg: "ES256",
239
- type: "openid4vci-proof+jwt",
240
- })
241
- .setAudience(this.walletProviderBaseUrl)
242
- .setIssuer(this.clientId)
243
- .setIssuedAt()
244
- .setExpirationTime("1h")
245
- .toSign();
246
- return unsignedProof;
247
- }
244
+ .setProtectedHeader({
245
+ type: "openid4vci-proof+jwt",
246
+ })
247
+ .setAudience(audience)
248
+ .setIssuer(issuer)
249
+ .setIssuedAt()
250
+ .setExpirationTime("1h")
251
+ .sign();
252
+ };
248
253
 
249
- /**
250
- * Make the credential issuing request to the PID issuer
251
- *
252
- * @function
253
- * @param unsignedDPopForPid The unsigned JWT for PID DPoP
254
- * @param dPopPidSignature The JWT for PID DPoP signature
255
- * @param unsignedNonceProof The unsigned JWT for nonce proof
256
- * @param nonceProofSignature The JWT for nonce proof signature
257
- * @param accessToken The access token obtained with getAuthToken
258
- * @param cieData Personal data read by the CIE
259
- *
260
- * @returns a credential
261
- *
262
- */
263
- async getCredential(
264
- unsignedDPopForPid: string,
265
- dPopPidSignature: string,
266
- unsignedNonceProof: string,
267
- nonceProofSignature: string,
268
- accessToken: string,
254
+ /**
255
+ * Complete the issuing flow and get the PID credential.
256
+ *
257
+ * @param params.pidCryptoContext The key pair associated with the PID. Will be use to prove the ownership of the credential.
258
+ * @param params.appFetch (optional) Http client
259
+ * @param authConf The authorization configuration retrieved with the access token
260
+ * @param cieData Data red from the CIE login process
261
+ * @returns The PID credential token
262
+ */
263
+ export const getCredential =
264
+ ({
265
+ pidCryptoContext,
266
+ appFetch = fetch,
267
+ }: {
268
+ pidCryptoContext: CryptoContext;
269
+ appFetch?: GlobalFetch["fetch"];
270
+ }) =>
271
+ async (
272
+ { nonce, accessToken, clientId, walletProviderBaseUrl }: AuthorizationConf,
273
+ pidProviderEntityConfiguration: CredentialIssuerEntityConfiguration,
269
274
  cieData: CieData
270
- ): Promise<PidResponse> {
271
- const signedDPopForPid = await SignJWT.appendSignature(
272
- unsignedDPopForPid,
273
- dPopPidSignature
275
+ ): Promise<PidResponse> => {
276
+ const signedDPopForPid = await createDPopToken(
277
+ {
278
+ htm: "POST",
279
+ htu: pidProviderEntityConfiguration.payload.metadata
280
+ .openid_credential_issuer.token_endpoint,
281
+ jti: `${uuid.v4()}`,
282
+ },
283
+ pidCryptoContext
274
284
  );
275
- const signedNonceProof = await SignJWT.appendSignature(
276
- unsignedNonceProof,
277
- nonceProofSignature
285
+ const signedNonceProof = await createNonceProof(
286
+ nonce,
287
+ clientId,
288
+ walletProviderBaseUrl,
289
+ pidCryptoContext
278
290
  );
279
- const credentialUrl = new URL("/credential", this.pidProviderBaseUrl).href;
291
+
292
+ const credentialUrl =
293
+ pidProviderEntityConfiguration.payload.metadata.openid_credential_issuer
294
+ .credential_endpoint;
280
295
 
281
296
  const requestBody = {
282
- credential_definition: JSON.stringify({ type: ["eu.eudiw.pid.it"] }),
297
+ credential_definition: JSON.stringify({
298
+ type: ["PersonIdentificationData"],
299
+ }),
283
300
  format: "vc+sd-jwt",
284
301
  proof: JSON.stringify({
285
302
  jwt: signedNonceProof,
@@ -289,7 +306,7 @@ export class Issuing {
289
306
  };
290
307
  const formBody = new URLSearchParams(requestBody);
291
308
 
292
- const response = await this.appFetch(credentialUrl, {
309
+ const response = await appFetch(credentialUrl, {
293
310
  method: "POST",
294
311
  headers: {
295
312
  "Content-Type": "application/x-www-form-urlencoded",
@@ -300,44 +317,28 @@ export class Issuing {
300
317
  });
301
318
 
302
319
  if (response.status === 200) {
303
- return await response.json();
320
+ const pidResponse = (await response.json()) as PidResponse;
321
+ await validatePid(pidResponse.credential, pidCryptoContext);
322
+ return pidResponse;
304
323
  }
305
324
 
306
- throw new PidIssuingError(`Unable to obtain credential!`);
307
- }
308
-
309
- /**
310
- * Obtain the PID issuer metadata
311
- *
312
- * @function
313
- * @returns PID issuer metadata
314
- *
315
- */
316
- async getEntityConfiguration(): Promise<PidIssuerEntityConfiguration> {
317
- const metadataUrl = new URL(
318
- "ci/.well-known/openid-federation",
319
- this.pidProviderBaseUrl
320
- ).href;
321
-
322
- const response = await this.appFetch(metadataUrl);
325
+ throw new PidIssuingError(
326
+ `Unable to obtain credential! url=${credentialUrl} status=${
327
+ response.status
328
+ } body=${await response.text()}`
329
+ );
330
+ };
323
331
 
324
- if (response.status === 200) {
325
- const jwtMetadata = await response.text();
326
- const { payload } = decodeJwt(jwtMetadata);
327
- const result = PidIssuerEntityConfiguration.safeParse(payload);
328
- if (result.success) {
329
- const parsedMetadata = result.data;
330
- await verifyJwt(jwtMetadata, parsedMetadata.jwks.keys);
331
- return parsedMetadata;
332
- } else {
333
- throw new PidMetadataError(result.error.message);
334
- }
335
- }
332
+ const validatePid = async (pidJwt: string, pidCryptoContext: CryptoContext) => {
333
+ const decoded = SdJwt.decode(pidJwt);
334
+ const pidKey = await pidCryptoContext.getPublicKey();
335
+ const holderBindedKey = decoded.sdJwt.payload.cnf.jwk;
336
336
 
337
- throw new PidMetadataError(
338
- `Unable to obtain PID metadata. Response: ${await response.text()} with status: ${
339
- response.status
340
- }`
337
+ if ((await thumbprint(pidKey)) !== (await thumbprint(holderBindedKey))) {
338
+ throw new PidIssuingError(
339
+ `The obtained pid does not seem to be valid according to your configuration. Your PID public key is: ${JSON.stringify(
340
+ pidKey
341
+ )} but PID holder binded key is: ${JSON.stringify(holderBindedKey)}`
341
342
  );
342
343
  }
343
- }
344
+ };
@@ -1,6 +1,6 @@
1
- import { RelyingPartySolution } from "..";
1
+ import { RelyingPartyEntityConfiguration } from "../../trust/types";
2
+ import * as RelyingPartySolution from "..";
2
3
  import { AuthRequestDecodeError } from "../../utils/errors";
3
- import { RpEntityConfiguration } from "../types";
4
4
 
5
5
  describe("decodeAuthRequestQR", () => {
6
6
  it("should return authentication request URL", async () => {
@@ -239,7 +239,7 @@ describe("RpEntityConfiguration", () => {
239
239
  ],
240
240
  },
241
241
  };
242
- const result = RpEntityConfiguration.safeParse(pp);
242
+ const result = RelyingPartyEntityConfiguration.safeParse(pp);
243
243
  if (result.success === false) {
244
244
  throw result.error;
245
245
  }