@pagopa/io-react-native-wallet 2.0.0-next.3 → 2.0.0-next.5
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/lib/commonjs/credential/issuance/04-complete-user-authorization.js +75 -57
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +3 -3
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/README.md +45 -34
- package/lib/commonjs/credential/issuance/types.js +1 -0
- package/lib/commonjs/credential/issuance/types.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +6 -13
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +7 -8
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.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 → 02-status-assertion.js} +28 -22
- package/lib/commonjs/credential/status/02-status-assertion.js.map +1 -0
- package/lib/commonjs/credential/status/03-verify-and-parse-status-assertion.js +85 -0
- package/lib/commonjs/credential/status/03-verify-and-parse-status-assertion.js.map +1 -0
- package/lib/commonjs/credential/status/README.md +22 -20
- package/lib/commonjs/credential/status/index.js +6 -6
- package/lib/commonjs/credential/status/index.js.map +1 -1
- package/lib/commonjs/credential/status/types.js +48 -15
- package/lib/commonjs/credential/status/types.js.map +1 -1
- package/lib/commonjs/sd-jwt/index.js +6 -1
- package/lib/commonjs/sd-jwt/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/types.js +25 -9
- package/lib/commonjs/sd-jwt/types.js.map +1 -1
- package/lib/commonjs/utils/credentials.js +33 -0
- package/lib/commonjs/utils/credentials.js.map +1 -0
- package/lib/commonjs/utils/crypto.js +1 -7
- package/lib/commonjs/utils/crypto.js.map +1 -1
- package/lib/commonjs/utils/jwk.js +12 -0
- package/lib/commonjs/utils/jwk.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/types.js +1 -2
- package/lib/commonjs/wallet-instance-attestation/types.js.map +1 -1
- package/lib/module/credential/issuance/04-complete-user-authorization.js +76 -58
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +4 -4
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/issuance/README.md +45 -34
- package/lib/module/credential/issuance/types.js +1 -0
- package/lib/module/credential/issuance/types.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js +6 -13
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +7 -8
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.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 → 02-status-assertion.js} +28 -22
- package/lib/module/credential/status/02-status-assertion.js.map +1 -0
- package/lib/module/credential/status/03-verify-and-parse-status-assertion.js +78 -0
- package/lib/module/credential/status/03-verify-and-parse-status-assertion.js.map +1 -0
- package/lib/module/credential/status/README.md +22 -20
- package/lib/module/credential/status/index.js +3 -3
- package/lib/module/credential/status/index.js.map +1 -1
- package/lib/module/credential/status/types.js +43 -12
- package/lib/module/credential/status/types.js.map +1 -1
- package/lib/module/sd-jwt/index.js +6 -1
- package/lib/module/sd-jwt/index.js.map +1 -1
- package/lib/module/sd-jwt/types.js +25 -9
- package/lib/module/sd-jwt/types.js.map +1 -1
- package/lib/module/utils/credentials.js +26 -0
- package/lib/module/utils/credentials.js.map +1 -0
- package/lib/module/utils/crypto.js +2 -8
- package/lib/module/utils/crypto.js.map +1 -1
- package/lib/module/utils/jwk.js +11 -1
- package/lib/module/utils/jwk.js.map +1 -1
- package/lib/module/wallet-instance-attestation/types.js +1 -2
- package/lib/module/wallet-instance-attestation/types.js.map +1 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +7 -14
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +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/issuance/types.d.ts +3 -0
- package/lib/typescript/credential/issuance/types.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/01-start-flow.d.ts +2 -2
- package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts +4 -3
- 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 +9 -5
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +3 -4
- package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
- package/lib/typescript/credential/status/02-status-assertion.d.ts +23 -0
- package/lib/typescript/credential/status/02-status-assertion.d.ts.map +1 -0
- package/lib/typescript/credential/status/03-verify-and-parse-status-assertion.d.ts +21 -0
- package/lib/typescript/credential/status/03-verify-and-parse-status-assertion.d.ts.map +1 -0
- package/lib/typescript/credential/status/index.d.ts +4 -4
- package/lib/typescript/credential/status/index.d.ts.map +1 -1
- package/lib/typescript/credential/status/types.d.ts +499 -22
- package/lib/typescript/credential/status/types.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/index.d.ts +68 -40
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/types.d.ts +97 -46
- package/lib/typescript/sd-jwt/types.d.ts.map +1 -1
- package/lib/typescript/utils/credentials.d.ts +11 -0
- package/lib/typescript/utils/credentials.d.ts.map +1 -0
- package/lib/typescript/utils/crypto.d.ts.map +1 -1
- package/lib/typescript/utils/jwk.d.ts +7 -0
- package/lib/typescript/utils/jwk.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/04-complete-user-authorization.ts +79 -85
- package/src/credential/issuance/06-obtain-credential.ts +4 -1
- package/src/credential/issuance/07-verify-and-parse-credential.ts +4 -6
- package/src/credential/issuance/README.md +45 -34
- package/src/credential/issuance/types.ts +1 -0
- package/src/credential/presentation/07-evaluate-dcql-query.ts +16 -17
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +16 -13
- package/src/credential/presentation/types.ts +1 -2
- package/src/credential/status/{02-status-attestation.ts → 02-status-assertion.ts} +37 -28
- package/src/credential/status/03-verify-and-parse-status-assertion.ts +109 -0
- package/src/credential/status/README.md +22 -20
- package/src/credential/status/index.ts +7 -14
- package/src/credential/status/types.ts +62 -15
- package/src/sd-jwt/index.ts +5 -1
- package/src/sd-jwt/types.ts +24 -10
- package/src/utils/credentials.ts +29 -0
- package/src/utils/crypto.ts +12 -20
- package/src/utils/jwk.ts +15 -1
- package/src/wallet-instance-attestation/types.ts +1 -1
- package/lib/commonjs/credential/status/02-status-attestation.js.map +0 -1
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +0 -55
- package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +0 -1
- package/lib/module/credential/status/02-status-attestation.js.map +0 -1
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +0 -49
- package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +0 -1
- package/lib/typescript/credential/status/02-status-attestation.d.ts +0 -19
- package/lib/typescript/credential/status/02-status-attestation.d.ts.map +0 -1
- package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts +0 -24
- package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +0 -1
- package/src/credential/status/03-verify-and-parse-status-attestation.ts +0 -70
@@ -10,16 +10,16 @@ import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
|
|
10
10
|
import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
|
11
11
|
import {
|
12
12
|
decode,
|
13
|
-
encodeBase64,
|
14
13
|
SignJWT,
|
15
14
|
type CryptoContext,
|
16
15
|
} from "@pagopa/io-react-native-jwt";
|
17
|
-
import { RequestObject } from "../presentation/types";
|
18
|
-
import { v4 as uuidv4 } from "uuid";
|
16
|
+
import { type RemotePresentation, RequestObject } from "../presentation/types";
|
19
17
|
import { ResponseUriResultShape } from "./types";
|
20
18
|
import { getJwtFromFormPost } from "../../utils/decoder";
|
21
19
|
import { AuthorizationError, AuthorizationIdpError } from "./errors";
|
22
20
|
import { LogLevel, Logger } from "../../utils/logging";
|
21
|
+
import { Presentation } from "..";
|
22
|
+
import type { DcqlQuery } from "dcql";
|
23
23
|
|
24
24
|
/**
|
25
25
|
* The interface of the phase to complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
|
@@ -30,11 +30,10 @@ export type CompleteUserAuthorizationWithQueryMode = (
|
|
30
30
|
|
31
31
|
export type CompleteUserAuthorizationWithFormPostJwtMode = (
|
32
32
|
requestObject: Out<GetRequestedCredentialToBePresented>,
|
33
|
+
pid: string,
|
33
34
|
context: {
|
34
35
|
wiaCryptoContext: CryptoContext;
|
35
36
|
pidCryptoContext: CryptoContext;
|
36
|
-
pid: string;
|
37
|
-
walletInstanceAttestation: string;
|
38
37
|
appFetch?: GlobalFetch["fetch"];
|
39
38
|
}
|
40
39
|
) => Promise<AuthorizationResult>;
|
@@ -158,103 +157,54 @@ export const getRequestedCredentialToBePresented: GetRequestedCredentialToBePres
|
|
158
157
|
};
|
159
158
|
|
160
159
|
/**
|
161
|
-
* WARNING: This function must be called after {@link
|
160
|
+
* WARNING: This function must be called after {@link getRequestedCredentialToBePresented}. The next function to be called is {@link authorizeAccess}.
|
162
161
|
* The interface of the phase to complete User authorization via presentation of existing credentials when the response mode is "form_post.jwt".
|
163
|
-
*
|
164
|
-
*
|
165
|
-
* @param
|
166
|
-
* @param
|
167
|
-
* @param
|
168
|
-
* @param context.walletInstanceAccestation the Wallet Instance's attestation to be presented
|
169
|
-
* @param context.pid the PID to be presented
|
170
|
-
* @param context.wiaCryptoContext The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
|
171
|
-
* @param context.pidCryptoContext The PID crypto context associated with the pid parameter
|
172
|
-
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
162
|
+
* The information is obtained by performing a POST request to the endpoint received in the response_uri field of the requestObject, where the Authorization Response payload is posted.
|
163
|
+
* Following this,the redirect_uri from the response is used to obtain the final authorization response.
|
164
|
+
* @param requestObject - The request object containing the necessary parameters for authorization.
|
165
|
+
* @param pid The `PID` that must be presented for the issuance of credentials.
|
166
|
+
* @param appFetch (optional) fetch api implementation. Default: built-in fetch
|
173
167
|
* @throws {ValidationFailed} if an error while validating the response
|
174
168
|
* @returns the authorization response which contains code, state and iss
|
175
169
|
*/
|
176
170
|
export const completeUserAuthorizationWithFormPostJwtMode: CompleteUserAuthorizationWithFormPostJwtMode =
|
177
|
-
async (
|
171
|
+
async (
|
172
|
+
requestObject,
|
173
|
+
pid,
|
174
|
+
{ wiaCryptoContext, pidCryptoContext, appFetch = fetch }
|
175
|
+
) => {
|
178
176
|
Logger.log(
|
179
177
|
LogLevel.DEBUG,
|
180
178
|
`The requeste credential is not a PersonIdentificationData, completing the user authorization with form_post.jwt mode`
|
181
179
|
);
|
182
180
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
pid,
|
187
|
-
walletInstanceAttestation,
|
188
|
-
appFetch = fetch,
|
189
|
-
} = ctx;
|
181
|
+
if (!requestObject.dcql_query) {
|
182
|
+
throw new Error("Invalid request object");
|
183
|
+
}
|
190
184
|
|
191
|
-
const
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
})
|
196
|
-
.setPayload({
|
197
|
-
vp: walletInstanceAttestation,
|
198
|
-
jti: uuidv4().toString(),
|
199
|
-
nonce: requestObject.nonce,
|
200
|
-
})
|
201
|
-
.setIssuedAt()
|
202
|
-
.setExpirationTime("5m")
|
203
|
-
.setAudience(requestObject.response_uri)
|
204
|
-
.sign();
|
185
|
+
const dcqlQueryResult = Presentation.evaluateDcqlQuery(
|
186
|
+
[[pidCryptoContext, pid]],
|
187
|
+
requestObject.dcql_query as DcqlQuery
|
188
|
+
);
|
205
189
|
|
206
|
-
const
|
207
|
-
|
208
|
-
|
209
|
-
|
190
|
+
const credentialsToPresent = dcqlQueryResult.map(
|
191
|
+
({ requiredDisclosures, ...rest }) => ({
|
192
|
+
...rest,
|
193
|
+
requestedClaims: requiredDisclosures.map(([, claimName]) => claimName),
|
210
194
|
})
|
211
|
-
.setPayload({
|
212
|
-
vp: pid,
|
213
|
-
jti: uuidv4().toString(),
|
214
|
-
nonce: requestObject.nonce,
|
215
|
-
})
|
216
|
-
.setIssuedAt()
|
217
|
-
.setExpirationTime("5m")
|
218
|
-
.setAudience(requestObject.response_uri)
|
219
|
-
.sign();
|
220
|
-
|
221
|
-
Logger.log(
|
222
|
-
LogLevel.DEBUG,
|
223
|
-
`Wallet instance attestation JWT token: ${wiaWpToken}`
|
224
195
|
);
|
225
196
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
definition_id: `${uuidv4()}`,
|
231
|
-
id: `${uuidv4()}`,
|
232
|
-
descriptor_map: [
|
233
|
-
{
|
234
|
-
id: "PersonIdentificationData",
|
235
|
-
path: "$.vp_token[0].vp",
|
236
|
-
format: "vc+sd-jwt",
|
237
|
-
},
|
238
|
-
{
|
239
|
-
id: "WalletAttestation",
|
240
|
-
path: "$.vp_token[1].vp",
|
241
|
-
format: "jwt",
|
242
|
-
},
|
243
|
-
],
|
244
|
-
};
|
245
|
-
|
246
|
-
Logger.log(
|
247
|
-
LogLevel.DEBUG,
|
248
|
-
`Presentation submission: ${JSON.stringify(presentationSubmission)}`
|
197
|
+
const remotePresentations = await Presentation.prepareRemotePresentations(
|
198
|
+
credentialsToPresent,
|
199
|
+
requestObject.nonce,
|
200
|
+
requestObject.client_id
|
249
201
|
);
|
250
202
|
|
251
|
-
const authzResponsePayload =
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
})
|
257
|
-
);
|
203
|
+
const authzResponsePayload = await createAuthzResponsePayload({
|
204
|
+
state: requestObject.state,
|
205
|
+
remotePresentations,
|
206
|
+
wiaCryptoContext,
|
207
|
+
});
|
258
208
|
|
259
209
|
Logger.log(
|
260
210
|
LogLevel.DEBUG,
|
@@ -334,3 +284,47 @@ export const parseAuthorizationResponse = (
|
|
334
284
|
}
|
335
285
|
return authResParsed.data;
|
336
286
|
};
|
287
|
+
|
288
|
+
/**
|
289
|
+
* Creates the authorization response payload to be sent.
|
290
|
+
* This payload includes the state and the VP tokens for the presented credentials.
|
291
|
+
* The payload is encoded in Base64.
|
292
|
+
* @param state - The state parameter from the request object (optional).
|
293
|
+
* @param remotePresentations - An array of remote presentations containing credential IDs and their corresponding VP tokens.
|
294
|
+
* @returns The Base64 encoded authorization response payload.
|
295
|
+
*/
|
296
|
+
const createAuthzResponsePayload = async ({
|
297
|
+
state,
|
298
|
+
remotePresentations,
|
299
|
+
wiaCryptoContext,
|
300
|
+
}: {
|
301
|
+
state?: string;
|
302
|
+
remotePresentations: RemotePresentation[];
|
303
|
+
wiaCryptoContext: CryptoContext;
|
304
|
+
}): Promise<string> => {
|
305
|
+
const { kid } = await wiaCryptoContext.getPublicKey();
|
306
|
+
|
307
|
+
return new SignJWT(wiaCryptoContext)
|
308
|
+
.setProtectedHeader({
|
309
|
+
typ: "jwt",
|
310
|
+
kid,
|
311
|
+
})
|
312
|
+
.setPayload({
|
313
|
+
/**
|
314
|
+
* TODO [SIW-2264]: `state` coming from `requestObject` is marked as `optional`
|
315
|
+
* At the moment, it is not entirely clear whether this value can indeed be omitted
|
316
|
+
* and, if so, what the consequences of its absence might be.
|
317
|
+
*/
|
318
|
+
...(state ? { state } : {}),
|
319
|
+
vp_token: remotePresentations.reduce(
|
320
|
+
(vp_token, { credentialId, vpToken }) => ({
|
321
|
+
...vp_token,
|
322
|
+
[credentialId]: vpToken,
|
323
|
+
}),
|
324
|
+
{}
|
325
|
+
),
|
326
|
+
})
|
327
|
+
.setIssuedAt()
|
328
|
+
.setExpirationTime("1h")
|
329
|
+
.sign();
|
330
|
+
};
|
@@ -33,7 +33,10 @@ export type ObtainCredential = (
|
|
33
33
|
appFetch?: GlobalFetch["fetch"];
|
34
34
|
},
|
35
35
|
operationType?: "reissuing"
|
36
|
-
) => Promise<{
|
36
|
+
) => Promise<{
|
37
|
+
credential: string;
|
38
|
+
format: string;
|
39
|
+
}>;
|
37
40
|
|
38
41
|
export const createNonceProof = async (
|
39
42
|
nonce: string,
|
@@ -2,12 +2,11 @@ import type { CryptoContext } from "@pagopa/io-react-native-jwt";
|
|
2
2
|
import type { Out } from "../../utils/misc";
|
3
3
|
import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
|
4
4
|
import { IoWalletError } from "../../utils/errors";
|
5
|
-
import { SdJwt4VC } from "../../sd-jwt
|
6
|
-
import { verify as verifySdJwt } from "../../sd-jwt";
|
5
|
+
import { SdJwt4VC, verify as verifySdJwt } from "../../sd-jwt";
|
7
6
|
import { getValueFromDisclosures } from "../../sd-jwt/converters";
|
8
|
-
import type
|
7
|
+
import { isSameThumbprint, type JWK } from "../../utils/jwk";
|
9
8
|
import type { ObtainCredential } from "./06-obtain-credential";
|
10
|
-
import {
|
9
|
+
import { Logger, LogLevel } from "../../utils/logging";
|
11
10
|
|
12
11
|
type IssuerConf = Out<EvaluateIssuerTrust>["issuerConf"];
|
13
12
|
type CredentialConf =
|
@@ -169,8 +168,7 @@ async function verifyCredentialSdJwt(
|
|
169
168
|
]);
|
170
169
|
|
171
170
|
const { cnf } = decodedCredential.sdJwt.payload;
|
172
|
-
|
173
|
-
if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
|
171
|
+
if (!(await isSameThumbprint(cnf.jwk, holderBindingKey as JWK))) {
|
174
172
|
const message = `Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`;
|
175
173
|
Logger.log(LogLevel.ERROR, message);
|
176
174
|
throw new IoWalletError(message);
|
@@ -72,8 +72,6 @@ The expected result from the authentication process is in `form_post.jwt` format
|
|
72
72
|
<summary>Credential issuance flow</summary>
|
73
73
|
|
74
74
|
```ts
|
75
|
-
// TODO: [SIW-2209] update documentation in PR #219
|
76
|
-
|
77
75
|
// Retrieve the integrity key tag from the store and create its context
|
78
76
|
const integrityKeyTag = "example"; // Let's assume this is the key tag used to create the wallet instance
|
79
77
|
const integrityContext = getIntegrityContext(integrityKeyTag);
|
@@ -98,17 +96,13 @@ const walletInstanceAttestation =
|
|
98
96
|
appFetch,
|
99
97
|
});
|
100
98
|
|
101
|
-
const
|
102
|
-
|
103
|
-
const eid = {
|
99
|
+
const pid = {
|
104
100
|
credential: "example",
|
105
101
|
parsedCredential: "example"
|
106
102
|
keyTag: "example";
|
107
|
-
credentialType: "
|
103
|
+
credentialType: "PersonIdentificationData";
|
108
104
|
};
|
109
105
|
|
110
|
-
const eidCryptoContext = createCryptoContextFor(eid.keyTag);
|
111
|
-
|
112
106
|
// Create credential crypto context
|
113
107
|
const credentialKeyTag = uuidv4().toString();
|
114
108
|
await generate(credentialKeyTag); // Let's assume this function generates a new hardware-backed key pair
|
@@ -117,22 +111,26 @@ const credentialCryptoContext = createCryptoContextFor(credentialKeyTag);
|
|
117
111
|
// Start the issuance flow
|
118
112
|
const startFlow: Credential.Issuance.StartFlow = () => ({
|
119
113
|
issuerUrl: WALLET_EAA_PROVIDER_BASE_URL,
|
120
|
-
|
114
|
+
credentialId: "someCredentialId",
|
121
115
|
});
|
122
116
|
|
123
|
-
const { issuerUrl } = startFlow();
|
117
|
+
const { issuerUrl, credentialId } = startFlow();
|
124
118
|
|
125
119
|
// Evaluate issuer trust
|
126
120
|
const { issuerConf } = await Credential.Issuance.evaluateIssuerTrust(issuerUrl);
|
127
121
|
|
128
122
|
// Start user authorization
|
129
|
-
const { issuerRequestUri, clientId, codeVerifier
|
130
|
-
await Credential.Issuance.startUserAuthorization(
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
123
|
+
const { issuerRequestUri, clientId, codeVerifier } =
|
124
|
+
await Credential.Issuance.startUserAuthorization(
|
125
|
+
issuerConf,
|
126
|
+
[credentialId],
|
127
|
+
{
|
128
|
+
walletInstanceAttestation,
|
129
|
+
redirectUri: REDIRECT_URI,
|
130
|
+
wiaCryptoContext,
|
131
|
+
appFetch,
|
132
|
+
}
|
133
|
+
);
|
136
134
|
|
137
135
|
const requestObject =
|
138
136
|
await Credential.Issuance.getRequestedCredentialToBePresented(
|
@@ -142,13 +140,12 @@ const requestObject =
|
|
142
140
|
appFetch
|
143
141
|
);
|
144
142
|
|
145
|
-
// The app here should ask the user to confirm the required data contained in the requestObject
|
146
|
-
|
147
143
|
// Complete the user authorization via form_post.jwt mode
|
148
144
|
const { code } =
|
149
145
|
await Credential.Issuance.completeUserAuthorizationWithFormPostJwtMode(
|
150
146
|
requestObject,
|
151
|
-
|
147
|
+
pid.credential,
|
148
|
+
{ wiaCryptoContext, pidCryptoContext: createCryptoContextFor(pid.keyTag) }
|
152
149
|
);
|
153
150
|
|
154
151
|
// Generate the DPoP context which will be used for the whole issuance flow
|
@@ -159,7 +156,7 @@ const { accessToken } = await Credential.Issuance.authorizeAccess(
|
|
159
156
|
issuerConf,
|
160
157
|
code,
|
161
158
|
clientId,
|
162
|
-
redirectUri,
|
159
|
+
redirectUri: REDIRECT_URI,
|
163
160
|
codeVerifier,
|
164
161
|
{
|
165
162
|
walletInstanceAttestation,
|
@@ -169,12 +166,19 @@ const { accessToken } = await Credential.Issuance.authorizeAccess(
|
|
169
166
|
}
|
170
167
|
);
|
171
168
|
|
172
|
-
//
|
173
|
-
const {
|
169
|
+
// For simplicity, in this example flow we work on a single credential.
|
170
|
+
const { credential_configuration_id, credential_identifiers } =
|
171
|
+
accessToken.authorization_details[0]!;
|
172
|
+
|
173
|
+
// Obtain the credential
|
174
|
+
const { credential } = await Credential.Issuance.obtainCredential(
|
174
175
|
issuerConf,
|
175
176
|
accessToken,
|
176
177
|
clientId,
|
177
|
-
|
178
|
+
{
|
179
|
+
credential_configuration_id,
|
180
|
+
credential_identifier: credential_identifiers[0],
|
181
|
+
},
|
178
182
|
{
|
179
183
|
credentialCryptoContext,
|
180
184
|
dPopCryptoContext,
|
@@ -186,22 +190,29 @@ const { credential, format } = await Credential.Issuance.obtainCredential(
|
|
186
190
|
* Parse and verify the credential. The ignoreMissingAttributes flag must be set to false or omitted in production.
|
187
191
|
* WARNING: includeUndefinedAttributes should not be set to true in production in order to get only claims explicitly declared by the issuer.
|
188
192
|
*/
|
189
|
-
const { parsedCredential } =
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
193
|
+
const { parsedCredential } =
|
194
|
+
await Credential.Issuance.verifyAndParseCredential(
|
195
|
+
issuerConf,
|
196
|
+
credential,
|
197
|
+
credential_configuration_id,
|
198
|
+
{
|
199
|
+
credentialCryptoContext,
|
200
|
+
ignoreMissingAttributes: true,
|
201
|
+
includeUndefinedAttributes: false
|
202
|
+
}
|
203
|
+
);
|
204
|
+
|
205
|
+
const credentialType =
|
206
|
+
issuerConf.openid_credential_issuer.credential_configurations_supported[
|
207
|
+
credential_configuration_id
|
208
|
+
].scope;
|
199
209
|
|
200
210
|
return {
|
201
211
|
parsedCredential,
|
202
212
|
credential,
|
203
213
|
keyTag: credentialKeyTag,
|
204
214
|
credentialType,
|
215
|
+
credentialConfigurationId: credential_configuration_id,
|
205
216
|
};
|
206
217
|
```
|
207
218
|
|
@@ -11,6 +11,7 @@ export type TokenResponse = z.infer<typeof TokenResponse>;
|
|
11
11
|
|
12
12
|
export const TokenResponse = z.object({
|
13
13
|
access_token: z.string(),
|
14
|
+
refresh_token: z.string().optional(),
|
14
15
|
authorization_details: z.array(AuthorizationDetail),
|
15
16
|
expires_in: z.number(),
|
16
17
|
token_type: z.string(),
|
@@ -2,9 +2,9 @@ import { DcqlQuery, DcqlError, DcqlQueryResult } from "dcql";
|
|
2
2
|
import { isValiError } from "valibot";
|
3
3
|
import { decode, prepareVpToken } from "../../sd-jwt";
|
4
4
|
import type { Disclosure } from "../../sd-jwt/types";
|
5
|
-
import { createCryptoContextFor } from "../../utils/crypto";
|
6
5
|
import type { RemotePresentation } from "./types";
|
7
6
|
import { CredentialsNotFoundError, type NotFoundDetail } from "./errors";
|
7
|
+
import type { CryptoContext } from "@pagopa/io-react-native-jwt";
|
8
8
|
|
9
9
|
/**
|
10
10
|
* The purpose for the credential request by the RP.
|
@@ -15,13 +15,13 @@ type CredentialPurpose = {
|
|
15
15
|
};
|
16
16
|
|
17
17
|
export type EvaluateDcqlQuery = (
|
18
|
-
credentialsSdJwt: [
|
18
|
+
credentialsSdJwt: [CryptoContext, string /* credential */][],
|
19
19
|
query: DcqlQuery.Input
|
20
20
|
) => {
|
21
21
|
id: string;
|
22
22
|
vct: string;
|
23
23
|
credential: string;
|
24
|
-
|
24
|
+
cryptoContext: CryptoContext;
|
25
25
|
requiredDisclosures: Disclosure[];
|
26
26
|
purposes: CredentialPurpose[];
|
27
27
|
}[];
|
@@ -30,7 +30,7 @@ export type PrepareRemotePresentations = (
|
|
30
30
|
credentials: {
|
31
31
|
id: string;
|
32
32
|
credential: string;
|
33
|
-
|
33
|
+
cryptoContext: CryptoContext;
|
34
34
|
requestedClaims: string[];
|
35
35
|
}[],
|
36
36
|
nonce: string,
|
@@ -55,11 +55,6 @@ const mapCredentialToObject = (jwt: string) => {
|
|
55
55
|
const { sdJwt, disclosures } = decode(jwt);
|
56
56
|
const credentialFormat = sdJwt.header.typ;
|
57
57
|
|
58
|
-
// TODO [SIW-2082]: support MDOC credentials
|
59
|
-
if (credentialFormat !== "dc+sd-jwt") {
|
60
|
-
throw new Error(`Unsupported credential format: ${credentialFormat}`);
|
61
|
-
}
|
62
|
-
|
63
58
|
return {
|
64
59
|
vct: sdJwt.payload.vct,
|
65
60
|
credential_format: credentialFormat,
|
@@ -100,7 +95,10 @@ const extractMissingCredentials = (
|
|
100
95
|
): NotFoundDetail[] => {
|
101
96
|
return getDcqlQueryFailedMatches(queryResult).map(([id]) => {
|
102
97
|
const credential = originalQuery.credentials.find((c) => c.id === id);
|
103
|
-
if (
|
98
|
+
if (
|
99
|
+
credential?.format !== "dc+sd-jwt" &&
|
100
|
+
credential?.format !== "vc+sd-jwt"
|
101
|
+
) {
|
104
102
|
throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
|
105
103
|
}
|
106
104
|
return { id, vctValues: credential.meta?.vct_values };
|
@@ -114,7 +112,6 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
114
112
|
const credentials = credentialsSdJwt.map(([, credential]) =>
|
115
113
|
mapCredentialToObject(credential)
|
116
114
|
);
|
117
|
-
|
118
115
|
try {
|
119
116
|
// Validate the query
|
120
117
|
const parsedQuery = DcqlQuery.parse(query);
|
@@ -131,11 +128,14 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
131
128
|
// Build an object vct:credentialJwt to map matched credentials to their JWT
|
132
129
|
const credentialsSdJwtByVct = credentials.reduce(
|
133
130
|
(acc, c, i) => ({ ...acc, [c.vct]: credentialsSdJwt[i]! }),
|
134
|
-
{} as Record<string, [
|
131
|
+
{} as Record<string, [CryptoContext, string /* credential */]>
|
135
132
|
);
|
136
133
|
|
137
134
|
return getDcqlQueryMatches(queryResult).map(([id, match]) => {
|
138
|
-
if (
|
135
|
+
if (
|
136
|
+
match.output.credential_format !== "dc+sd-jwt" &&
|
137
|
+
match.output.credential_format !== "vc+sd-jwt"
|
138
|
+
) {
|
139
139
|
throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
|
140
140
|
}
|
141
141
|
const { vct, claims } = match.output;
|
@@ -147,12 +147,12 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
|
|
147
147
|
required: Boolean(credentialSet.required),
|
148
148
|
}));
|
149
149
|
|
150
|
-
const [
|
150
|
+
const [cryptoContext, credential] = credentialsSdJwtByVct[vct]!;
|
151
151
|
const requiredDisclosures = Object.values(claims) as Disclosure[];
|
152
152
|
return {
|
153
153
|
id,
|
154
154
|
vct,
|
155
|
-
|
155
|
+
cryptoContext,
|
156
156
|
credential,
|
157
157
|
requiredDisclosures,
|
158
158
|
// When it is a match but no credential_sets are found, the credential is required by default
|
@@ -185,14 +185,13 @@ export const prepareRemotePresentations: PrepareRemotePresentations = async (
|
|
185
185
|
const { vp_token } = await prepareVpToken(nonce, clientId, [
|
186
186
|
item.credential,
|
187
187
|
item.requestedClaims,
|
188
|
-
|
188
|
+
item.cryptoContext,
|
189
189
|
]);
|
190
190
|
|
191
191
|
return {
|
192
192
|
credentialId: item.id,
|
193
193
|
requestedClaims: item.requestedClaims,
|
194
194
|
vpToken: vp_token,
|
195
|
-
format: "dc+sd-jwt",
|
196
195
|
};
|
197
196
|
})
|
198
197
|
);
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { InputDescriptor, type LegacyRemotePresentation } from "./types";
|
2
2
|
import { SdJwt4VC, type DisclosureWithEncoded } from "../../sd-jwt/types";
|
3
3
|
import { decode, prepareVpToken } from "../../sd-jwt";
|
4
|
-
import { createCryptoContextFor } from "../../utils/crypto";
|
5
4
|
import { JSONPath } from "jsonpath-plus";
|
6
5
|
import { CredentialsNotFoundError, MissingDataError } from "./errors";
|
7
6
|
import Ajv from "ajv";
|
7
|
+
import type { CryptoContext } from "@pagopa/io-react-native-jwt";
|
8
8
|
|
9
9
|
const ajv = new Ajv({ allErrors: true });
|
10
10
|
const INDEX_CLAIM_NAME = 1;
|
@@ -23,13 +23,16 @@ export type EvaluateInputDescriptorSdJwt4VC = (
|
|
23
23
|
|
24
24
|
export type EvaluateInputDescriptors = (
|
25
25
|
descriptors: InputDescriptor[],
|
26
|
-
credentialsSdJwt: [
|
26
|
+
credentialsSdJwt: [
|
27
|
+
CryptoContext /* cryptoContext */,
|
28
|
+
string /* credential */,
|
29
|
+
][]
|
27
30
|
) => Promise<
|
28
31
|
{
|
29
32
|
evaluatedDisclosure: EvaluatedDisclosures;
|
30
33
|
inputDescriptor: InputDescriptor;
|
31
34
|
credential: string;
|
32
|
-
|
35
|
+
cryptoContext: CryptoContext;
|
33
36
|
}[]
|
34
37
|
>;
|
35
38
|
|
@@ -41,7 +44,7 @@ export type PrepareLegacyRemotePresentations = (
|
|
41
44
|
requestedClaims: string[];
|
42
45
|
inputDescriptor: InputDescriptor;
|
43
46
|
credential: string;
|
44
|
-
|
47
|
+
cryptoContext: CryptoContext;
|
45
48
|
}[],
|
46
49
|
nonce: string,
|
47
50
|
client_id: string
|
@@ -247,7 +250,7 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
|
|
247
250
|
};
|
248
251
|
|
249
252
|
type DecodedCredentialSdJwt = {
|
250
|
-
|
253
|
+
cryptoContext: CryptoContext;
|
251
254
|
credential: string;
|
252
255
|
sdJwt: SdJwt4VC;
|
253
256
|
disclosures: DisclosureWithEncoded[];
|
@@ -264,11 +267,11 @@ export const findCredentialSdJwt = (
|
|
264
267
|
decodedSdJwtCredentials: DecodedCredentialSdJwt[]
|
265
268
|
): {
|
266
269
|
matchedEvaluation: EvaluatedDisclosures;
|
267
|
-
matchedKeyTag: string;
|
268
270
|
matchedCredential: string;
|
271
|
+
cryptoContext: CryptoContext;
|
269
272
|
} => {
|
270
273
|
for (const {
|
271
|
-
|
274
|
+
cryptoContext,
|
272
275
|
credential,
|
273
276
|
sdJwt,
|
274
277
|
disclosures,
|
@@ -282,7 +285,7 @@ export const findCredentialSdJwt = (
|
|
282
285
|
|
283
286
|
return {
|
284
287
|
matchedEvaluation: evaluatedDisclosure,
|
285
|
-
|
288
|
+
cryptoContext,
|
286
289
|
matchedCredential: credential,
|
287
290
|
};
|
288
291
|
} catch {
|
@@ -319,9 +322,9 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
319
322
|
) => {
|
320
323
|
// We need decode SD-JWT credentials for evaluation
|
321
324
|
const decodedSdJwtCredentials =
|
322
|
-
credentialsSdJwt?.map(([
|
325
|
+
credentialsSdJwt?.map(([cryptoContext, credential]) => {
|
323
326
|
const { sdJwt, disclosures } = decode(credential);
|
324
|
-
return {
|
327
|
+
return { cryptoContext, credential, sdJwt, disclosures };
|
325
328
|
}) || [];
|
326
329
|
|
327
330
|
return Promise.all(
|
@@ -336,14 +339,14 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
|
336
339
|
]);
|
337
340
|
}
|
338
341
|
|
339
|
-
const { matchedEvaluation,
|
342
|
+
const { matchedEvaluation, cryptoContext, matchedCredential } =
|
340
343
|
findCredentialSdJwt(descriptor, decodedSdJwtCredentials);
|
341
344
|
|
342
345
|
return {
|
343
346
|
evaluatedDisclosure: matchedEvaluation,
|
344
347
|
inputDescriptor: descriptor,
|
345
348
|
credential: matchedCredential,
|
346
|
-
|
349
|
+
cryptoContext,
|
347
350
|
};
|
348
351
|
}
|
349
352
|
|
@@ -383,7 +386,7 @@ export const prepareLegacyRemotePresentations: PrepareLegacyRemotePresentations
|
|
383
386
|
const { vp_token } = await prepareVpToken(nonce, client_id, [
|
384
387
|
item.credential,
|
385
388
|
item.requestedClaims,
|
386
|
-
|
389
|
+
item.cryptoContext,
|
387
390
|
]);
|
388
391
|
|
389
392
|
return {
|
@@ -30,7 +30,6 @@ export type LegacyRemotePresentation = {
|
|
30
30
|
export type RemotePresentation = {
|
31
31
|
requestedClaims: string[];
|
32
32
|
credentialId: string;
|
33
|
-
format: string;
|
34
33
|
vpToken: string;
|
35
34
|
};
|
36
35
|
|
@@ -97,7 +96,7 @@ export const RequestObject = z.object({
|
|
97
96
|
state: z.string().optional(),
|
98
97
|
nonce: z.string(),
|
99
98
|
response_uri: z.string(),
|
100
|
-
|
99
|
+
request_uri_method: z.string().optional(),
|
101
100
|
response_type: z.literal("vp_token"),
|
102
101
|
response_mode: z.literal("direct_post.jwt"),
|
103
102
|
client_id: z.string(),
|