@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.
- 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);
|