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