@pagopa/io-react-native-wallet 0.28.1 → 0.29.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 +17 -3
- 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 +14 -14
- package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +4 -2
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
- package/lib/commonjs/credential/presentation/03-get-request-object.js +2 -2
- package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
- package/lib/commonjs/credential/presentation/05-verify-request-object.js +11 -4
- package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +54 -14
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +26 -7
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js +4 -4
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/commonjs/credential/presentation/README.md +96 -2
- package/lib/commonjs/credential/presentation/errors.js +16 -19
- package/lib/commonjs/credential/presentation/errors.js.map +1 -1
- package/lib/commonjs/credential/presentation/index.js +27 -2
- package/lib/commonjs/credential/presentation/index.js.map +1 -1
- package/lib/commonjs/credential/presentation/types.js +1 -1
- 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/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 +17 -3
- 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 +14 -14
- package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js +4 -2
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
- package/lib/module/credential/presentation/03-get-request-object.js +2 -2
- package/lib/module/credential/presentation/03-get-request-object.js.map +1 -1
- package/lib/module/credential/presentation/05-verify-request-object.js +11 -4
- package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js +55 -14
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +25 -6
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js +4 -4
- package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/module/credential/presentation/README.md +96 -2
- package/lib/module/credential/presentation/errors.js +13 -16
- package/lib/module/credential/presentation/errors.js.map +1 -1
- package/lib/module/credential/presentation/index.js +4 -3
- package/lib/module/credential/presentation/index.js.map +1 -1
- package/lib/module/credential/presentation/types.js +1 -1
- 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/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 +2 -2
- 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 -19
- package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts +1 -0
- package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/03-get-request-object.d.ts +1 -4
- 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 +4 -2
- 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 +13 -5
- 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 +7 -2
- 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 +3 -3
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/errors.d.ts +14 -9
- package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/index.d.ts +5 -4
- package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +3 -3
- 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/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 +3 -3
- package/src/credential/issuance/03-start-user-authorization.ts +18 -0
- package/src/credential/issuance/04-complete-user-authorization.ts +57 -3
- 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 +18 -20
- package/src/credential/presentation/02-evaluate-rp-trust.ts +3 -2
- package/src/credential/presentation/03-get-request-object.ts +4 -6
- package/src/credential/presentation/05-verify-request-object.ts +17 -6
- package/src/credential/presentation/07-evaluate-dcql-query.ts +60 -17
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +53 -39
- package/src/credential/presentation/08-send-authorization-response.ts +9 -7
- package/src/credential/presentation/README.md +96 -2
- package/src/credential/presentation/errors.ts +21 -14
- package/src/credential/presentation/index.ts +22 -4
- package/src/credential/presentation/types.ts +1 -1
- 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/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
@@ -10,6 +10,7 @@ import { ASSERTION_TYPE } from "./const";
|
|
10
10
|
import { TokenResponse } from "./types";
|
11
11
|
import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
|
12
12
|
import type { CompleteUserAuthorizationWithQueryMode } from "./04-complete-user-authorization";
|
13
|
+
import { LogLevel, Logger } from "../../utils/logging";
|
13
14
|
|
14
15
|
export type AuthorizeAccess = (
|
15
16
|
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
@@ -76,6 +77,8 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
76
77
|
dPopCryptoContext
|
77
78
|
);
|
78
79
|
|
80
|
+
Logger.log(LogLevel.DEBUG, `Token request DPoP: ${tokenRequestSignedDPop}`);
|
81
|
+
|
79
82
|
const signedWiaPoP = await createPopToken(
|
80
83
|
{
|
81
84
|
jti: `${uuidv4()}`,
|
@@ -85,6 +88,8 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
85
88
|
wiaCryptoContext
|
86
89
|
);
|
87
90
|
|
91
|
+
Logger.log(LogLevel.DEBUG, `WIA DPoP token: ${signedWiaPoP}`);
|
92
|
+
|
88
93
|
const requestBody = {
|
89
94
|
grant_type: "authorization_code",
|
90
95
|
client_id: clientId,
|
@@ -96,6 +101,12 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
96
101
|
};
|
97
102
|
|
98
103
|
const authorizationRequestFormBody = new URLSearchParams(requestBody);
|
104
|
+
|
105
|
+
Logger.log(
|
106
|
+
LogLevel.DEBUG,
|
107
|
+
`Auth form request body: ${authorizationRequestFormBody}`
|
108
|
+
);
|
109
|
+
|
99
110
|
const tokenRes = await appFetch(tokenUrl, {
|
100
111
|
method: "POST",
|
101
112
|
headers: {
|
@@ -109,6 +120,11 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
109
120
|
.then((body) => TokenResponse.safeParse(body));
|
110
121
|
|
111
122
|
if (!tokenRes.success) {
|
123
|
+
Logger.log(
|
124
|
+
LogLevel.ERROR,
|
125
|
+
`Token Response validation failed: ${tokenRes.error.message}`
|
126
|
+
);
|
127
|
+
|
112
128
|
throw new ValidationFailed({
|
113
129
|
message: "Token Response validation failed",
|
114
130
|
reason: tokenRes.error.message,
|
@@ -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
|
};
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import * as z from "zod";
|
2
|
-
import {
|
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,32 +13,30 @@ 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 = (params:
|
20
|
-
|
21
|
-
|
22
|
-
requestUriMethod?: "get" | "post";
|
23
|
-
state?: string;
|
24
|
-
};
|
19
|
+
export type StartFlow = (params: {
|
20
|
+
[K in keyof PresentationParams]?: PresentationParams[K] | null;
|
21
|
+
}) => PresentationParams;
|
25
22
|
|
26
23
|
/**
|
27
|
-
* 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.
|
28
26
|
*
|
29
|
-
* @param params The
|
27
|
+
* @param params The parameters to be validated
|
30
28
|
* @returns The url for the Relying Party to connect with
|
31
|
-
* @throws If the provided
|
29
|
+
* @throws If the provided parameters are not valid
|
32
30
|
*/
|
33
31
|
export const startFlowFromQR: StartFlow = (params) => {
|
34
|
-
const result = PresentationParams.safeParse(
|
32
|
+
const result = PresentationParams.safeParse({
|
33
|
+
...params,
|
34
|
+
request_uri_method: params.request_uri_method ?? "get",
|
35
|
+
});
|
35
36
|
|
36
37
|
if (result.success) {
|
37
38
|
return result.data;
|
38
|
-
} else {
|
39
|
-
throw new ValidationFailed({
|
40
|
-
message: "Invalid parameters provided",
|
41
|
-
reason: result.error.message,
|
42
|
-
});
|
43
39
|
}
|
40
|
+
|
41
|
+
throw new InvalidQRCodeError(result.error.message);
|
44
42
|
};
|
@@ -10,6 +10,7 @@ export type EvaluateRelyingPartyTrust = (
|
|
10
10
|
}
|
11
11
|
) => Promise<{
|
12
12
|
rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
|
13
|
+
subject: string;
|
13
14
|
}>;
|
14
15
|
|
15
16
|
/**
|
@@ -25,9 +26,9 @@ export const evaluateRelyingPartyTrust: EvaluateRelyingPartyTrust = async (
|
|
25
26
|
{ appFetch = fetch } = {}
|
26
27
|
) => {
|
27
28
|
const {
|
28
|
-
payload: { metadata: rpConf },
|
29
|
+
payload: { metadata: rpConf, sub },
|
29
30
|
} = await getRelyingPartyEntityConfiguration(rpUrl, {
|
30
31
|
appFetch,
|
31
32
|
});
|
32
|
-
return { rpConf };
|
33
|
+
return { rpConf, subject: sub };
|
33
34
|
};
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import { hasStatusOrThrow
|
2
|
-
import type { StartFlow } from "./01-start-flow";
|
1
|
+
import { hasStatusOrThrow } from "../../utils/misc";
|
3
2
|
import { RequestObjectWalletCapabilities } from "./types";
|
4
3
|
|
5
4
|
export type GetRequestObject = (
|
6
|
-
requestUri:
|
7
|
-
context
|
5
|
+
requestUri: string,
|
6
|
+
context?: {
|
8
7
|
appFetch?: GlobalFetch["fetch"];
|
9
|
-
walletInstanceAttestation: string;
|
10
8
|
walletCapabilities?: RequestObjectWalletCapabilities;
|
11
9
|
}
|
12
10
|
) => Promise<{ requestObjectEncodedJwt: string }>;
|
@@ -23,7 +21,7 @@ export type GetRequestObject = (
|
|
23
21
|
*/
|
24
22
|
export const getRequestObject: GetRequestObject = async (
|
25
23
|
requestUri,
|
26
|
-
{ appFetch = fetch, walletCapabilities }
|
24
|
+
{ appFetch = fetch, walletCapabilities } = {}
|
27
25
|
) => {
|
28
26
|
if (walletCapabilities) {
|
29
27
|
// Validate external input
|
@@ -8,8 +8,9 @@ export type VerifyRequestObject = (
|
|
8
8
|
requestObjectEncodedJwt: string,
|
9
9
|
context: {
|
10
10
|
clientId: string;
|
11
|
-
|
12
|
-
|
11
|
+
rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
|
12
|
+
rpSubject: string;
|
13
|
+
state?: string;
|
13
14
|
}
|
14
15
|
) => Promise<{ requestObject: RequestObject }>;
|
15
16
|
|
@@ -17,16 +18,16 @@ export type VerifyRequestObject = (
|
|
17
18
|
* Function to verify the Request Object's signature and the client ID.
|
18
19
|
* @param requestObjectEncodedJwt The Request Object in JWT format
|
19
20
|
* @param context.clientId The client ID to verify
|
20
|
-
* @param context.jwkKeys The set of keys to verify the signature
|
21
21
|
* @param context.rpConf The Entity Configuration of the Relying Party
|
22
|
+
* @param context.state Optional state
|
22
23
|
* @returns The verified Request Object
|
23
24
|
*/
|
24
25
|
export const verifyRequestObject: VerifyRequestObject = async (
|
25
26
|
requestObjectEncodedJwt,
|
26
|
-
{ clientId, rpConf }
|
27
|
+
{ clientId, rpConf, rpSubject, state }
|
27
28
|
) => {
|
28
29
|
const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
|
29
|
-
const { keys } = getJwksFromConfig(rpConf
|
30
|
+
const { keys } = getJwksFromConfig(rpConf);
|
30
31
|
|
31
32
|
// Verify token signature to ensure the request object is authentic
|
32
33
|
const pubKey = keys?.find(
|
@@ -42,11 +43,21 @@ export const verifyRequestObject: VerifyRequestObject = async (
|
|
42
43
|
|
43
44
|
const requestObject = RequestObject.parse(requestObjectJwt.payload);
|
44
45
|
|
45
|
-
|
46
|
+
const isClientIdMatch =
|
47
|
+
clientId === requestObject.client_id && clientId === rpSubject;
|
48
|
+
|
49
|
+
if (!isClientIdMatch) {
|
46
50
|
throw new UnverifiedEntityError(
|
47
51
|
"Client ID does not match Request Object or Entity Configuration"
|
48
52
|
);
|
49
53
|
}
|
50
54
|
|
55
|
+
const isStateMatch =
|
56
|
+
state && requestObject.state ? state === requestObject.state : true;
|
57
|
+
|
58
|
+
if (!isStateMatch) {
|
59
|
+
throw new UnverifiedEntityError("State does not match Request Object");
|
60
|
+
}
|
61
|
+
|
51
62
|
return { requestObject };
|
52
63
|
};
|
@@ -6,23 +6,33 @@ import {
|
|
6
6
|
} from "dcql";
|
7
7
|
import { isValiError } from "valibot";
|
8
8
|
import { decode, prepareVpToken } from "../../sd-jwt";
|
9
|
-
import type { Disclosure
|
9
|
+
import type { Disclosure } from "../../sd-jwt/types";
|
10
10
|
import { ValidationFailed } from "../../utils/errors";
|
11
11
|
import { createCryptoContextFor } from "../../utils/crypto";
|
12
12
|
import type { RemotePresentation } from "./types";
|
13
|
+
import { CredentialsNotFoundError, type NotFoundDetail } from "./errors";
|
13
14
|
|
14
|
-
|
15
|
+
/**
|
16
|
+
* The purpose for the credential request by the RP.
|
17
|
+
*/
|
18
|
+
type CredentialPurpose = {
|
19
|
+
required: boolean;
|
20
|
+
description?: string;
|
21
|
+
};
|
22
|
+
|
23
|
+
export type EvaluateDcqlQuery = (
|
15
24
|
credentialsSdJwt: [string /* keyTag */, string /* credential */][],
|
16
25
|
query: DcqlQuery.Input
|
17
26
|
) => {
|
18
27
|
id: string;
|
28
|
+
vct: string;
|
19
29
|
credential: string;
|
20
30
|
keyTag: string;
|
21
|
-
requiredDisclosures:
|
22
|
-
|
31
|
+
requiredDisclosures: Disclosure[];
|
32
|
+
purposes: CredentialPurpose[];
|
23
33
|
}[];
|
24
34
|
|
25
|
-
type PrepareRemotePresentations = (
|
35
|
+
export type PrepareRemotePresentations = (
|
26
36
|
credentials: {
|
27
37
|
id: string;
|
28
38
|
credential: string;
|
@@ -38,6 +48,11 @@ type DcqlMatchSuccess = Extract<
|
|
38
48
|
{ success: true }
|
39
49
|
>;
|
40
50
|
|
51
|
+
type DcqlMatchFailure = Extract<
|
52
|
+
DcqlQueryResult.CredentialMatch,
|
53
|
+
{ success: false }
|
54
|
+
>;
|
55
|
+
|
41
56
|
/**
|
42
57
|
* Convert a credential in JWT format to an object with claims
|
43
58
|
* for correct parsing by the `dcql` library.
|
@@ -72,6 +87,32 @@ const getDcqlQueryMatches = (result: DcqlQueryResult) =>
|
|
72
87
|
([, match]) => match.success === true
|
73
88
|
) as [string, DcqlMatchSuccess][];
|
74
89
|
|
90
|
+
/**
|
91
|
+
* Extract only failed matches from the DCQL query result.
|
92
|
+
*/
|
93
|
+
const getDcqlQueryFailedMatches = (result: DcqlQueryResult) =>
|
94
|
+
Object.entries(result.credential_matches).filter(
|
95
|
+
([, match]) => match.success === false
|
96
|
+
) as [string, DcqlMatchFailure][];
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Extract missing credentials from the DCQL query result.
|
100
|
+
* Note: here we are assuming a failed match is a missing credential,
|
101
|
+
* but there might be other reasons for its failure.
|
102
|
+
*/
|
103
|
+
const extractMissingCredentials = (
|
104
|
+
queryResult: DcqlQueryResult,
|
105
|
+
originalQuery: DcqlQuery
|
106
|
+
): NotFoundDetail[] => {
|
107
|
+
return getDcqlQueryFailedMatches(queryResult).map(([id]) => {
|
108
|
+
const credential = originalQuery.credentials.find((c) => c.id === id);
|
109
|
+
if (credential?.format !== "vc+sd-jwt") {
|
110
|
+
throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
|
111
|
+
}
|
112
|
+
return { id, vctValues: credential.meta?.vct_values };
|
113
|
+
});
|
114
|
+
};
|
115
|
+
|
75
116
|
export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
76
117
|
credentialsSdJwt,
|
77
118
|
query
|
@@ -88,7 +129,9 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
88
129
|
const queryResult = DcqlQuery.query(parsedQuery, credentials);
|
89
130
|
|
90
131
|
if (!queryResult.canBeSatisfied) {
|
91
|
-
throw new
|
132
|
+
throw new CredentialsNotFoundError(
|
133
|
+
extractMissingCredentials(queryResult, parsedQuery)
|
134
|
+
);
|
92
135
|
}
|
93
136
|
|
94
137
|
// Build an object vct:credentialJwt to map matched credentials to their JWT
|
@@ -103,24 +146,24 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
103
146
|
}
|
104
147
|
const { vct, claims } = match.output;
|
105
148
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
const isOptional = credentialSet ? !credentialSet.required : false;
|
149
|
+
const purposes = queryResult.credential_sets
|
150
|
+
?.filter((set) => set.matching_options?.flat().includes(id))
|
151
|
+
?.map<CredentialPurpose>((credentialSet) => ({
|
152
|
+
description: credentialSet.purpose?.toString(),
|
153
|
+
required: Boolean(credentialSet.required),
|
154
|
+
}));
|
113
155
|
|
114
156
|
const [keyTag, credential] = credentialsSdJwtByVct[vct]!;
|
115
|
-
const requiredDisclosures = Object.values(
|
116
|
-
claims
|
117
|
-
) as DisclosureWithEncoded[];
|
157
|
+
const requiredDisclosures = Object.values(claims) as Disclosure[];
|
118
158
|
return {
|
119
159
|
id,
|
160
|
+
vct,
|
120
161
|
keyTag,
|
121
162
|
credential,
|
122
|
-
isOptional,
|
123
163
|
requiredDisclosures,
|
164
|
+
// When it is a match but no credential_sets are found, the credential is required by default
|
165
|
+
// See https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#section-6.3.1.2-2.1
|
166
|
+
purposes: purposes ?? [{ required: true }],
|
124
167
|
};
|
125
168
|
});
|
126
169
|
} catch (error) {
|
@@ -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 });
|
@@ -33,7 +33,10 @@ export type EvaluateInputDescriptors = (
|
|
33
33
|
}[]
|
34
34
|
>;
|
35
35
|
|
36
|
-
|
36
|
+
/**
|
37
|
+
* @deprecated Use `prepareRemotePresentations` from DCQL
|
38
|
+
*/
|
39
|
+
export type PrepareLegacyRemotePresentations = (
|
37
40
|
credentialAndDescriptors: {
|
38
41
|
requestedClaims: string[];
|
39
42
|
inputDescriptor: InputDescriptor;
|
@@ -288,9 +291,12 @@ export const findCredentialSdJwt = (
|
|
288
291
|
}
|
289
292
|
}
|
290
293
|
|
291
|
-
throw new
|
292
|
-
|
293
|
-
|
294
|
+
throw new CredentialsNotFoundError([
|
295
|
+
{
|
296
|
+
id: "",
|
297
|
+
reason: "None of the vc+sd-jwt credentials satisfy the requirements.",
|
298
|
+
},
|
299
|
+
]);
|
294
300
|
};
|
295
301
|
|
296
302
|
/**
|
@@ -322,9 +328,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
322
328
|
inputDescriptors.map(async (descriptor) => {
|
323
329
|
if (descriptor.format?.["vc+sd-jwt"]) {
|
324
330
|
if (!decodedSdJwtCredentials.length) {
|
325
|
-
throw new
|
326
|
-
|
327
|
-
|
331
|
+
throw new CredentialsNotFoundError([
|
332
|
+
{
|
333
|
+
id: descriptor.id,
|
334
|
+
reason: "vc+sd-jwt credential is not supported.",
|
335
|
+
},
|
336
|
+
]);
|
328
337
|
}
|
329
338
|
|
330
339
|
const { matchedEvaluation, matchedKeyTag, matchedCredential } =
|
@@ -338,9 +347,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
338
347
|
};
|
339
348
|
}
|
340
349
|
|
341
|
-
throw new
|
342
|
-
|
343
|
-
|
350
|
+
throw new CredentialsNotFoundError([
|
351
|
+
{
|
352
|
+
id: descriptor.id,
|
353
|
+
reason: `${descriptor.format} format is not supported.`,
|
354
|
+
},
|
355
|
+
]);
|
344
356
|
})
|
345
357
|
);
|
346
358
|
};
|
@@ -352,6 +364,8 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
352
364
|
* - Validates the credential format.
|
353
365
|
* - Generates a verifiable presentation token (vpToken) using the provided nonce and client identifier.
|
354
366
|
*
|
367
|
+
* @deprecated Use `prepareRemotePresentations` from DCQL
|
368
|
+
*
|
355
369
|
* @param credentialAndDescriptors - An array containing objects with requested claims,
|
356
370
|
* input descriptor, credential, and keyTag.
|
357
371
|
* @param nonce - A unique nonce for the verifiable presentation token.
|
@@ -359,33 +373,33 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
359
373
|
* @returns A promise that resolves to an array of RemotePresentation objects.
|
360
374
|
* @throws {CredentialNotFoundError} When the credential format is unsupported.
|
361
375
|
*/
|
362
|
-
export const
|
363
|
-
credentialAndDescriptors,
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
376
|
+
export const prepareLegacyRemotePresentations: PrepareLegacyRemotePresentations =
|
377
|
+
async (credentialAndDescriptors, nonce, client_id) => {
|
378
|
+
return Promise.all(
|
379
|
+
credentialAndDescriptors.map(async (item) => {
|
380
|
+
const descriptor = item.inputDescriptor;
|
381
|
+
|
382
|
+
if (descriptor.format?.["vc+sd-jwt"]) {
|
383
|
+
const { vp_token } = await prepareVpToken(nonce, client_id, [
|
384
|
+
item.credential,
|
385
|
+
item.requestedClaims,
|
386
|
+
createCryptoContextFor(item.keyTag),
|
387
|
+
]);
|
388
|
+
|
389
|
+
return {
|
390
|
+
requestedClaims: item.requestedClaims,
|
391
|
+
inputDescriptor: descriptor,
|
392
|
+
vpToken: vp_token,
|
393
|
+
format: "vc+sd-jwt",
|
394
|
+
};
|
395
|
+
}
|
370
396
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
397
|
+
throw new CredentialsNotFoundError([
|
398
|
+
{
|
399
|
+
id: descriptor.id,
|
400
|
+
reason: `${descriptor.format} format is not supported.`,
|
401
|
+
},
|
376
402
|
]);
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
inputDescriptor: descriptor,
|
381
|
-
vpToken: vp_token,
|
382
|
-
format: "vc+sd-jwt",
|
383
|
-
};
|
384
|
-
}
|
385
|
-
|
386
|
-
throw new CredentialNotFoundError(
|
387
|
-
`${descriptor.format} format is not supported.`
|
388
|
-
);
|
389
|
-
})
|
390
|
-
);
|
391
|
-
};
|
403
|
+
})
|
404
|
+
);
|
405
|
+
};
|