@pagopa/io-react-native-wallet 2.0.0-next.2 → 2.0.0-next.4
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/03-start-user-authorization.js +38 -24
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
- 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/05-authorize-access.js +6 -10
- package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +43 -11
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +51 -48
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/README.md +77 -45
- package/lib/commonjs/credential/issuance/const.js +1 -1
- package/lib/commonjs/credential/issuance/types.js +17 -10
- 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 +10 -11
- 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/README.md +0 -1
- package/lib/commonjs/sd-jwt/__test__/index.test.js +11 -15
- package/lib/commonjs/sd-jwt/__test__/index.test.js.map +1 -1
- package/lib/commonjs/sd-jwt/__test__/types.test.js +5 -2
- package/lib/commonjs/sd-jwt/__test__/types.test.js.map +1 -1
- package/lib/commonjs/sd-jwt/__test__/utils.test.js +37 -0
- package/lib/commonjs/sd-jwt/__test__/utils.test.js.map +1 -0
- package/lib/commonjs/sd-jwt/index.js +26 -1
- package/lib/commonjs/sd-jwt/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/types.js +71 -8
- package/lib/commonjs/sd-jwt/types.js.map +1 -1
- package/lib/commonjs/sd-jwt/utils.js +64 -0
- package/lib/commonjs/sd-jwt/utils.js.map +1 -0
- package/lib/commonjs/trust/types.js +18 -13
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/utils/par.js +32 -22
- package/lib/commonjs/utils/par.js.map +1 -1
- package/lib/commonjs/utils/pop.js +1 -1
- package/lib/commonjs/utils/pop.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/types.js +4 -1
- package/lib/commonjs/wallet-instance-attestation/types.js.map +1 -1
- package/lib/module/credential/issuance/03-start-user-authorization.js +38 -24
- package/lib/module/credential/issuance/03-start-user-authorization.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/05-authorize-access.js +6 -10
- package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +44 -12
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +51 -48
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/issuance/README.md +77 -45
- package/lib/module/credential/issuance/const.js +1 -1
- package/lib/module/credential/issuance/types.js +13 -8
- 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 +10 -11
- 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/README.md +0 -1
- package/lib/module/sd-jwt/__test__/index.test.js +11 -16
- package/lib/module/sd-jwt/__test__/index.test.js.map +1 -1
- package/lib/module/sd-jwt/__test__/types.test.js +5 -2
- package/lib/module/sd-jwt/__test__/types.test.js.map +1 -1
- package/lib/module/sd-jwt/__test__/utils.test.js +35 -0
- package/lib/module/sd-jwt/__test__/utils.test.js.map +1 -0
- package/lib/module/sd-jwt/index.js +7 -1
- package/lib/module/sd-jwt/index.js.map +1 -1
- package/lib/module/sd-jwt/types.js +70 -7
- package/lib/module/sd-jwt/types.js.map +1 -1
- package/lib/module/sd-jwt/utils.js +57 -0
- package/lib/module/sd-jwt/utils.js.map +1 -0
- package/lib/module/trust/types.js +18 -13
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/utils/par.js +29 -20
- package/lib/module/utils/par.js.map +1 -1
- package/lib/module/utils/pop.js +1 -1
- package/lib/module/utils/pop.js.map +1 -1
- package/lib/module/wallet-instance-attestation/types.js +4 -1
- package/lib/module/wallet-instance-attestation/types.js.map +1 -1
- package/lib/typescript/client/generated/wallet-provider.d.ts +12 -12
- package/lib/typescript/credential/issuance/01-start-flow.d.ts +2 -2
- package/lib/typescript/credential/issuance/01-start-flow.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +7 -6
- 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 +7 -14
- 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 +10 -5
- 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 +3 -2
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/const.d.ts +1 -1
- package/lib/typescript/credential/issuance/types.d.ts +49 -26
- 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/types.d.ts +4 -4
- package/lib/typescript/pid/sd-jwt/types.d.ts +7 -7
- package/lib/typescript/sd-jwt/__test__/utils.test.d.ts +2 -0
- package/lib/typescript/sd-jwt/__test__/utils.test.d.ts.map +1 -0
- package/lib/typescript/sd-jwt/index.d.ts +69 -28
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/types.d.ts +264 -31
- package/lib/typescript/sd-jwt/types.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/utils.d.ts +18 -0
- package/lib/typescript/sd-jwt/utils.d.ts.map +1 -0
- package/lib/typescript/trust/build-chain.d.ts +30 -14
- package/lib/typescript/trust/build-chain.d.ts.map +1 -1
- package/lib/typescript/trust/types.d.ts +322 -158
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/utils/par.d.ts +29 -13
- package/lib/typescript/utils/par.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/types.d.ts +9 -9
- package/lib/typescript/wallet-instance-attestation/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/01-start-flow.ts +2 -2
- package/src/credential/issuance/03-start-user-authorization.ts +57 -38
- package/src/credential/issuance/04-complete-user-authorization.ts +79 -85
- package/src/credential/issuance/05-authorize-access.ts +5 -11
- package/src/credential/issuance/06-obtain-credential.ts +56 -23
- package/src/credential/issuance/07-verify-and-parse-credential.ts +54 -62
- package/src/credential/issuance/README.md +77 -45
- package/src/credential/issuance/const.ts +1 -1
- package/src/credential/issuance/types.ts +19 -8
- package/src/credential/presentation/07-evaluate-dcql-query.ts +16 -17
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +19 -16
- package/src/credential/presentation/types.ts +1 -2
- package/src/credential/status/README.md +0 -1
- package/src/sd-jwt/__test__/index.test.ts +8 -29
- package/src/sd-jwt/__test__/types.test.ts +6 -2
- package/src/sd-jwt/__test__/utils.test.ts +37 -0
- package/src/sd-jwt/index.ts +7 -1
- package/src/sd-jwt/types.ts +68 -7
- package/src/sd-jwt/utils.ts +73 -0
- package/src/trust/types.ts +23 -17
- package/src/utils/par.ts +37 -21
- package/src/utils/pop.ts +1 -1
- package/src/wallet-instance-attestation/types.ts +3 -1
@@ -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
|
+
};
|
@@ -6,7 +6,6 @@ import { v4 as uuidv4 } from "uuid";
|
|
6
6
|
import { createPopToken } from "../../utils/pop";
|
7
7
|
import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
|
8
8
|
import type { CryptoContext } from "@pagopa/io-react-native-jwt";
|
9
|
-
import { ASSERTION_TYPE } from "./const";
|
10
9
|
import { TokenResponse } from "./types";
|
11
10
|
import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
|
12
11
|
import type { CompleteUserAuthorizationWithQueryMode } from "./04-complete-user-authorization";
|
@@ -47,7 +46,7 @@ export type AuthorizeAccess = (
|
|
47
46
|
export const authorizeAccess: AuthorizeAccess = async (
|
48
47
|
issuerConf,
|
49
48
|
code,
|
50
|
-
|
49
|
+
_,
|
51
50
|
redirectUri,
|
52
51
|
codeVerifier,
|
53
52
|
context
|
@@ -58,11 +57,7 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
58
57
|
wiaCryptoContext,
|
59
58
|
dPopCryptoContext,
|
60
59
|
} = context;
|
61
|
-
|
62
|
-
const parEndpoint =
|
63
|
-
issuerConf.oauth_authorization_server.pushed_authorization_request_endpoint;
|
64
|
-
const parUrl = new URL(parEndpoint);
|
65
|
-
const aud = `${parUrl.protocol}//${parUrl.hostname}`;
|
60
|
+
const aud = issuerConf.openid_credential_issuer.credential_issuer;
|
66
61
|
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation)
|
67
62
|
.payload.cnf.jwk.kid;
|
68
63
|
|
@@ -92,12 +87,9 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
92
87
|
|
93
88
|
const requestBody = {
|
94
89
|
grant_type: "authorization_code",
|
95
|
-
client_id: clientId,
|
96
90
|
code,
|
97
|
-
redirect_uri: redirectUri,
|
98
91
|
code_verifier: codeVerifier,
|
99
|
-
|
100
|
-
client_assertion: walletInstanceAttestation + "~" + signedWiaPoP,
|
92
|
+
redirect_uri: redirectUri,
|
101
93
|
};
|
102
94
|
|
103
95
|
const authorizationRequestFormBody = new URLSearchParams(requestBody);
|
@@ -112,6 +104,8 @@ export const authorizeAccess: AuthorizeAccess = async (
|
|
112
104
|
headers: {
|
113
105
|
"Content-Type": "application/x-www-form-urlencoded",
|
114
106
|
DPoP: tokenRequestSignedDPop,
|
107
|
+
"OAuth-Client-Attestation": walletInstanceAttestation,
|
108
|
+
"OAuth-Client-Attestation-PoP": signedWiaPoP,
|
115
109
|
},
|
116
110
|
body: authorizationRequestFormBody.toString(),
|
117
111
|
})
|
@@ -14,7 +14,7 @@ import {
|
|
14
14
|
UnexpectedStatusCodeError,
|
15
15
|
ValidationFailed,
|
16
16
|
} from "../../utils/errors";
|
17
|
-
import { CredentialResponse } from "./types";
|
17
|
+
import { CredentialResponse, NonceResponse } from "./types";
|
18
18
|
import { createDPopToken } from "../../utils/dpop";
|
19
19
|
import { v4 as uuidv4 } from "uuid";
|
20
20
|
import { LogLevel, Logger } from "../../utils/logging";
|
@@ -23,14 +23,20 @@ export type ObtainCredential = (
|
|
23
23
|
issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
|
24
24
|
accessToken: Out<AuthorizeAccess>["accessToken"],
|
25
25
|
clientId: Out<StartUserAuthorization>["clientId"],
|
26
|
-
credentialDefinition:
|
26
|
+
credentialDefinition: {
|
27
|
+
credential_configuration_id: string;
|
28
|
+
credential_identifier?: string;
|
29
|
+
},
|
27
30
|
context: {
|
28
31
|
dPopCryptoContext: CryptoContext;
|
29
32
|
credentialCryptoContext: CryptoContext;
|
30
33
|
appFetch?: GlobalFetch["fetch"];
|
31
34
|
},
|
32
35
|
operationType?: "reissuing"
|
33
|
-
) => Promise<
|
36
|
+
) => Promise<{
|
37
|
+
credential: string;
|
38
|
+
format: string;
|
39
|
+
}>;
|
34
40
|
|
35
41
|
export const createNonceProof = async (
|
36
42
|
nonce: string,
|
@@ -63,11 +69,11 @@ export const createNonceProof = async (
|
|
63
69
|
* @param issuerConf The issuer configuration returned by {@link evaluateIssuerTrust}
|
64
70
|
* @param accessToken The access token response returned by {@link authorizeAccess}
|
65
71
|
* @param clientId The client id returned by {@link startUserAuthorization}
|
66
|
-
* @param credentialDefinition The credential definition of the credential to be obtained returned by {@link
|
67
|
-
* @param tokenRequestSignedDPop The DPoP signed token request returned by {@link authorizeAccess}
|
72
|
+
* @param credentialDefinition The credential definition of the credential to be obtained returned by {@link authorizeAccess}
|
68
73
|
* @param context.credentialCryptoContext The crypto context used to obtain the credential
|
69
74
|
* @param context.dPopCryptoContext The DPoP crypto context
|
70
75
|
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
76
|
+
* @param operationType Specify the type of credential issuance (used for reissuing)
|
71
77
|
* @returns The credential response containing the credential
|
72
78
|
*/
|
73
79
|
export const obtainCredential: ObtainCredential = async (
|
@@ -83,8 +89,21 @@ export const obtainCredential: ObtainCredential = async (
|
|
83
89
|
appFetch = fetch,
|
84
90
|
dPopCryptoContext,
|
85
91
|
} = context;
|
92
|
+
const { credential_configuration_id, credential_identifier } =
|
93
|
+
credentialDefinition;
|
86
94
|
|
87
95
|
const credentialUrl = issuerConf.openid_credential_issuer.credential_endpoint;
|
96
|
+
const issuerUrl = issuerConf.oauth_authorization_server.issuer;
|
97
|
+
const nonceUrl = issuerConf.openid_credential_issuer.nonce_endpoint;
|
98
|
+
|
99
|
+
// Fetch the nonce from the Credential Issuer
|
100
|
+
const { c_nonce } = await appFetch(nonceUrl, {
|
101
|
+
method: "POST",
|
102
|
+
headers: { "Content-Type": "application/json" },
|
103
|
+
})
|
104
|
+
.then(hasStatusOrThrow(200))
|
105
|
+
.then((res) => res.json())
|
106
|
+
.then((body) => NonceResponse.parse(body));
|
88
107
|
|
89
108
|
/**
|
90
109
|
* JWT proof token to bind the request nonce to the key that will bind the holder User with the Credential
|
@@ -92,9 +111,9 @@ export const obtainCredential: ObtainCredential = async (
|
|
92
111
|
* @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-proof-types
|
93
112
|
*/
|
94
113
|
const signedNonceProof = await createNonceProof(
|
95
|
-
|
114
|
+
c_nonce,
|
96
115
|
clientId,
|
97
|
-
|
116
|
+
issuerUrl,
|
98
117
|
credentialCryptoContext
|
99
118
|
);
|
100
119
|
|
@@ -103,10 +122,10 @@ export const obtainCredential: ObtainCredential = async (
|
|
103
122
|
// Validation of accessTokenResponse.authorization_details if contain credentialDefinition
|
104
123
|
const containsCredentialDefinition = accessToken.authorization_details.some(
|
105
124
|
(c) =>
|
106
|
-
c.credential_configuration_id ===
|
107
|
-
|
108
|
-
|
109
|
-
|
125
|
+
c.credential_configuration_id === credential_configuration_id &&
|
126
|
+
(credential_identifier
|
127
|
+
? c.credential_identifiers.includes(credential_identifier)
|
128
|
+
: true)
|
110
129
|
);
|
111
130
|
|
112
131
|
if (!containsCredentialDefinition) {
|
@@ -120,17 +139,21 @@ export const obtainCredential: ObtainCredential = async (
|
|
120
139
|
});
|
121
140
|
}
|
122
141
|
|
123
|
-
/**
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
142
|
+
/**
|
143
|
+
* The credential request body.
|
144
|
+
* We accept both `credential_identifier` (recommended) and `credential_configuration_id`
|
145
|
+
* when the Authorization Server does not support `credential_identifier`.
|
146
|
+
* @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-15.html#section-3.3.4
|
147
|
+
*/
|
148
|
+
const credentialRequestFormBody = credential_identifier
|
149
|
+
? {
|
150
|
+
credential_identifier: credential_identifier,
|
151
|
+
proof: { jwt: signedNonceProof, proof_type: "jwt" },
|
152
|
+
}
|
153
|
+
: {
|
154
|
+
credential_configuration_id: credential_configuration_id,
|
155
|
+
proof: { jwt: signedNonceProof, proof_type: "jwt" },
|
156
|
+
};
|
134
157
|
|
135
158
|
Logger.log(
|
136
159
|
LogLevel.DEBUG,
|
@@ -180,7 +203,17 @@ export const obtainCredential: ObtainCredential = async (
|
|
180
203
|
`Credential Response: ${JSON.stringify(credentialRes.data)}`
|
181
204
|
);
|
182
205
|
|
183
|
-
|
206
|
+
// Extract the format corresponding to the credential_configuration_id used
|
207
|
+
const issuerCredentialConfig =
|
208
|
+
issuerConf.openid_credential_issuer.credential_configurations_supported[
|
209
|
+
credential_configuration_id
|
210
|
+
];
|
211
|
+
|
212
|
+
// TODO: [SIW-2264] Handle multiple credentials
|
213
|
+
return {
|
214
|
+
credential: credentialRes.data.credentials.at(0)!.credential,
|
215
|
+
format: issuerCredentialConfig!.format,
|
216
|
+
};
|
184
217
|
};
|
185
218
|
|
186
219
|
/**
|
@@ -9,10 +9,14 @@ import type { JWK } from "../../utils/jwk";
|
|
9
9
|
import type { ObtainCredential } from "./06-obtain-credential";
|
10
10
|
import { LogLevel, Logger } from "../../utils/logging";
|
11
11
|
|
12
|
+
type IssuerConf = Out<EvaluateIssuerTrust>["issuerConf"];
|
13
|
+
type CredentialConf =
|
14
|
+
IssuerConf["openid_credential_issuer"]["credential_configurations_supported"][string];
|
15
|
+
|
12
16
|
export type VerifyAndParseCredential = (
|
13
|
-
issuerConf:
|
17
|
+
issuerConf: IssuerConf,
|
14
18
|
credential: Out<ObtainCredential>["credential"],
|
15
|
-
|
19
|
+
credentialConfigurationId: string,
|
16
20
|
context: {
|
17
21
|
credentialCryptoContext: CryptoContext;
|
18
22
|
/**
|
@@ -54,54 +58,35 @@ type DecodedSdJwtCredential = Out<typeof verifySdJwt> & {
|
|
54
58
|
};
|
55
59
|
|
56
60
|
const parseCredentialSdJwt = (
|
57
|
-
//
|
58
|
-
|
61
|
+
// The credential configuration to use to parse the provided credential
|
62
|
+
credentialConfig: CredentialConf,
|
59
63
|
{ sdJwt, disclosures }: DecodedSdJwtCredential,
|
60
64
|
ignoreMissingAttributes: boolean = false,
|
61
65
|
includeUndefinedAttributes: boolean = false
|
62
66
|
): ParsedCredential => {
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
LogLevel.ERROR,
|
68
|
-
`Credential type not supported by the issuer: ${sdJwt.payload.vct}`
|
69
|
-
);
|
70
|
-
throw new IoWalletError("Credential type not supported by the issuer");
|
67
|
+
if (credentialConfig.format !== sdJwt.header.typ) {
|
68
|
+
const message = `Received credential is of an unknwown type. Expected one of [${credentialConfig.format}], received '${sdJwt.header.typ}'`;
|
69
|
+
Logger.log(LogLevel.ERROR, message);
|
70
|
+
throw new IoWalletError(message);
|
71
71
|
}
|
72
72
|
|
73
|
-
if (
|
74
|
-
Logger.log(
|
75
|
-
LogLevel.ERROR,
|
76
|
-
`Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}'`
|
77
|
-
);
|
78
|
-
throw new IoWalletError(
|
79
|
-
`Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}', `
|
80
|
-
);
|
81
|
-
}
|
82
|
-
|
83
|
-
// transfrom a record { key: value } in an iterable of pairs [key, value]
|
84
|
-
if (!credentialSubject.claims) {
|
73
|
+
if (!credentialConfig.claims) {
|
85
74
|
Logger.log(LogLevel.ERROR, "Missing claims in the credential subject");
|
86
75
|
throw new IoWalletError("Missing claims in the credential subject"); // TODO [SIW-1268]: should not be optional
|
87
76
|
}
|
88
|
-
const attrDefinitions =
|
77
|
+
const attrDefinitions = credentialConfig.claims;
|
89
78
|
|
90
79
|
// the key of the attribute defintion must match the disclosure's name
|
91
80
|
const attrsNotInDisclosures = attrDefinitions.filter(
|
92
|
-
(
|
81
|
+
(definition) => !disclosures.some(([, name]) => name === definition.path[0]) // Ignore nested paths for now, see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-15.html#name-claims-path-pointer
|
93
82
|
);
|
94
83
|
if (attrsNotInDisclosures.length > 0) {
|
95
|
-
const missing = attrsNotInDisclosures.map((_) => _[0
|
84
|
+
const missing = attrsNotInDisclosures.map((_) => _.path[0]).join(", ");
|
96
85
|
const received = disclosures.map((_) => _[1 /* name */]).join(", ");
|
97
86
|
if (!ignoreMissingAttributes) {
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
);
|
102
|
-
throw new IoWalletError(
|
103
|
-
`Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`
|
104
|
-
);
|
87
|
+
const message = `Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`;
|
88
|
+
Logger.log(LogLevel.ERROR, message);
|
89
|
+
throw new IoWalletError(message);
|
105
90
|
}
|
106
91
|
}
|
107
92
|
|
@@ -111,13 +96,13 @@ const parseCredentialSdJwt = (
|
|
111
96
|
attrDefinitions
|
112
97
|
// retrieve the value from the disclosure set
|
113
98
|
.map(
|
114
|
-
(
|
99
|
+
({ path, ...definition }) =>
|
115
100
|
[
|
116
|
-
|
101
|
+
path[0],
|
117
102
|
{
|
118
103
|
...definition,
|
119
104
|
value: disclosures.find(
|
120
|
-
(_) => _[1 /* name */] ===
|
105
|
+
(_) => _[1 /* name */] === path[0]
|
121
106
|
)?.[2 /* value */],
|
122
107
|
},
|
123
108
|
] as const
|
@@ -186,30 +171,18 @@ async function verifyCredentialSdJwt(
|
|
186
171
|
const { cnf } = decodedCredential.sdJwt.payload;
|
187
172
|
|
188
173
|
if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
);
|
193
|
-
throw new IoWalletError(
|
194
|
-
`Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`
|
195
|
-
);
|
174
|
+
const message = `Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`;
|
175
|
+
Logger.log(LogLevel.ERROR, message);
|
176
|
+
throw new IoWalletError(message);
|
196
177
|
}
|
197
178
|
|
198
179
|
return decodedCredential;
|
199
180
|
}
|
200
181
|
|
201
|
-
|
202
|
-
type WithFormat<Format extends Parameters<VerifyAndParseCredential>[2]> = (
|
203
|
-
_0: Parameters<VerifyAndParseCredential>[0],
|
204
|
-
_1: Parameters<VerifyAndParseCredential>[1],
|
205
|
-
_2: Format,
|
206
|
-
_3: Parameters<VerifyAndParseCredential>[3]
|
207
|
-
) => ReturnType<VerifyAndParseCredential>;
|
208
|
-
|
209
|
-
const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
|
182
|
+
const verifyAndParseCredentialSdJwt: VerifyAndParseCredential = async (
|
210
183
|
issuerConf,
|
211
184
|
credential,
|
212
|
-
|
185
|
+
credentialConfigurationId,
|
213
186
|
{
|
214
187
|
credentialCryptoContext,
|
215
188
|
ignoreMissingAttributes,
|
@@ -224,8 +197,21 @@ const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
|
|
224
197
|
|
225
198
|
Logger.log(LogLevel.DEBUG, `Decoded credential: ${JSON.stringify(decoded)}`);
|
226
199
|
|
200
|
+
const credentialConfig =
|
201
|
+
issuerConf.openid_credential_issuer.credential_configurations_supported[
|
202
|
+
credentialConfigurationId
|
203
|
+
];
|
204
|
+
|
205
|
+
if (!credentialConfig) {
|
206
|
+
Logger.log(
|
207
|
+
LogLevel.ERROR,
|
208
|
+
`Credential type not supported by the issuer: ${credentialConfigurationId}`
|
209
|
+
);
|
210
|
+
throw new IoWalletError("Credential type not supported by the issuer");
|
211
|
+
}
|
212
|
+
|
227
213
|
const parsedCredential = parseCredentialSdJwt(
|
228
|
-
|
214
|
+
credentialConfig,
|
229
215
|
decoded,
|
230
216
|
ignoreMissingAttributes,
|
231
217
|
includeUndefinedAttributes
|
@@ -251,7 +237,7 @@ const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
|
|
251
237
|
* Verify and parse an encoded credential.
|
252
238
|
* @param issuerConf The Issuer configuration returned by {@link evaluateIssuerTrust}
|
253
239
|
* @param credential The encoded credential returned by {@link obtainCredential}
|
254
|
-
* @param
|
240
|
+
* @param credentialConfigurationId The credential configuration ID that defines the provided credential
|
255
241
|
* @param context.credentialCryptoContext The crypto context used to obtain the credential in {@link obtainCredential}
|
256
242
|
* @param context.ignoreMissingAttributes Skip error when attributes declared in the issuer configuration are not found within disclosures
|
257
243
|
* @param context.includeUndefinedAttributes Include attributes not explicitly declared in the issuer configuration
|
@@ -263,19 +249,25 @@ const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
|
|
263
249
|
export const verifyAndParseCredential: VerifyAndParseCredential = async (
|
264
250
|
issuerConf,
|
265
251
|
credential,
|
266
|
-
|
252
|
+
credentialConfigurationId,
|
267
253
|
context
|
268
254
|
) => {
|
269
|
-
|
270
|
-
|
255
|
+
const format =
|
256
|
+
issuerConf.openid_credential_issuer.credential_configurations_supported[
|
257
|
+
credentialConfigurationId
|
258
|
+
]?.format;
|
259
|
+
|
260
|
+
if (format === "dc+sd-jwt") {
|
261
|
+
Logger.log(LogLevel.DEBUG, "Parsing credential in dc+sd-jwt format");
|
271
262
|
return verifyAndParseCredentialSdJwt(
|
272
263
|
issuerConf,
|
273
264
|
credential,
|
274
|
-
|
265
|
+
credentialConfigurationId,
|
275
266
|
context
|
276
267
|
);
|
277
268
|
}
|
278
269
|
|
279
|
-
|
280
|
-
|
270
|
+
const message = `Unsupported credential format: ${format}`;
|
271
|
+
Logger.log(LogLevel.ERROR, message);
|
272
|
+
throw new IoWalletError(message);
|
281
273
|
};
|