@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.
- package/README.md +128 -19
- package/lib/commonjs/index.js +16 -23
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/pid/index.js +3 -8
- package/lib/commonjs/pid/index.js.map +1 -1
- package/lib/commonjs/pid/issuing.js +153 -176
- package/lib/commonjs/pid/issuing.js.map +1 -1
- package/lib/commonjs/rp/__test__/index.test.js +7 -5
- package/lib/commonjs/rp/__test__/index.test.js.map +1 -1
- package/lib/commonjs/rp/index.js +145 -155
- package/lib/commonjs/rp/index.js.map +1 -1
- package/lib/commonjs/rp/types.js +1 -21
- package/lib/commonjs/rp/types.js.map +1 -1
- package/lib/commonjs/trust/index.js +24 -5
- package/lib/commonjs/trust/index.js.map +1 -1
- package/lib/commonjs/trust/types.js +102 -9
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/utils/crypto.js +46 -0
- package/lib/commonjs/utils/crypto.js.map +1 -0
- package/lib/commonjs/utils/dpop.js +14 -7
- package/lib/commonjs/utils/dpop.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/index.js +3 -3
- package/lib/commonjs/wallet-instance-attestation/issuing.js +42 -60
- package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/module/index.js +4 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/pid/index.js +1 -1
- package/lib/module/pid/index.js.map +1 -1
- package/lib/module/pid/issuing.js +152 -180
- package/lib/module/pid/issuing.js.map +1 -1
- package/lib/module/rp/__test__/index.test.js +3 -3
- package/lib/module/rp/__test__/index.test.js.map +1 -1
- package/lib/module/rp/index.js +141 -154
- package/lib/module/rp/index.js.map +1 -1
- package/lib/module/rp/types.js +0 -20
- package/lib/module/rp/types.js.map +1 -1
- package/lib/module/trust/index.js +19 -5
- package/lib/module/trust/index.js.map +1 -1
- package/lib/module/trust/types.js +100 -7
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/utils/crypto.js +40 -0
- package/lib/module/utils/crypto.js.map +1 -0
- package/lib/module/utils/dpop.js +13 -5
- package/lib/module/utils/dpop.js.map +1 -1
- package/lib/module/wallet-instance-attestation/index.js +2 -2
- package/lib/module/wallet-instance-attestation/index.js.map +1 -1
- package/lib/module/wallet-instance-attestation/issuing.js +40 -58
- package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/typescript/index.d.ts +4 -6
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/pid/index.d.ts +1 -1
- package/lib/typescript/pid/index.d.ts.map +1 -1
- package/lib/typescript/pid/issuing.d.ts +43 -88
- package/lib/typescript/pid/issuing.d.ts.map +1 -1
- package/lib/typescript/rp/index.d.ts +41 -87
- package/lib/typescript/rp/index.d.ts.map +1 -1
- package/lib/typescript/rp/types.d.ts +10 -906
- package/lib/typescript/rp/types.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/index.d.ts +1 -1
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/trust/index.d.ts +806 -3
- package/lib/typescript/trust/index.d.ts.map +1 -1
- package/lib/typescript/trust/types.d.ts +9655 -297
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/utils/crypto.d.ts +10 -0
- package/lib/typescript/utils/crypto.d.ts.map +1 -0
- package/lib/typescript/utils/dpop.d.ts +10 -2
- package/lib/typescript/utils/dpop.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/index.d.ts +2 -2
- package/lib/typescript/wallet-instance-attestation/index.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/issuing.d.ts +18 -31
- package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/types.d.ts +4 -4
- package/package.json +2 -2
- package/src/index.ts +14 -13
- package/src/pid/index.ts +1 -1
- package/src/pid/issuing.ts +233 -232
- package/src/rp/__test__/index.test.ts +3 -3
- package/src/rp/index.ts +172 -194
- package/src/rp/types.ts +0 -24
- package/src/sd-jwt/index.ts +1 -1
- package/src/trust/index.ts +106 -5
- package/src/trust/types.ts +152 -34
- package/src/utils/crypto.ts +41 -0
- package/src/utils/dpop.ts +17 -7
- package/src/wallet-instance-attestation/index.ts +2 -2
- package/src/wallet-instance-attestation/issuing.ts +51 -63
- package/lib/commonjs/pid/metadata.js +0 -49
- package/lib/commonjs/pid/metadata.js.map +0 -1
- package/lib/module/pid/metadata.js +0 -41
- package/lib/module/pid/metadata.js.map +0 -1
- package/lib/typescript/pid/metadata.d.ts +0 -482
- package/lib/typescript/pid/metadata.d.ts.map +0 -1
- 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 {
|
20
|
+
import { createDPopToken } from "../utils/dpop";
|
21
|
+
import { RelyingPartyEntityConfiguration } from "../trust/types";
|
22
|
+
import * as WalletInstanceAttestation from "../wallet-instance-attestation";
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
115
|
-
): Promise<
|
116
|
-
const
|
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 ${
|
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 =
|
134
|
-
|
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
|
142
|
+
const requestObject = RequestObject.parse({
|
146
143
|
header: responseJwt.protectedHeader,
|
147
144
|
payload: responseJwt.payload,
|
148
145
|
});
|
149
146
|
|
150
|
-
return
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
//
|
179
|
+
// obtain issuer from Wallet Instance
|
180
|
+
const {
|
181
|
+
payload: { iss },
|
182
|
+
} = WalletInstanceAttestation.decode(walletInstanceAttestation);
|
188
183
|
|
189
|
-
const
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
201
|
-
|
190
|
+
kid: pidKid,
|
191
|
+
})
|
192
|
+
.setPayload({
|
193
|
+
vp: vp,
|
194
|
+
jti: `${uuid.v4()}`,
|
195
|
+
iss,
|
196
|
+
nonce: requestObject.payload.nonce,
|
202
197
|
})
|
203
|
-
.
|
198
|
+
.setAudience(requestObject.payload.response_uri)
|
199
|
+
.setIssuedAt()
|
200
|
+
.setExpirationTime("1h")
|
201
|
+
.sign();
|
204
202
|
|
205
|
-
const vc_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
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
async
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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 =
|
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:
|
255
|
+
state: requestObject.payload.state,
|
245
256
|
presentation_submission,
|
246
|
-
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
|
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(),
|
package/src/sd-jwt/index.ts
CHANGED
@@ -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 "
|
10
|
+
import type { JWK } from "../utils/jwk";
|
11
11
|
import {
|
12
12
|
ClaimsNotFoundBetweenDislosures,
|
13
13
|
ClaimsNotFoundInToken,
|
package/src/trust/index.ts
CHANGED
@@ -1,27 +1,82 @@
|
|
1
1
|
import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
|
2
|
-
import {
|
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
|
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
|
-
|
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
|
-
)
|
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
|
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);
|