@pagopa/io-react-native-wallet 0.28.2 → 0.30.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 +43 -0
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js +5 -0
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +12 -0
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/05-authorize-access.js +5 -0
- package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +13 -2
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +10 -0
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/presentation/01-start-flow.js +9 -8
- package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
- package/lib/commonjs/credential/presentation/03-get-request-object.js +3 -2
- package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
- package/lib/commonjs/credential/presentation/05-verify-request-object.js +57 -22
- package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +43 -16
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +16 -4
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js +85 -3
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/commonjs/credential/presentation/README.md +27 -9
- package/lib/commonjs/credential/presentation/errors.js +28 -23
- package/lib/commonjs/credential/presentation/errors.js.map +1 -1
- package/lib/commonjs/credential/presentation/index.js +6 -0
- package/lib/commonjs/credential/presentation/index.js.map +1 -1
- package/lib/commonjs/credential/presentation/types.js +14 -7
- package/lib/commonjs/credential/presentation/types.js.map +1 -1
- package/lib/commonjs/credential/status/02-status-attestation.js +2 -0
- package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -1
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +3 -0
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -1
- package/lib/commonjs/credential/trustmark/get-credential-trustmark.js +5 -0
- package/lib/commonjs/credential/trustmark/get-credential-trustmark.js.map +1 -1
- package/lib/commonjs/index.js +3 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/decoder.js +2 -0
- package/lib/commonjs/utils/decoder.js.map +1 -1
- package/lib/commonjs/utils/error-codes.js +9 -1
- package/lib/commonjs/utils/error-codes.js.map +1 -1
- package/lib/commonjs/utils/errors.js +31 -14
- package/lib/commonjs/utils/errors.js.map +1 -1
- package/lib/commonjs/utils/logging.js +68 -0
- package/lib/commonjs/utils/logging.js.map +1 -0
- package/lib/commonjs/utils/misc.js +2 -0
- package/lib/commonjs/utils/misc.js.map +1 -1
- package/lib/commonjs/utils/par.js +2 -0
- package/lib/commonjs/utils/par.js.map +1 -1
- package/lib/commonjs/wallet-instance/index.js +4 -0
- package/lib/commonjs/wallet-instance/index.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/issuing.js +5 -0
- package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/module/credential/issuance/03-start-user-authorization.js +5 -0
- package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/04-complete-user-authorization.js +12 -0
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/05-authorize-access.js +5 -0
- package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +13 -2
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +10 -0
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/presentation/01-start-flow.js +9 -8
- package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
- package/lib/module/credential/presentation/03-get-request-object.js +3 -2
- package/lib/module/credential/presentation/03-get-request-object.js.map +1 -1
- package/lib/module/credential/presentation/05-verify-request-object.js +58 -23
- package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js +44 -17
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +17 -5
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js +82 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/module/credential/presentation/README.md +27 -9
- package/lib/module/credential/presentation/errors.js +17 -19
- package/lib/module/credential/presentation/errors.js.map +1 -1
- package/lib/module/credential/presentation/index.js +2 -2
- package/lib/module/credential/presentation/index.js.map +1 -1
- package/lib/module/credential/presentation/types.js +12 -6
- package/lib/module/credential/presentation/types.js.map +1 -1
- package/lib/module/credential/status/02-status-attestation.js +2 -0
- package/lib/module/credential/status/02-status-attestation.js.map +1 -1
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +3 -0
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +1 -1
- package/lib/module/credential/trustmark/get-credential-trustmark.js +5 -0
- package/lib/module/credential/trustmark/get-credential-trustmark.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/decoder.js +2 -0
- package/lib/module/utils/decoder.js.map +1 -1
- package/lib/module/utils/error-codes.js +7 -0
- package/lib/module/utils/error-codes.js.map +1 -1
- package/lib/module/utils/errors.js +23 -14
- package/lib/module/utils/errors.js.map +1 -1
- package/lib/module/utils/logging.js +62 -0
- package/lib/module/utils/logging.js.map +1 -0
- package/lib/module/utils/misc.js +2 -0
- package/lib/module/utils/misc.js.map +1 -1
- package/lib/module/utils/par.js +2 -0
- package/lib/module/utils/par.js.map +1 -1
- package/lib/module/wallet-instance/index.js +4 -0
- package/lib/module/wallet-instance/index.js.map +1 -1
- package/lib/module/wallet-instance-attestation/issuing.js +5 -0
- package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +1 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/01-start-flow.d.ts +17 -14
- package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/03-get-request-object.d.ts +1 -3
- package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/05-verify-request-object.d.ts +2 -1
- package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +30 -2
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/errors.d.ts +17 -12
- package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/index.d.ts +3 -3
- package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +24 -17
- package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
- package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -1
- package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +1 -1
- package/lib/typescript/credential/trustmark/get-credential-trustmark.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/decoder.d.ts.map +1 -1
- package/lib/typescript/utils/error-codes.d.ts +8 -0
- package/lib/typescript/utils/error-codes.d.ts.map +1 -1
- package/lib/typescript/utils/errors.d.ts +32 -18
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/logging.d.ts +35 -0
- package/lib/typescript/utils/logging.d.ts.map +1 -0
- package/lib/typescript/utils/misc.d.ts.map +1 -1
- package/lib/typescript/utils/par.d.ts.map +1 -1
- package/lib/typescript/wallet-instance/index.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/03-start-user-authorization.ts +18 -0
- package/src/credential/issuance/04-complete-user-authorization.ts +51 -0
- package/src/credential/issuance/05-authorize-access.ts +16 -0
- package/src/credential/issuance/06-obtain-credential.ts +31 -2
- package/src/credential/issuance/07-verify-and-parse-credential.ts +27 -1
- package/src/credential/presentation/01-start-flow.ts +12 -11
- package/src/credential/presentation/03-get-request-object.ts +5 -5
- package/src/credential/presentation/05-verify-request-object.ts +73 -15
- package/src/credential/presentation/07-evaluate-dcql-query.ts +43 -18
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +25 -13
- package/src/credential/presentation/08-send-authorization-response.ts +110 -3
- package/src/credential/presentation/README.md +27 -9
- package/src/credential/presentation/errors.ts +24 -17
- package/src/credential/presentation/index.ts +4 -0
- package/src/credential/presentation/types.ts +22 -10
- package/src/credential/status/02-status-attestation.ts +3 -0
- package/src/credential/status/03-verify-and-parse-status-attestation.ts +10 -0
- package/src/credential/trustmark/get-credential-trustmark.ts +19 -0
- package/src/index.ts +2 -0
- package/src/utils/decoder.ts +5 -0
- package/src/utils/error-codes.ts +11 -0
- package/src/utils/errors.ts +59 -29
- package/src/utils/logging.ts +68 -0
- package/src/utils/misc.ts +5 -0
- package/src/utils/par.ts +6 -0
- package/src/wallet-instance/index.ts +17 -1
- package/src/wallet-instance-attestation/issuing.ts +19 -0
@@ -17,6 +17,7 @@ import {
|
|
17
17
|
import { CredentialResponse } from "./types";
|
18
18
|
import { createDPopToken } from "../../utils/dpop";
|
19
19
|
import { v4 as uuidv4 } from "uuid";
|
20
|
+
import { LogLevel, Logger } from "../../utils/logging";
|
20
21
|
|
21
22
|
export type ObtainCredential = (
|
22
23
|
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
@@ -27,7 +28,8 @@ export type ObtainCredential = (
|
|
27
28
|
dPopCryptoContext: CryptoContext;
|
28
29
|
credentialCryptoContext: CryptoContext;
|
29
30
|
appFetch?: GlobalFetch["fetch"];
|
30
|
-
}
|
31
|
+
},
|
32
|
+
operationType?: "reissuing"
|
31
33
|
) => Promise<CredentialResponse>;
|
32
34
|
|
33
35
|
export const createNonceProof = async (
|
@@ -73,7 +75,8 @@ export const obtainCredential: ObtainCredential = async (
|
|
73
75
|
accessToken,
|
74
76
|
clientId,
|
75
77
|
credentialDefinition,
|
76
|
-
context
|
78
|
+
context,
|
79
|
+
operationType
|
77
80
|
) => {
|
78
81
|
const {
|
79
82
|
credentialCryptoContext,
|
@@ -95,6 +98,8 @@ export const obtainCredential: ObtainCredential = async (
|
|
95
98
|
credentialCryptoContext
|
96
99
|
);
|
97
100
|
|
101
|
+
Logger.log(LogLevel.DEBUG, `Signed nonce proof: ${signedNonceProof}`);
|
102
|
+
|
98
103
|
// Validation of accessTokenResponse.authorization_details if contain credentialDefinition
|
99
104
|
const containsCredentialDefinition = accessToken.authorization_details.some(
|
100
105
|
(c) =>
|
@@ -105,6 +110,10 @@ export const obtainCredential: ObtainCredential = async (
|
|
105
110
|
);
|
106
111
|
|
107
112
|
if (!containsCredentialDefinition) {
|
113
|
+
Logger.log(
|
114
|
+
LogLevel.ERROR,
|
115
|
+
`Credential definition not found in the access token response ${accessToken.authorization_details}`
|
116
|
+
);
|
108
117
|
throw new ValidationFailed({
|
109
118
|
message:
|
110
119
|
"The access token response does not contain the requested credential",
|
@@ -123,6 +132,11 @@ export const obtainCredential: ObtainCredential = async (
|
|
123
132
|
},
|
124
133
|
};
|
125
134
|
|
135
|
+
Logger.log(
|
136
|
+
LogLevel.DEBUG,
|
137
|
+
`Credential request body: ${JSON.stringify(credentialRequestFormBody)}`
|
138
|
+
);
|
139
|
+
|
126
140
|
const tokenRequestSignedDPop = await createDPopToken(
|
127
141
|
{
|
128
142
|
htm: "POST",
|
@@ -132,12 +146,16 @@ export const obtainCredential: ObtainCredential = async (
|
|
132
146
|
},
|
133
147
|
dPopCryptoContext
|
134
148
|
);
|
149
|
+
|
150
|
+
Logger.log(LogLevel.DEBUG, `Token request DPoP: ${tokenRequestSignedDPop}`);
|
151
|
+
|
135
152
|
const credentialRes = await appFetch(credentialUrl, {
|
136
153
|
method: "POST",
|
137
154
|
headers: {
|
138
155
|
"Content-Type": "application/json",
|
139
156
|
DPoP: tokenRequestSignedDPop,
|
140
157
|
Authorization: `${accessToken.token_type} ${accessToken.access_token}`,
|
158
|
+
...(operationType === "reissuing" && { operationType }),
|
141
159
|
},
|
142
160
|
body: JSON.stringify(credentialRequestFormBody),
|
143
161
|
})
|
@@ -147,12 +165,21 @@ export const obtainCredential: ObtainCredential = async (
|
|
147
165
|
.catch(handleObtainCredentialError);
|
148
166
|
|
149
167
|
if (!credentialRes.success) {
|
168
|
+
Logger.log(
|
169
|
+
LogLevel.ERROR,
|
170
|
+
`Credential Response validation failed: ${credentialRes.error.message}`
|
171
|
+
);
|
150
172
|
throw new ValidationFailed({
|
151
173
|
message: "Credential Response validation failed",
|
152
174
|
reason: credentialRes.error.message,
|
153
175
|
});
|
154
176
|
}
|
155
177
|
|
178
|
+
Logger.log(
|
179
|
+
LogLevel.DEBUG,
|
180
|
+
`Credential Response: ${JSON.stringify(credentialRes.data)}`
|
181
|
+
);
|
182
|
+
|
156
183
|
return credentialRes.data;
|
157
184
|
};
|
158
185
|
|
@@ -163,6 +190,8 @@ export const obtainCredential: ObtainCredential = async (
|
|
163
190
|
* @throws {IssuerResponseError} with a specific code for more context
|
164
191
|
*/
|
165
192
|
const handleObtainCredentialError = (e: unknown) => {
|
193
|
+
Logger.log(LogLevel.ERROR, `Error occurred while obtaining credential: ${e}`);
|
194
|
+
|
166
195
|
if (!(e instanceof UnexpectedStatusCodeError)) {
|
167
196
|
throw e;
|
168
197
|
}
|
@@ -7,6 +7,7 @@ import { verify as verifySdJwt } from "../../sd-jwt";
|
|
7
7
|
import { getValueFromDisclosures } from "../../sd-jwt/converters";
|
8
8
|
import type { JWK } from "../../utils/jwk";
|
9
9
|
import type { ObtainCredential } from "./06-obtain-credential";
|
10
|
+
import { LogLevel, Logger } from "../../utils/logging";
|
10
11
|
|
11
12
|
export type VerifyAndParseCredential = (
|
12
13
|
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
@@ -62,10 +63,18 @@ const parseCredentialSdJwt = (
|
|
62
63
|
const credentialSubject = credentials_supported[sdJwt.payload.vct];
|
63
64
|
|
64
65
|
if (!credentialSubject) {
|
66
|
+
Logger.log(
|
67
|
+
LogLevel.ERROR,
|
68
|
+
`Credential type not supported by the issuer: ${sdJwt.payload.vct}`
|
69
|
+
);
|
65
70
|
throw new IoWalletError("Credential type not supported by the issuer");
|
66
71
|
}
|
67
72
|
|
68
73
|
if (credentialSubject.format !== sdJwt.header.typ) {
|
74
|
+
Logger.log(
|
75
|
+
LogLevel.ERROR,
|
76
|
+
`Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}'`
|
77
|
+
);
|
69
78
|
throw new IoWalletError(
|
70
79
|
`Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}', `
|
71
80
|
);
|
@@ -73,6 +82,7 @@ const parseCredentialSdJwt = (
|
|
73
82
|
|
74
83
|
// transfrom a record { key: value } in an iterable of pairs [key, value]
|
75
84
|
if (!credentialSubject.claims) {
|
85
|
+
Logger.log(LogLevel.ERROR, "Missing claims in the credential subject");
|
76
86
|
throw new IoWalletError("Missing claims in the credential subject"); // TODO [SIW-1268]: should not be optional
|
77
87
|
}
|
78
88
|
const attrDefinitions = Object.entries(credentialSubject.claims);
|
@@ -85,6 +95,10 @@ const parseCredentialSdJwt = (
|
|
85
95
|
const missing = attrsNotInDisclosures.map((_) => _[0 /* key */]).join(", ");
|
86
96
|
const received = disclosures.map((_) => _[1 /* name */]).join(", ");
|
87
97
|
if (!ignoreMissingAttributes) {
|
98
|
+
Logger.log(
|
99
|
+
LogLevel.ERROR,
|
100
|
+
`Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`
|
101
|
+
);
|
88
102
|
throw new IoWalletError(
|
89
103
|
`Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`
|
90
104
|
);
|
@@ -172,6 +186,10 @@ async function verifyCredentialSdJwt(
|
|
172
186
|
const { cnf } = decodedCredential.sdJwt.payload;
|
173
187
|
|
174
188
|
if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
|
189
|
+
Logger.log(
|
190
|
+
LogLevel.ERROR,
|
191
|
+
`Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`
|
192
|
+
);
|
175
193
|
throw new IoWalletError(
|
176
194
|
`Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`
|
177
195
|
);
|
@@ -204,15 +222,21 @@ const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
|
|
204
222
|
credentialCryptoContext
|
205
223
|
);
|
206
224
|
|
225
|
+
Logger.log(LogLevel.DEBUG, `Decoded credential: ${JSON.stringify(decoded)}`);
|
226
|
+
|
207
227
|
const parsedCredential = parseCredentialSdJwt(
|
208
228
|
issuerConf.openid_credential_issuer.credential_configurations_supported,
|
209
229
|
decoded,
|
210
230
|
ignoreMissingAttributes,
|
211
231
|
includeUndefinedAttributes
|
212
232
|
);
|
213
|
-
|
214
233
|
const maybeIssuedAt = getValueFromDisclosures(decoded.disclosures, "iat");
|
215
234
|
|
235
|
+
Logger.log(
|
236
|
+
LogLevel.DEBUG,
|
237
|
+
`Parsed credential: ${JSON.stringify(parsedCredential)}\nIssued at: ${maybeIssuedAt}`
|
238
|
+
);
|
239
|
+
|
216
240
|
return {
|
217
241
|
parsedCredential,
|
218
242
|
expiration: new Date(decoded.sdJwt.payload.exp * 1000),
|
@@ -243,6 +267,7 @@ export const verifyAndParseCredential: VerifyAndParseCredential = async (
|
|
243
267
|
context
|
244
268
|
) => {
|
245
269
|
if (format === "vc+sd-jwt") {
|
270
|
+
Logger.log(LogLevel.DEBUG, "Parsing credential in vc+sd-jwt format");
|
246
271
|
return verifyAndParseCredentialSdJwt(
|
247
272
|
issuerConf,
|
248
273
|
credential,
|
@@ -251,5 +276,6 @@ export const verifyAndParseCredential: VerifyAndParseCredential = async (
|
|
251
276
|
);
|
252
277
|
}
|
253
278
|
|
279
|
+
Logger.log(LogLevel.ERROR, `Unsupported credential format: ${format}`);
|
254
280
|
throw new IoWalletError(`Unsupported credential format: ${format}`);
|
255
281
|
};
|
@@ -2,9 +2,9 @@ import * as z from "zod";
|
|
2
2
|
import { InvalidQRCodeError } from "./errors";
|
3
3
|
|
4
4
|
const PresentationParams = z.object({
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
client_id: z.string().nonempty(),
|
6
|
+
request_uri: z.string().url(),
|
7
|
+
request_uri_method: z.enum(["get", "post"]),
|
8
8
|
state: z.string().optional(),
|
9
9
|
});
|
10
10
|
export type PresentationParams = z.infer<typeof PresentationParams>;
|
@@ -13,24 +13,25 @@ export type PresentationParams = z.infer<typeof PresentationParams>;
|
|
13
13
|
* The beginning of the presentation flow.
|
14
14
|
* To be implemented accordind to the user touchpoint
|
15
15
|
*
|
16
|
-
* @param params Presentation parameters, depending on the starting
|
16
|
+
* @param params Presentation parameters, depending on the starting touchpoint
|
17
17
|
* @returns The url for the Relying Party to connect with
|
18
18
|
*/
|
19
|
-
export type StartFlow = (
|
20
|
-
|
21
|
-
) => PresentationParams;
|
19
|
+
export type StartFlow = (params: {
|
20
|
+
[K in keyof PresentationParams]?: PresentationParams[K] | null;
|
21
|
+
}) => PresentationParams;
|
22
22
|
|
23
23
|
/**
|
24
|
-
* Start a presentation flow by
|
24
|
+
* Start a presentation flow by validating the required parameters.
|
25
|
+
* Parameters are extracted from a url encoded in a QR code or in a deep link.
|
25
26
|
*
|
26
|
-
* @param params The
|
27
|
+
* @param params The parameters to be validated
|
27
28
|
* @returns The url for the Relying Party to connect with
|
28
|
-
* @throws If the provided
|
29
|
+
* @throws If the provided parameters are not valid
|
29
30
|
*/
|
30
31
|
export const startFlowFromQR: StartFlow = (params) => {
|
31
32
|
const result = PresentationParams.safeParse({
|
32
33
|
...params,
|
33
|
-
|
34
|
+
request_uri_method: params.request_uri_method ?? "get",
|
34
35
|
});
|
35
36
|
|
36
37
|
if (result.success) {
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import {
|
2
|
-
import
|
1
|
+
import { RelyingPartyResponseError } from "../../utils/errors";
|
2
|
+
import { hasStatusOrThrow } from "../../utils/misc";
|
3
3
|
import { RequestObjectWalletCapabilities } from "./types";
|
4
4
|
|
5
5
|
export type GetRequestObject = (
|
6
|
-
requestUri:
|
6
|
+
requestUri: string,
|
7
7
|
context?: {
|
8
8
|
appFetch?: GlobalFetch["fetch"];
|
9
9
|
walletCapabilities?: RequestObjectWalletCapabilities;
|
@@ -41,7 +41,7 @@ export const getRequestObject: GetRequestObject = async (
|
|
41
41
|
},
|
42
42
|
body: formUrlEncodedBody.toString(),
|
43
43
|
})
|
44
|
-
.then(hasStatusOrThrow(200))
|
44
|
+
.then(hasStatusOrThrow(200, RelyingPartyResponseError))
|
45
45
|
.then((res) => res.text());
|
46
46
|
|
47
47
|
return {
|
@@ -52,7 +52,7 @@ export const getRequestObject: GetRequestObject = async (
|
|
52
52
|
const requestObjectEncodedJwt = await appFetch(requestUri, {
|
53
53
|
method: "GET",
|
54
54
|
})
|
55
|
-
.then(hasStatusOrThrow(200))
|
55
|
+
.then(hasStatusOrThrow(200, RelyingPartyResponseError))
|
56
56
|
.then((res) => res.text());
|
57
57
|
|
58
58
|
return {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { decode as decodeJwt, verify } from "@pagopa/io-react-native-jwt";
|
2
2
|
import type { RelyingPartyEntityConfiguration } from "../../trust";
|
3
|
-
import {
|
3
|
+
import { InvalidRequestObjectError } from "./errors";
|
4
4
|
import { RequestObject } from "./types";
|
5
5
|
import { getJwksFromConfig } from "./04-retrieve-rp-jwks";
|
6
6
|
|
@@ -15,39 +15,38 @@ export type VerifyRequestObject = (
|
|
15
15
|
) => Promise<{ requestObject: RequestObject }>;
|
16
16
|
|
17
17
|
/**
|
18
|
-
* Function to verify the Request Object's signature
|
18
|
+
* Function to verify the Request Object's validity, from the signature to the required properties.
|
19
19
|
* @param requestObjectEncodedJwt The Request Object in JWT format
|
20
20
|
* @param context.clientId The client ID to verify
|
21
21
|
* @param context.rpConf The Entity Configuration of the Relying Party
|
22
22
|
* @param context.state Optional state
|
23
23
|
* @returns The verified Request Object
|
24
|
+
* @throws {InvalidRequestObjectError} if the Request Object cannot be validated
|
24
25
|
*/
|
25
26
|
export const verifyRequestObject: VerifyRequestObject = async (
|
26
27
|
requestObjectEncodedJwt,
|
27
28
|
{ clientId, rpConf, rpSubject, state }
|
28
29
|
) => {
|
29
30
|
const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
|
30
|
-
const { keys } = getJwksFromConfig(rpConf);
|
31
31
|
|
32
|
-
|
33
|
-
const pubKey = keys?.find(
|
34
|
-
({ kid }) => kid === requestObjectJwt.protectedHeader.kid
|
35
|
-
);
|
32
|
+
const pubKey = getSigPublicKey(rpConf, requestObjectJwt.protectedHeader.kid);
|
36
33
|
|
37
|
-
|
38
|
-
|
34
|
+
try {
|
35
|
+
// Standard claims are verified within `verify`
|
36
|
+
await verify(requestObjectEncodedJwt, pubKey, { issuer: clientId });
|
37
|
+
} catch (_) {
|
38
|
+
throw new InvalidRequestObjectError(
|
39
|
+
"The Request Object signature verification failed"
|
40
|
+
);
|
39
41
|
}
|
40
42
|
|
41
|
-
|
42
|
-
await verify(requestObjectEncodedJwt, pubKey, { issuer: clientId });
|
43
|
-
|
44
|
-
const requestObject = RequestObject.parse(requestObjectJwt.payload);
|
43
|
+
const requestObject = validateRequestObjectShape(requestObjectJwt.payload);
|
45
44
|
|
46
45
|
const isClientIdMatch =
|
47
46
|
clientId === requestObject.client_id && clientId === rpSubject;
|
48
47
|
|
49
48
|
if (!isClientIdMatch) {
|
50
|
-
throw new
|
49
|
+
throw new InvalidRequestObjectError(
|
51
50
|
"Client ID does not match Request Object or Entity Configuration"
|
52
51
|
);
|
53
52
|
}
|
@@ -56,8 +55,67 @@ export const verifyRequestObject: VerifyRequestObject = async (
|
|
56
55
|
state && requestObject.state ? state === requestObject.state : true;
|
57
56
|
|
58
57
|
if (!isStateMatch) {
|
59
|
-
throw new
|
58
|
+
throw new InvalidRequestObjectError(
|
59
|
+
"The provided state does not match the Request Object's"
|
60
|
+
);
|
60
61
|
}
|
61
62
|
|
62
63
|
return { requestObject };
|
63
64
|
};
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Validate the shape of the Request Object to ensure all required properties are present and are of the expected type.
|
68
|
+
*
|
69
|
+
* @param payload The Request Object to validate
|
70
|
+
* @returns A valid Request Object
|
71
|
+
* @throws {InvalidRequestObjectError} when the Request Object cannot be parsed
|
72
|
+
*/
|
73
|
+
const validateRequestObjectShape = (payload: unknown): RequestObject => {
|
74
|
+
const requestObjectParse = RequestObject.safeParse(payload);
|
75
|
+
|
76
|
+
if (requestObjectParse.success) {
|
77
|
+
return requestObjectParse.data;
|
78
|
+
}
|
79
|
+
|
80
|
+
throw new InvalidRequestObjectError(
|
81
|
+
"The Request Object cannot be parsed successfully",
|
82
|
+
formatFlattenedZodErrors(requestObjectParse.error.flatten())
|
83
|
+
);
|
84
|
+
};
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Get the public key to verify the Request Object's signature from the Relying Party's EC.
|
88
|
+
*
|
89
|
+
* @param rpConf The Relying Party's EC
|
90
|
+
* @param kid The identifier of the key to find
|
91
|
+
* @returns The corresponding public key to verify the signature
|
92
|
+
* @throws {InvalidRequestObjectError} when the key cannot be found
|
93
|
+
*/
|
94
|
+
const getSigPublicKey = (
|
95
|
+
rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"],
|
96
|
+
kid: string | undefined
|
97
|
+
) => {
|
98
|
+
try {
|
99
|
+
const { keys } = getJwksFromConfig(rpConf);
|
100
|
+
|
101
|
+
const pubKey = keys.find((k) => k.kid === kid);
|
102
|
+
|
103
|
+
if (!pubKey) throw new Error();
|
104
|
+
|
105
|
+
return pubKey;
|
106
|
+
} catch (_) {
|
107
|
+
throw new InvalidRequestObjectError(
|
108
|
+
`The public key for signature verification (${kid}) cannot be found in the Entity Configuration`
|
109
|
+
);
|
110
|
+
}
|
111
|
+
};
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Utility to format flattened Zod errors into a simplified string `key1: key1_error, key2: key2_error`
|
115
|
+
*/
|
116
|
+
const formatFlattenedZodErrors = (
|
117
|
+
errors: Zod.typeToFlattenedError<RequestObject>
|
118
|
+
): string =>
|
119
|
+
Object.entries(errors.fieldErrors)
|
120
|
+
.map(([key, error]) => `${key}: ${error[0]}`)
|
121
|
+
.join(", ");
|
@@ -1,15 +1,10 @@
|
|
1
|
-
import {
|
2
|
-
DcqlQuery,
|
3
|
-
DcqlError,
|
4
|
-
DcqlCredentialSetError,
|
5
|
-
DcqlQueryResult,
|
6
|
-
} from "dcql";
|
1
|
+
import { DcqlQuery, DcqlError, DcqlQueryResult } from "dcql";
|
7
2
|
import { isValiError } from "valibot";
|
8
3
|
import { decode, prepareVpToken } from "../../sd-jwt";
|
9
4
|
import type { Disclosure } from "../../sd-jwt/types";
|
10
|
-
import { ValidationFailed } from "../../utils/errors";
|
11
5
|
import { createCryptoContextFor } from "../../utils/crypto";
|
12
6
|
import type { RemotePresentation } from "./types";
|
7
|
+
import { CredentialsNotFoundError, type NotFoundDetail } from "./errors";
|
13
8
|
|
14
9
|
/**
|
15
10
|
* The purpose for the credential request by the RP.
|
@@ -47,6 +42,11 @@ type DcqlMatchSuccess = Extract<
|
|
47
42
|
{ success: true }
|
48
43
|
>;
|
49
44
|
|
45
|
+
type DcqlMatchFailure = Extract<
|
46
|
+
DcqlQueryResult.CredentialMatch,
|
47
|
+
{ success: false }
|
48
|
+
>;
|
49
|
+
|
50
50
|
/**
|
51
51
|
* Convert a credential in JWT format to an object with claims
|
52
52
|
* for correct parsing by the `dcql` library.
|
@@ -81,6 +81,32 @@ const getDcqlQueryMatches = (result: DcqlQueryResult) =>
|
|
81
81
|
([, match]) => match.success === true
|
82
82
|
) as [string, DcqlMatchSuccess][];
|
83
83
|
|
84
|
+
/**
|
85
|
+
* Extract only failed matches from the DCQL query result.
|
86
|
+
*/
|
87
|
+
const getDcqlQueryFailedMatches = (result: DcqlQueryResult) =>
|
88
|
+
Object.entries(result.credential_matches).filter(
|
89
|
+
([, match]) => match.success === false
|
90
|
+
) as [string, DcqlMatchFailure][];
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Extract missing credentials from the DCQL query result.
|
94
|
+
* Note: here we are assuming a failed match is a missing credential,
|
95
|
+
* but there might be other reasons for its failure.
|
96
|
+
*/
|
97
|
+
const extractMissingCredentials = (
|
98
|
+
queryResult: DcqlQueryResult,
|
99
|
+
originalQuery: DcqlQuery
|
100
|
+
): NotFoundDetail[] => {
|
101
|
+
return getDcqlQueryFailedMatches(queryResult).map(([id]) => {
|
102
|
+
const credential = originalQuery.credentials.find((c) => c.id === id);
|
103
|
+
if (credential?.format !== "vc+sd-jwt") {
|
104
|
+
throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
|
105
|
+
}
|
106
|
+
return { id, vctValues: credential.meta?.vct_values };
|
107
|
+
});
|
108
|
+
};
|
109
|
+
|
84
110
|
export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
85
111
|
credentialsSdJwt,
|
86
112
|
query
|
@@ -97,8 +123,11 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
97
123
|
const queryResult = DcqlQuery.query(parsedQuery, credentials);
|
98
124
|
|
99
125
|
if (!queryResult.canBeSatisfied) {
|
100
|
-
throw new
|
126
|
+
throw new CredentialsNotFoundError(
|
127
|
+
extractMissingCredentials(queryResult, parsedQuery)
|
128
|
+
);
|
101
129
|
}
|
130
|
+
|
102
131
|
// Build an object vct:credentialJwt to map matched credentials to their JWT
|
103
132
|
const credentialsSdJwtByVct = credentials.reduce(
|
104
133
|
(acc, c, i) => ({ ...acc, [c.vct]: credentialsSdJwt[i]! }),
|
@@ -132,20 +161,16 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
132
161
|
};
|
133
162
|
});
|
134
163
|
} catch (error) {
|
135
|
-
// Invalid DCQL query structure
|
164
|
+
// Invalid DCQL query structure. Remap to `DcqlError` for consistency.
|
136
165
|
if (isValiError(error)) {
|
137
|
-
throw new
|
138
|
-
message: "
|
139
|
-
|
166
|
+
throw new DcqlError({
|
167
|
+
message: "Failed to parse the provided DCQL query",
|
168
|
+
code: "PARSE_ERROR",
|
169
|
+
cause: error.issues,
|
140
170
|
});
|
141
171
|
}
|
142
172
|
|
143
|
-
|
144
|
-
// TODO [SIW-2110]: handle invalid DQCL query or let the error propagate
|
145
|
-
}
|
146
|
-
if (error instanceof DcqlCredentialSetError) {
|
147
|
-
// TODO [SIW-2110]: handle missing credentials or let the error propagate
|
148
|
-
}
|
173
|
+
// Let other errors propagate so they can be caught with `err instanceof DcqlError`
|
149
174
|
throw error;
|
150
175
|
}
|
151
176
|
};
|
@@ -3,7 +3,7 @@ import { SdJwt4VC, type DisclosureWithEncoded } from "../../sd-jwt/types";
|
|
3
3
|
import { decode, prepareVpToken } from "../../sd-jwt";
|
4
4
|
import { createCryptoContextFor } from "../../utils/crypto";
|
5
5
|
import { JSONPath } from "jsonpath-plus";
|
6
|
-
import {
|
6
|
+
import { CredentialsNotFoundError, MissingDataError } from "./errors";
|
7
7
|
import Ajv from "ajv";
|
8
8
|
|
9
9
|
const ajv = new Ajv({ allErrors: true });
|
@@ -291,9 +291,12 @@ export const findCredentialSdJwt = (
|
|
291
291
|
}
|
292
292
|
}
|
293
293
|
|
294
|
-
throw new
|
295
|
-
|
296
|
-
|
294
|
+
throw new CredentialsNotFoundError([
|
295
|
+
{
|
296
|
+
id: "",
|
297
|
+
reason: "None of the vc+sd-jwt credentials satisfy the requirements.",
|
298
|
+
},
|
299
|
+
]);
|
297
300
|
};
|
298
301
|
|
299
302
|
/**
|
@@ -325,9 +328,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
325
328
|
inputDescriptors.map(async (descriptor) => {
|
326
329
|
if (descriptor.format?.["vc+sd-jwt"]) {
|
327
330
|
if (!decodedSdJwtCredentials.length) {
|
328
|
-
throw new
|
329
|
-
|
330
|
-
|
331
|
+
throw new CredentialsNotFoundError([
|
332
|
+
{
|
333
|
+
id: descriptor.id,
|
334
|
+
reason: "vc+sd-jwt credential is not supported.",
|
335
|
+
},
|
336
|
+
]);
|
331
337
|
}
|
332
338
|
|
333
339
|
const { matchedEvaluation, matchedKeyTag, matchedCredential } =
|
@@ -341,9 +347,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
341
347
|
};
|
342
348
|
}
|
343
349
|
|
344
|
-
throw new
|
345
|
-
|
346
|
-
|
350
|
+
throw new CredentialsNotFoundError([
|
351
|
+
{
|
352
|
+
id: descriptor.id,
|
353
|
+
reason: `${descriptor.format} format is not supported.`,
|
354
|
+
},
|
355
|
+
]);
|
347
356
|
})
|
348
357
|
);
|
349
358
|
};
|
@@ -385,9 +394,12 @@ export const prepareLegacyRemotePresentations: PrepareLegacyRemotePresentations
|
|
385
394
|
};
|
386
395
|
}
|
387
396
|
|
388
|
-
throw new
|
389
|
-
|
390
|
-
|
397
|
+
throw new CredentialsNotFoundError([
|
398
|
+
{
|
399
|
+
id: descriptor.id,
|
400
|
+
reason: `${descriptor.format} format is not supported.`,
|
401
|
+
},
|
402
|
+
]);
|
391
403
|
})
|
392
404
|
);
|
393
405
|
};
|