@pagopa/io-react-native-wallet 2.3.0 → 2.4.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/lib/commonjs/credential/issuance/03-start-user-authorization.js +24 -6
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +26 -2
- package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/README.md +155 -18
- package/lib/commonjs/credential/issuance/index.js +9 -1
- package/lib/commonjs/credential/issuance/index.js.map +1 -1
- package/lib/commonjs/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js +57 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js.map +1 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/02-init-challenge.js +61 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/02-init-challenge.js.map +1 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/03-validate-challenge.js +95 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/03-validate-challenge.js.map +1 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/README.md +92 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/index.js +33 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/index.js.map +1 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/types.js +57 -0
- package/lib/commonjs/credential/issuance/mrtd-pop/types.js.map +1 -0
- package/lib/commonjs/utils/auth.js +9 -1
- package/lib/commonjs/utils/auth.js.map +1 -1
- package/lib/commonjs/utils/par.js +7 -2
- package/lib/commonjs/utils/par.js.map +1 -1
- package/lib/module/credential/issuance/03-start-user-authorization.js +24 -6
- package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/04-complete-user-authorization.js +25 -2
- package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/README.md +155 -18
- package/lib/module/credential/issuance/index.js +3 -2
- package/lib/module/credential/issuance/index.js.map +1 -1
- package/lib/module/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js +50 -0
- package/lib/module/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.js.map +1 -0
- package/lib/module/credential/issuance/mrtd-pop/02-init-challenge.js +52 -0
- package/lib/module/credential/issuance/mrtd-pop/02-init-challenge.js.map +1 -0
- package/lib/module/credential/issuance/mrtd-pop/03-validate-challenge.js +85 -0
- package/lib/module/credential/issuance/mrtd-pop/03-validate-challenge.js.map +1 -0
- package/lib/module/credential/issuance/mrtd-pop/README.md +92 -0
- package/lib/module/credential/issuance/mrtd-pop/index.js +5 -0
- package/lib/module/credential/issuance/mrtd-pop/index.js.map +1 -0
- package/lib/module/credential/issuance/mrtd-pop/types.js +46 -0
- package/lib/module/credential/issuance/mrtd-pop/types.js.map +1 -0
- package/lib/module/utils/auth.js +8 -0
- package/lib/module/utils/auth.js.map +1 -1
- package/lib/module/utils/par.js +7 -2
- package/lib/module/utils/par.js.map +1 -1
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +14 -4
- 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 +9 -1
- package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/index.d.ts +4 -3
- package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.d.ts +25 -0
- package/lib/typescript/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/mrtd-pop/02-init-challenge.d.ts +23 -0
- package/lib/typescript/credential/issuance/mrtd-pop/02-init-challenge.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/mrtd-pop/03-validate-challenge.d.ts +39 -0
- package/lib/typescript/credential/issuance/mrtd-pop/03-validate-challenge.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/mrtd-pop/index.d.ts +7 -0
- package/lib/typescript/credential/issuance/mrtd-pop/index.d.ts.map +1 -0
- package/lib/typescript/credential/issuance/mrtd-pop/types.d.ts +201 -0
- package/lib/typescript/credential/issuance/mrtd-pop/types.d.ts.map +1 -0
- package/lib/typescript/utils/auth.d.ts +11 -0
- package/lib/typescript/utils/auth.d.ts.map +1 -1
- package/lib/typescript/utils/par.d.ts +34 -4
- package/lib/typescript/utils/par.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/03-start-user-authorization.ts +30 -7
- package/src/credential/issuance/04-complete-user-authorization.ts +43 -1
- package/src/credential/issuance/README.md +155 -18
- package/src/credential/issuance/index.ts +7 -1
- package/src/credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.ts +70 -0
- package/src/credential/issuance/mrtd-pop/02-init-challenge.ts +82 -0
- package/src/credential/issuance/mrtd-pop/03-validate-challenge.ts +140 -0
- package/src/credential/issuance/mrtd-pop/README.md +92 -0
- package/src/credential/issuance/mrtd-pop/index.ts +27 -0
- package/src/credential/issuance/mrtd-pop/types.ts +65 -0
- package/src/utils/auth.ts +12 -0
- package/src/utils/par.ts +12 -4
|
@@ -23,30 +23,45 @@ graph TD;
|
|
|
23
23
|
6[obtainCredential]
|
|
24
24
|
7[verifyAndParseCredential]
|
|
25
25
|
credSel{Is credential an eID?}
|
|
26
|
+
proofSel{Requires MRTD PoP?}
|
|
27
|
+
M1[continueUserAuthorizationWithMRTDPoPChallenge]
|
|
28
|
+
subgraph MRTD PoP flow
|
|
29
|
+
M2[verifyAndParseChallengeInfo]
|
|
30
|
+
M3[initChallenge]
|
|
31
|
+
M4[validateChallenge]
|
|
32
|
+
end
|
|
26
33
|
|
|
27
34
|
0 --> 1
|
|
28
35
|
1 --> 2
|
|
29
36
|
2 --> 3
|
|
30
37
|
3 --> credSel
|
|
31
|
-
credSel -->|Yes|
|
|
38
|
+
credSel -->|Yes| proofSel
|
|
32
39
|
credSel -->|No| C4
|
|
40
|
+
proofSel --> |Yes| M1
|
|
41
|
+
proofSel --> |No| E4
|
|
33
42
|
C4 --> C4.1
|
|
34
43
|
C4.1 --> 5
|
|
35
44
|
E4 --> 5
|
|
36
45
|
5 --> 6
|
|
37
46
|
6 --> 7
|
|
47
|
+
|
|
48
|
+
M1 --> M2
|
|
49
|
+
M2 --> M3
|
|
50
|
+
M3 --> M4
|
|
51
|
+
M4 --> E4
|
|
52
|
+
|
|
38
53
|
```
|
|
39
54
|
|
|
40
55
|
## Mapped results
|
|
41
56
|
|
|
42
57
|
The following errors are mapped to a `IssuerResponseError` with specific codes.
|
|
43
58
|
|
|
44
|
-
|HTTP Status|Error Code|Description|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
| HTTP Status | Error Code | Description |
|
|
60
|
+
| --------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `201 Created` | `ERR_CREDENTIAL_ISSUING_NOT_SYNCHRONOUS` | This response is returned by the credential issuer when the request has been queued because the credential cannot be issued synchronously. The consumer should try to obtain the credential at a later time. Although `201 Created` is not considered an error, it is mapped as an error in this context in order to handle the case where the credential issuance is not synchronous. This allows keeping the flow consistent and handle the case where the credential is not immediately available. |
|
|
62
|
+
| `403 Forbidden` | `ERR_CREDENTIAL_INVALID_STATUS` | This response is returned by the credential issuer when the requested credential has an invalid status. It might contain more details in the `reason` property. |
|
|
63
|
+
| `404 Not Found` | `ERR_CREDENTIAL_INVALID_STATUS` | This response is returned by the credential issuer when the authenticated user is not entitled to receive the requested credential. It might contain more details in the `reason` property. |
|
|
64
|
+
| `*` | `ERR_ISSUER_GENERIC_ERROR` | This is a generic error code to map unexpected errors that occurred when interacting with the Issuer. |
|
|
50
65
|
|
|
51
66
|
## Strong authentication for eID issuance (Query Mode)
|
|
52
67
|
|
|
@@ -60,6 +75,17 @@ CIE+PIN(L3) requires a different flow due to the physical card presence. Helper
|
|
|
60
75
|
|
|
61
76
|
The expected result from the authentication process is in provided in the query string as defined in the [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/oauth-v2-jarm.html#name-response-mode-queryjwt).
|
|
62
77
|
|
|
78
|
+
#### eID Substantial Authentication (L2+) with MRTD Verification
|
|
79
|
+
|
|
80
|
+
MRTD Verification is a sub-flow of the Issuance flow and is used when the requested eID requires **eID Substantial Authentication (LoA3) with MRTD (Machine Readable Travel Document) Verification**. This method provides an alternative to CIEid LoA High authentication, requiring two distinct steps to complete the authorization:
|
|
81
|
+
|
|
82
|
+
1. **Primary Authentication**: LoA3 electronic identification (SPID or CIEid L2).
|
|
83
|
+
2. **MRTD Proof of Possession (PoP)**: Electronic document reading and cryptographic verification.
|
|
84
|
+
|
|
85
|
+
This process is initiated by the Authorization Server responding to the primary authentication step with a redirect that includes a challenge in the query string, which is handled by the `continueUserAuthorizationWithMRTDPoPChallenge` function. Once the MRTD PoP is completed, the user must continue the PID issuance flow with the `completeUserAuthorizationWithQueryMode` function.
|
|
86
|
+
|
|
87
|
+
Complete documentation for the MRTD PoP flow can be found here: [mrtd-pop](./mrtd-pop/README.md)
|
|
88
|
+
|
|
63
89
|
## Authentication through credentials (Form Post JWT Mode)
|
|
64
90
|
|
|
65
91
|
When the credential is different than an eID, the flow requires the user to present other credentials in order to obtain the requested one. This is done through the `getRequestedCredentialToBePresented` followed by the `completeUserAuthorizationWithFormPostJwtMode`.
|
|
@@ -122,8 +148,9 @@ const { issuerConf } = await Credential.Issuance.evaluateIssuerTrust(issuerUrl);
|
|
|
122
148
|
// Start user authorization
|
|
123
149
|
const { issuerRequestUri, clientId, codeVerifier } =
|
|
124
150
|
await Credential.Issuance.startUserAuthorization(
|
|
125
|
-
issuerConf,
|
|
126
|
-
[credentialId],
|
|
151
|
+
issuerConf,
|
|
152
|
+
[credentialId],
|
|
153
|
+
{ proofType: "none" },
|
|
127
154
|
{
|
|
128
155
|
walletInstanceAttestation,
|
|
129
156
|
redirectUri: REDIRECT_URI,
|
|
@@ -199,10 +226,10 @@ const { parsedCredential } =
|
|
|
199
226
|
issuerConf,
|
|
200
227
|
credential,
|
|
201
228
|
credential_configuration_id,
|
|
202
|
-
{
|
|
203
|
-
credentialCryptoContext,
|
|
229
|
+
{
|
|
230
|
+
credentialCryptoContext,
|
|
204
231
|
ignoreMissingAttributes: true,
|
|
205
|
-
includeUndefinedAttributes: false
|
|
232
|
+
includeUndefinedAttributes: false
|
|
206
233
|
},
|
|
207
234
|
mockX509CertRoot
|
|
208
235
|
);
|
|
@@ -285,6 +312,7 @@ const { issuerRequestUri, clientId, codeVerifier, credentialDefinition } =
|
|
|
285
312
|
await Credential.Issuance.startUserAuthorization(
|
|
286
313
|
issuerConf,
|
|
287
314
|
[credentialId], // Request authorization for one or more credentials
|
|
315
|
+
{ proofType: "none" },
|
|
288
316
|
{
|
|
289
317
|
walletInstanceAttestation,
|
|
290
318
|
redirectUri,
|
|
@@ -296,12 +324,7 @@ const { issuerRequestUri, clientId, codeVerifier, credentialDefinition } =
|
|
|
296
324
|
// Complete the authorization process with query mode with the authorizationContext which opens the browser
|
|
297
325
|
const { code } =
|
|
298
326
|
await Credential.Issuance.completeUserAuthorizationWithQueryMode(
|
|
299
|
-
issuerRequestUri
|
|
300
|
-
clientId,
|
|
301
|
-
issuerConf,
|
|
302
|
-
idpHint,
|
|
303
|
-
redirectUri,
|
|
304
|
-
authorizationContext
|
|
327
|
+
issuerRequestUri
|
|
305
328
|
);
|
|
306
329
|
|
|
307
330
|
// Create DPoP context which will be used for the whole issuance flow
|
|
@@ -372,3 +395,117 @@ return {
|
|
|
372
395
|
The result of this flow is a raw credential and a parsed credential which must be stored securely in the wallet along with its crypto key.
|
|
373
396
|
|
|
374
397
|
</details>
|
|
398
|
+
|
|
399
|
+
<details>
|
|
400
|
+
<summary>eID issuance flow with MRTD PoP validation</summary>
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
/**
|
|
404
|
+
*
|
|
405
|
+
* Previous steps are the sames as the "eID issuance flow" example
|
|
406
|
+
*
|
|
407
|
+
*/
|
|
408
|
+
|
|
409
|
+
// Start user authorization indicating "mrtd-pop" as the proof type with the idpHint of the
|
|
410
|
+
// chosen identification method
|
|
411
|
+
const { issuerRequestUri, clientId, codeVerifier, credentialDefinition } =
|
|
412
|
+
await Credential.Issuance.startUserAuthorization(
|
|
413
|
+
issuerConf,
|
|
414
|
+
[credentialId],
|
|
415
|
+
{ proofType: "mrtd-pop", idpHinting: idpHint },
|
|
416
|
+
{
|
|
417
|
+
walletInstanceAttestation,
|
|
418
|
+
redirectUri: redirectUri,
|
|
419
|
+
wiaCryptoContext,
|
|
420
|
+
appFetch,
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
// Obtain the Authorization URL
|
|
425
|
+
const { authUrl } = await Credential.Issuance.buildAuthorizationUrl(
|
|
426
|
+
issuerRequestUri,
|
|
427
|
+
clientId,
|
|
428
|
+
issuerConf,
|
|
429
|
+
idpHint
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Extract challenge info from the Authorization URL
|
|
433
|
+
const { challenge_info } =
|
|
434
|
+
await Credential.Issuance.continueUserAuthorizationWithMRTDPoPChallenge(
|
|
435
|
+
authUrl
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Verify and parse challenge info and extract challenge data: initialization url, session and nonce
|
|
439
|
+
const {
|
|
440
|
+
htu: initUrl,
|
|
441
|
+
mrtd_auth_session,
|
|
442
|
+
mrtd_pop_jwt_nonce,
|
|
443
|
+
} = await Credential.Issuance.MRTDPoP.verifyAndParseChallengeInfo(
|
|
444
|
+
issuerConf,
|
|
445
|
+
challenge_info,
|
|
446
|
+
{ wiaCryptoContext }
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// Initialize challenge and obtain the challenge text to sign the CIE PACE protocol and validation url
|
|
450
|
+
const {
|
|
451
|
+
htu: validationUrl,
|
|
452
|
+
challenge,
|
|
453
|
+
mrtd_pop_nonce,
|
|
454
|
+
} = await Credential.Issuance.MRTDPoP.initChallenge(
|
|
455
|
+
issuerConf,
|
|
456
|
+
initUrl,
|
|
457
|
+
mrtd_auth_session,
|
|
458
|
+
mrtd_pop_jwt_nonce,
|
|
459
|
+
{
|
|
460
|
+
walletInstanceAttestation,
|
|
461
|
+
wiaCryptoContext,
|
|
462
|
+
appFetch,
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
// CIE cryptographic interaction: you need to sign the challenge with the CIE through NFC interaction
|
|
467
|
+
const { nis, mrtds } = /* NFC interactions functions */
|
|
468
|
+
|
|
469
|
+
// Validate challenge
|
|
470
|
+
const { mrtd_val_pop_nonce, redirect_uri } =
|
|
471
|
+
await Credential.Issuance.MRTDPoP.validateChallenge(
|
|
472
|
+
issuerConf,
|
|
473
|
+
validationUrl,
|
|
474
|
+
mrtd_auth_session,
|
|
475
|
+
mrtd_pop_nonce,
|
|
476
|
+
mrtd,
|
|
477
|
+
ias,
|
|
478
|
+
{
|
|
479
|
+
walletInstanceAttestation,
|
|
480
|
+
wiaCryptoContext,
|
|
481
|
+
appFetch,
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
// Build the callback url
|
|
486
|
+
const { callbackUrl } = await Credential.Issuance.buildChallengeCallbackUrl(
|
|
487
|
+
redirect_uri,
|
|
488
|
+
mrtd_val_pop_nonce,
|
|
489
|
+
mrtd_auth_session
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
// The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
|
|
493
|
+
const authRedirectUrl = /* From a browser or webview redirect */
|
|
494
|
+
|
|
495
|
+
// Complete the authorization process with query mode using the returned callback url
|
|
496
|
+
const { code } =
|
|
497
|
+
await Credential.Issuance.completeUserAuthorizationWithQueryMode(
|
|
498
|
+
authRedirectUrl
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
*
|
|
503
|
+
* The next steps are the same as the "eID issuance flow" example
|
|
504
|
+
*
|
|
505
|
+
*/
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
The result of this flow is a raw credential and a parsed credential which must be stored securely in the wallet along with its crypto key.
|
|
510
|
+
|
|
511
|
+
</details>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { evaluateIssuerTrust } from "./02-evaluate-issuer-trust";
|
|
2
2
|
import { startUserAuthorization } from "./03-start-user-authorization";
|
|
3
|
-
import { completeUserAuthorizationWithQueryMode, completeUserAuthorizationWithFormPostJwtMode, parseAuthorizationResponse, buildAuthorizationUrl, getRequestedCredentialToBePresented } from "./04-complete-user-authorization";
|
|
3
|
+
import { continueUserAuthorizationWithMRTDPoPChallenge, completeUserAuthorizationWithQueryMode, completeUserAuthorizationWithFormPostJwtMode, parseAuthorizationResponse, buildAuthorizationUrl, getRequestedCredentialToBePresented } from "./04-complete-user-authorization";
|
|
4
4
|
import { authorizeAccess } from "./05-authorize-access";
|
|
5
5
|
import { obtainCredential } from "./06-obtain-credential";
|
|
6
6
|
import { verifyAndParseCredential } from "./07-verify-and-parse-credential";
|
|
7
7
|
import * as Errors from "./errors";
|
|
8
|
-
|
|
8
|
+
import * as MRTDPoP from "./mrtd-pop";
|
|
9
|
+
export { MRTDPoP, evaluateIssuerTrust, startUserAuthorization, buildAuthorizationUrl, completeUserAuthorizationWithQueryMode, continueUserAuthorizationWithMRTDPoPChallenge, getRequestedCredentialToBePresented, completeUserAuthorizationWithFormPostJwtMode, authorizeAccess, obtainCredential, verifyAndParseCredential, parseAuthorizationResponse, Errors };
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["evaluateIssuerTrust","startUserAuthorization","completeUserAuthorizationWithQueryMode","completeUserAuthorizationWithFormPostJwtMode","parseAuthorizationResponse","buildAuthorizationUrl","getRequestedCredentialToBePresented","authorizeAccess","obtainCredential","verifyAndParseCredential","Errors"],"sourceRoot":"../../../../src","sources":["credential/issuance/index.ts"],"mappings":"AACA,SACEA,mBAAmB,QAEd,4BAA4B;AACnC,SACEC,sBAAsB,QAEjB,+BAA+B;AACtC,SACEC,sCAAsC,EACtCC,4CAA4C,EAC5CC,0BAA0B,EAC1BC,qBAAqB,
|
|
1
|
+
{"version":3,"names":["evaluateIssuerTrust","startUserAuthorization","continueUserAuthorizationWithMRTDPoPChallenge","completeUserAuthorizationWithQueryMode","completeUserAuthorizationWithFormPostJwtMode","parseAuthorizationResponse","buildAuthorizationUrl","getRequestedCredentialToBePresented","authorizeAccess","obtainCredential","verifyAndParseCredential","Errors","MRTDPoP"],"sourceRoot":"../../../../src","sources":["credential/issuance/index.ts"],"mappings":"AACA,SACEA,mBAAmB,QAEd,4BAA4B;AACnC,SACEC,sBAAsB,QAEjB,+BAA+B;AACtC,SACEC,6CAA6C,EAC7CC,sCAAsC,EACtCC,4CAA4C,EAC5CC,0BAA0B,EAC1BC,qBAAqB,EACrBC,mCAAmC,QAM9B,kCAAkC;AACzC,SAASC,eAAe,QAA8B,uBAAuB;AAC7E,SACEC,gBAAgB,QAEX,wBAAwB;AAC/B,SACEC,wBAAwB,QAEnB,kCAAkC;AACzC,OAAO,KAAKC,MAAM,MAAM,UAAU;AAClC,OAAO,KAAKC,OAAO,MAAM,YAAY;AAErC,SACEA,OAAO,EACPZ,mBAAmB,EACnBC,sBAAsB,EACtBK,qBAAqB,EACrBH,sCAAsC,EACtCD,6CAA6C,EAC7CK,mCAAmC,EACnCH,4CAA4C,EAC5CI,eAAe,EACfC,gBAAgB,EAChBC,wBAAwB,EACxBL,0BAA0B,EAC1BM,MAAM"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { decode as decodeJwt, verify as verifyJwt } from "@pagopa/io-react-native-jwt";
|
|
2
|
+
import { MrtdProofChallengeInfo } from "./types";
|
|
3
|
+
import { IoWalletError } from "../../../utils/errors";
|
|
4
|
+
/**
|
|
5
|
+
* Verifies and parses the payload of a MRTD Proof Challenge Info JWT obtained after the primary authentication.
|
|
6
|
+
*
|
|
7
|
+
* This function performs the following steps:
|
|
8
|
+
* 1. Validates the JWT signature using the issuer's JWKS.
|
|
9
|
+
* 2. Decodes the JWT and parses its structure according to the {@link MrtdProofChallengeInfo} schema.
|
|
10
|
+
* 3. Verifies that the `aud` claim matches the client's public key ID.
|
|
11
|
+
* 4. Checks that the JWT is not expired and was not issued in the future.
|
|
12
|
+
*
|
|
13
|
+
* @param issuerConf - The issuer configuration containing the JWKS for signature verification.
|
|
14
|
+
* @param challengeInfoJwt - The JWT string representing the MRTD Proof Challenge Info.
|
|
15
|
+
* @param context - The context containing the WIA crypto context used to retrieve the client public key.
|
|
16
|
+
* @returns The parsed payload of the MRTD Proof Challenge Info JWT.
|
|
17
|
+
* @throws {Error} If the JWT signature is invalid, the structure is malformed, the `aud` claim does not match,
|
|
18
|
+
* or the JWT is expired/not yet valid.
|
|
19
|
+
*/
|
|
20
|
+
export const verifyAndParseChallengeInfo = async (issuerConf, challengeInfoJwt, _ref) => {
|
|
21
|
+
let {
|
|
22
|
+
wiaCryptoContext
|
|
23
|
+
} = _ref;
|
|
24
|
+
// Verify JWT signature
|
|
25
|
+
await verifyJwt(challengeInfoJwt, issuerConf.oauth_authorization_server.jwks.keys);
|
|
26
|
+
|
|
27
|
+
// Decode JWT
|
|
28
|
+
const challengeInfoDecoded = decodeJwt(challengeInfoJwt);
|
|
29
|
+
|
|
30
|
+
// Parse and validate structure
|
|
31
|
+
const challengeInfoParsed = MrtdProofChallengeInfo.safeParse(challengeInfoDecoded);
|
|
32
|
+
if (!challengeInfoParsed.success) {
|
|
33
|
+
throw new IoWalletError("Malformed challenge info.");
|
|
34
|
+
}
|
|
35
|
+
const payload = challengeInfoParsed.data.payload;
|
|
36
|
+
|
|
37
|
+
// Verify aud claim
|
|
38
|
+
const clientId = await wiaCryptoContext.getPublicKey().then(_ => _.kid);
|
|
39
|
+
if (payload.aud !== clientId) {
|
|
40
|
+
throw new IoWalletError("aud claim does not match client_id.");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Verify iat and exp
|
|
44
|
+
const now = Math.floor(Date.now() / 1000);
|
|
45
|
+
if (payload.iat > now || payload.exp < now) {
|
|
46
|
+
throw new IoWalletError("JWT is not valid (issued in future or expired).");
|
|
47
|
+
}
|
|
48
|
+
return payload;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=01-verify-and-parse-challenge-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["decode","decodeJwt","verify","verifyJwt","MrtdProofChallengeInfo","IoWalletError","verifyAndParseChallengeInfo","issuerConf","challengeInfoJwt","_ref","wiaCryptoContext","oauth_authorization_server","jwks","keys","challengeInfoDecoded","challengeInfoParsed","safeParse","success","payload","data","clientId","getPublicKey","then","_","kid","aud","now","Math","floor","Date","iat","exp"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/01-verify-and-parse-challenge-info.ts"],"mappings":"AAAA,SACEA,MAAM,IAAIC,SAAS,EACnBC,MAAM,IAAIC,SAAS,QAEd,6BAA6B;AACpC,SAASC,sBAAsB,QAAQ,SAAS;AAGhD,SAASC,aAAa,QAAQ,uBAAuB;AAUrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,2BAAwD,GAAG,MAAAA,CACtEC,UAAU,EACVC,gBAAwB,EAAAC,IAAA,KAErB;EAAA,IADH;IAAEC;EAAiB,CAAC,GAAAD,IAAA;EAEpB;EACA,MAAMN,SAAS,CACbK,gBAAgB,EAChBD,UAAU,CAACI,0BAA0B,CAACC,IAAI,CAACC,IAC7C,CAAC;;EAED;EACA,MAAMC,oBAAoB,GAAGb,SAAS,CAACO,gBAAgB,CAAC;;EAExD;EACA,MAAMO,mBAAmB,GACvBX,sBAAsB,CAACY,SAAS,CAACF,oBAAoB,CAAC;EACxD,IAAI,CAACC,mBAAmB,CAACE,OAAO,EAAE;IAChC,MAAM,IAAIZ,aAAa,CAAC,2BAA2B,CAAC;EACtD;EACA,MAAMa,OAAO,GAAGH,mBAAmB,CAACI,IAAI,CAACD,OAAO;;EAEhD;EACA,MAAME,QAAQ,GAAG,MAAMV,gBAAgB,CAACW,YAAY,CAAC,CAAC,CAACC,IAAI,CAAEC,CAAC,IAAKA,CAAC,CAACC,GAAG,CAAC;EACzE,IAAIN,OAAO,CAACO,GAAG,KAAKL,QAAQ,EAAE;IAC5B,MAAM,IAAIf,aAAa,CAAC,qCAAqC,CAAC;EAChE;;EAEA;EACA,MAAMqB,GAAG,GAAGC,IAAI,CAACC,KAAK,CAACC,IAAI,CAACH,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;EACzC,IAAIR,OAAO,CAACY,GAAG,GAAGJ,GAAG,IAAIR,OAAO,CAACa,GAAG,GAAGL,GAAG,EAAE;IAC1C,MAAM,IAAIrB,aAAa,CAAC,iDAAiD,CAAC;EAC5E;EAEA,OAAOa,OAAO;AAChB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { hasStatusOrThrow } from "../../../utils/misc";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { createPopToken } from "../../../utils/pop";
|
|
4
|
+
import * as WalletInstanceAttestation from "../../../wallet-instance-attestation";
|
|
5
|
+
import { IssuerResponseError } from "../../../utils/errors";
|
|
6
|
+
import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
|
|
7
|
+
import { MrtdPoPChallenge } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Initialaizes the MRTD challenge with the data received from the issuer after the primary authentication.
|
|
10
|
+
* This function must be called after {@link verifyAndParseChallengeInfo}.
|
|
11
|
+
*
|
|
12
|
+
* @param issuerConf - The issuer configuration containing the JWKS for signature verification.
|
|
13
|
+
* @param initUrl - The endpoint to call to initialize the challenge.
|
|
14
|
+
* @param mrtd_auth_session - Session identifier for session binding obtained from the MRTD Proof JWT.
|
|
15
|
+
* @param mrtd_pop_jwt_nonce - Nonce value obtained from the MRTD Proof JWT.
|
|
16
|
+
* @param context - The context containing the WIA crypto context used to retrieve the client public key,
|
|
17
|
+
* the wallet instance attestation and an optional fetch implementation.
|
|
18
|
+
* @returns The payload of the MRTD PoP Challenge JWT.
|
|
19
|
+
*/
|
|
20
|
+
export const initChallenge = async (issuerConf, initUrl, mrtd_auth_session, mrtd_pop_jwt_nonce, context) => {
|
|
21
|
+
const {
|
|
22
|
+
appFetch = fetch,
|
|
23
|
+
walletInstanceAttestation,
|
|
24
|
+
wiaCryptoContext
|
|
25
|
+
} = context;
|
|
26
|
+
const aud = issuerConf.openid_credential_issuer.credential_issuer;
|
|
27
|
+
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation).payload.cnf.jwk.kid;
|
|
28
|
+
const signedWiaPoP = await createPopToken({
|
|
29
|
+
jti: `${uuidv4()}`,
|
|
30
|
+
aud,
|
|
31
|
+
iss
|
|
32
|
+
}, wiaCryptoContext);
|
|
33
|
+
const requestBody = {
|
|
34
|
+
mrtd_auth_session,
|
|
35
|
+
mrtd_pop_jwt_nonce
|
|
36
|
+
};
|
|
37
|
+
const mrtdPoPChallengeJwt = await appFetch(initUrl, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"OAuth-Client-Attestation": walletInstanceAttestation,
|
|
42
|
+
"OAuth-Client-Attestation-PoP": signedWiaPoP
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify(requestBody)
|
|
45
|
+
}).then(hasStatusOrThrow(202, IssuerResponseError)).then(res => res.text());
|
|
46
|
+
const mrtdPoPChallengeDecoded = decodeJwt(mrtdPoPChallengeJwt);
|
|
47
|
+
const {
|
|
48
|
+
payload
|
|
49
|
+
} = MrtdPoPChallenge.parse(mrtdPoPChallengeDecoded);
|
|
50
|
+
return payload;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=02-init-challenge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["hasStatusOrThrow","v4","uuidv4","createPopToken","WalletInstanceAttestation","IssuerResponseError","decode","decodeJwt","MrtdPoPChallenge","initChallenge","issuerConf","initUrl","mrtd_auth_session","mrtd_pop_jwt_nonce","context","appFetch","fetch","walletInstanceAttestation","wiaCryptoContext","aud","openid_credential_issuer","credential_issuer","iss","payload","cnf","jwk","kid","signedWiaPoP","jti","requestBody","mrtdPoPChallengeJwt","method","headers","body","JSON","stringify","then","res","text","mrtdPoPChallengeDecoded","parse"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/02-init-challenge.ts"],"mappings":"AAAA,SAASA,gBAAgB,QAAkB,qBAAqB;AAEhE,SAASC,EAAE,IAAIC,MAAM,QAAQ,MAAM;AACnC,SAASC,cAAc,QAAQ,oBAAoB;AACnD,OAAO,KAAKC,yBAAyB,MAAM,sCAAsC;AAEjF,SAASC,mBAAmB,QAAQ,uBAAuB;AAC3D,SAASC,MAAM,IAAIC,SAAS,QAAQ,6BAA6B;AACjE,SAASC,gBAAgB,QAAQ,SAAS;AAc1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,aAA4B,GAAG,MAAAA,CAC1CC,UAAU,EACVC,OAAO,EACPC,iBAAiB,EACjBC,kBAAkB,EAClBC,OAAO,KACJ;EACH,MAAM;IACJC,QAAQ,GAAGC,KAAK;IAChBC,yBAAyB;IACzBC;EACF,CAAC,GAAGJ,OAAO;EAEX,MAAMK,GAAG,GAAGT,UAAU,CAACU,wBAAwB,CAACC,iBAAiB;EACjE,MAAMC,GAAG,GAAGlB,yBAAyB,CAACE,MAAM,CAACW,yBAAyB,CAAC,CACpEM,OAAO,CAACC,GAAG,CAACC,GAAG,CAACC,GAAG;EAEtB,MAAMC,YAAY,GAAG,MAAMxB,cAAc,CACvC;IACEyB,GAAG,EAAG,GAAE1B,MAAM,CAAC,CAAE,EAAC;IAClBiB,GAAG;IACHG;EACF,CAAC,EACDJ,gBACF,CAAC;EAED,MAAMW,WAAW,GAAG;IAClBjB,iBAAiB;IACjBC;EACF,CAAC;EAED,MAAMiB,mBAAmB,GAAG,MAAMf,QAAQ,CAACJ,OAAO,EAAE;IAClDoB,MAAM,EAAE,MAAM;IACdC,OAAO,EAAE;MACP,cAAc,EAAE,kBAAkB;MAClC,0BAA0B,EAAEf,yBAAyB;MACrD,8BAA8B,EAAEU;IAClC,CAAC;IACDM,IAAI,EAAEC,IAAI,CAACC,SAAS,CAACN,WAAW;EAClC,CAAC,CAAC,CACCO,IAAI,CAACpC,gBAAgB,CAAC,GAAG,EAAEK,mBAAmB,CAAC,CAAC,CAChD+B,IAAI,CAAEC,GAAG,IAAKA,GAAG,CAACC,IAAI,CAAC,CAAC,CAAC;EAE5B,MAAMC,uBAAuB,GAAGhC,SAAS,CAACuB,mBAAmB,CAAC;EAC9D,MAAM;IAAEP;EAAQ,CAAC,GAAGf,gBAAgB,CAACgC,KAAK,CAACD,uBAAuB,CAAC;EAEnE,OAAOhB,OAAO;AAChB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { SignJWT } from "@pagopa/io-react-native-jwt";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { IssuerResponseError } from "../../../utils/errors";
|
|
4
|
+
import { hasStatusOrThrow } from "../../../utils/misc";
|
|
5
|
+
import { createPopToken } from "../../../utils/pop";
|
|
6
|
+
import * as WalletInstanceAttestation from "../../../wallet-instance-attestation";
|
|
7
|
+
import { MrtdPopVerificationResult } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Validates the MRTD signed challenge by sending the MRTD and IAS payloads to the issuer.
|
|
10
|
+
* This function must be called after {@link initChallenge} and after obtaining the MRTD and IAS payloads
|
|
11
|
+
* through the CIE PACE process.
|
|
12
|
+
*
|
|
13
|
+
* @param issuerConf - The issuer configuration containing the JWKS for signature verification.
|
|
14
|
+
* @param verifyUrl - The endpoint to call to validate the challenge.
|
|
15
|
+
* @param mrtd_auth_session - Session identifier for session binding obtained from the MRTD Proof JWT.
|
|
16
|
+
* @param mrtd_pop_nonce - Nonce value obtained from the MRTD Proof JWT.
|
|
17
|
+
* @param mrtd - MRTD validation data containing Data Groups and SOD.
|
|
18
|
+
* @param ias - IAS validation data containing Anti-Cloning Public Key, and SOD.
|
|
19
|
+
* @param context - The context containing the WIA crypto context used to retrieve the client public key,
|
|
20
|
+
* the wallet instance attestation and an optional fetch implementation.
|
|
21
|
+
* @returns The MRTD PoP Verification Result containing the validation nonce and redirect URI to complete the flow.
|
|
22
|
+
*/
|
|
23
|
+
export const validateChallenge = async (issuerConf, verifyUrl, mrtd_auth_session, mrtd_pop_nonce, mrtd, ias, context) => {
|
|
24
|
+
const {
|
|
25
|
+
appFetch = fetch,
|
|
26
|
+
walletInstanceAttestation,
|
|
27
|
+
wiaCryptoContext
|
|
28
|
+
} = context;
|
|
29
|
+
const aud = issuerConf.openid_credential_issuer.credential_issuer;
|
|
30
|
+
const iss = WalletInstanceAttestation.decode(walletInstanceAttestation).payload.cnf.jwk.kid;
|
|
31
|
+
const signedWiaPoP = await createPopToken({
|
|
32
|
+
jti: `${uuidv4()}`,
|
|
33
|
+
aud,
|
|
34
|
+
iss
|
|
35
|
+
}, wiaCryptoContext);
|
|
36
|
+
const {
|
|
37
|
+
kid
|
|
38
|
+
} = await wiaCryptoContext.getPublicKey();
|
|
39
|
+
const mrtd_validation_jwt = await new SignJWT(wiaCryptoContext).setProtectedHeader({
|
|
40
|
+
typ: "mrtd-ias+jwt",
|
|
41
|
+
kid
|
|
42
|
+
}).setPayload({
|
|
43
|
+
iss,
|
|
44
|
+
aud,
|
|
45
|
+
document_type: "cie",
|
|
46
|
+
mrtd,
|
|
47
|
+
ias
|
|
48
|
+
}).setIssuedAt().setExpirationTime("5m").sign();
|
|
49
|
+
const requestBody = {
|
|
50
|
+
mrtd_validation_jwt,
|
|
51
|
+
mrtd_auth_session,
|
|
52
|
+
mrtd_pop_nonce
|
|
53
|
+
};
|
|
54
|
+
const verifyResult = await appFetch(verifyUrl, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
"OAuth-Client-Attestation": walletInstanceAttestation,
|
|
59
|
+
"OAuth-Client-Attestation-PoP": signedWiaPoP
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify(requestBody)
|
|
62
|
+
}).then(hasStatusOrThrow(202, IssuerResponseError)).then(res => res.json());
|
|
63
|
+
const verifyResultParsed = MrtdPopVerificationResult.parse(verifyResult);
|
|
64
|
+
return verifyResultParsed;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* WARNING: This function must be called after {@link validateChallenge}. The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
|
|
69
|
+
* Builds the callback URL to which the end user should be redirected to continue the authentication flow after the MRTD challenge validation.
|
|
70
|
+
* @param redirectUri - The redirect URI provided by the issuer after the challenge validation to continue the authentication flow.
|
|
71
|
+
* @param valPopNonce - The MRTD validation PoP nonce obtained from the challenge validation response.
|
|
72
|
+
* @param authSession - The MRTD authentication session identifier used for session binding.
|
|
73
|
+
* @returns An object containing the callback URL
|
|
74
|
+
*/
|
|
75
|
+
export const buildChallengeCallbackUrl = async (redirectUri, valPopNonce, authSession) => {
|
|
76
|
+
const params = new URLSearchParams({
|
|
77
|
+
mrtd_val_pop_nonce: valPopNonce,
|
|
78
|
+
mrtd_auth_session: authSession
|
|
79
|
+
});
|
|
80
|
+
const callbackUrl = `${redirectUri}?${params}`;
|
|
81
|
+
return {
|
|
82
|
+
callbackUrl
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=03-validate-challenge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SignJWT","v4","uuidv4","IssuerResponseError","hasStatusOrThrow","createPopToken","WalletInstanceAttestation","MrtdPopVerificationResult","validateChallenge","issuerConf","verifyUrl","mrtd_auth_session","mrtd_pop_nonce","mrtd","ias","context","appFetch","fetch","walletInstanceAttestation","wiaCryptoContext","aud","openid_credential_issuer","credential_issuer","iss","decode","payload","cnf","jwk","kid","signedWiaPoP","jti","getPublicKey","mrtd_validation_jwt","setProtectedHeader","typ","setPayload","document_type","setIssuedAt","setExpirationTime","sign","requestBody","verifyResult","method","headers","body","JSON","stringify","then","res","json","verifyResultParsed","parse","buildChallengeCallbackUrl","redirectUri","valPopNonce","authSession","params","URLSearchParams","mrtd_val_pop_nonce","callbackUrl"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/03-validate-challenge.ts"],"mappings":"AAAA,SAASA,OAAO,QAA4B,6BAA6B;AACzE,SAASC,EAAE,IAAIC,MAAM,QAAQ,MAAM;AACnC,SAASC,mBAAmB,QAAQ,uBAAuB;AAC3D,SAASC,gBAAgB,QAAkB,qBAAqB;AAChE,SAASC,cAAc,QAAQ,oBAAoB;AACnD,OAAO,KAAKC,yBAAyB,MAAM,sCAAsC;AAEjF,SACEC,yBAAyB,QAGpB,SAAS;AAyBhB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAoC,GAAG,MAAAA,CAClDC,UAAU,EACVC,SAAS,EACTC,iBAAiB,EACjBC,cAAc,EACdC,IAAI,EACJC,GAAG,EACHC,OAAO,KACJ;EACH,MAAM;IACJC,QAAQ,GAAGC,KAAK;IAChBC,yBAAyB;IACzBC;EACF,CAAC,GAAGJ,OAAO;EAEX,MAAMK,GAAG,GAAGX,UAAU,CAACY,wBAAwB,CAACC,iBAAiB;EACjE,MAAMC,GAAG,GAAGjB,yBAAyB,CAACkB,MAAM,CAACN,yBAAyB,CAAC,CACpEO,OAAO,CAACC,GAAG,CAACC,GAAG,CAACC,GAAG;EAEtB,MAAMC,YAAY,GAAG,MAAMxB,cAAc,CACvC;IACEyB,GAAG,EAAG,GAAE5B,MAAM,CAAC,CAAE,EAAC;IAClBkB,GAAG;IACHG;EACF,CAAC,EACDJ,gBACF,CAAC;EAED,MAAM;IAAES;EAAI,CAAC,GAAG,MAAMT,gBAAgB,CAACY,YAAY,CAAC,CAAC;EAErD,MAAMC,mBAAmB,GAAG,MAAM,IAAIhC,OAAO,CAACmB,gBAAgB,CAAC,CAC5Dc,kBAAkB,CAAC;IAClBC,GAAG,EAAE,cAAc;IACnBN;EACF,CAAC,CAAC,CACDO,UAAU,CAAC;IACVZ,GAAG;IACHH,GAAG;IACHgB,aAAa,EAAE,KAAK;IACpBvB,IAAI;IACJC;EACF,CAAC,CAAC,CACDuB,WAAW,CAAC,CAAC,CACbC,iBAAiB,CAAC,IAAI,CAAC,CACvBC,IAAI,CAAC,CAAC;EAET,MAAMC,WAAW,GAAG;IAClBR,mBAAmB;IACnBrB,iBAAiB;IACjBC;EACF,CAAC;EAED,MAAM6B,YAAY,GAAG,MAAMzB,QAAQ,CAACN,SAAS,EAAE;IAC7CgC,MAAM,EAAE,MAAM;IACdC,OAAO,EAAE;MACP,cAAc,EAAE,kBAAkB;MAClC,0BAA0B,EAAEzB,yBAAyB;MACrD,8BAA8B,EAAEW;IAClC,CAAC;IACDe,IAAI,EAAEC,IAAI,CAACC,SAAS,CAACN,WAAW;EAClC,CAAC,CAAC,CACCO,IAAI,CAAC3C,gBAAgB,CAAC,GAAG,EAAED,mBAAmB,CAAC,CAAC,CAChD4C,IAAI,CAAEC,GAAG,IAAKA,GAAG,CAACC,IAAI,CAAC,CAAC,CAAC;EAE5B,MAAMC,kBAAkB,GAAG3C,yBAAyB,CAAC4C,KAAK,CAACV,YAAY,CAAC;EACxE,OAAOS,kBAAkB;AAC3B,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAME,yBAAoD,GAAG,MAAAA,CAClEC,WAAW,EACXC,WAAW,EACXC,WAAW,KACR;EACH,MAAMC,MAAM,GAAG,IAAIC,eAAe,CAAC;IACjCC,kBAAkB,EAAEJ,WAAW;IAC/B3C,iBAAiB,EAAE4C;EACrB,CAAC,CAAC;EAEF,MAAMI,WAAW,GAAI,GAAEN,WAAY,IAAGG,MAAO,EAAC;EAC9C,OAAO;IAAEG;EAAY,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# MRTD PoP flow
|
|
2
|
+
|
|
3
|
+
**MRTD-PoP (Machine Readable Travel Document - Proof of Possession)** flow for the IO Wallet, following the [eID Wallet L2+ Credential Issuance specification](https://italia.github.io/eid-wallet-it-docs/versione-corrente/en/credential-issuance-l2plus.html).
|
|
4
|
+
|
|
5
|
+
The MRTD-PoP flow is used to prove possession of an MRTD (such as a CIE) during the issuance of high-assurance credentials. The process involves a challenge-response protocol between the wallet and the issuer, leveraging JWTs and cryptographic attestation.
|
|
6
|
+
|
|
7
|
+
This flow is part of the [PID issuance flow](../README.md) and must be started after the `continueUserAuthorizationWithMRTDPoPChallenge` function. Once MRTD PoP is completed, the PID issuance flow must continue with the `completeUserAuthorizationWithQueryMode` function with the authorization url obtained from the validation.
|
|
8
|
+
|
|
9
|
+
> **⚠️ Important**: The entire flow must be initiated and concluded within the same web context (e.g., the same WebView instance) to maintain session continuity. Using different contexts (such as switching between an external browser and a WebView) will result in session loss and authentication failures due to cookie/session mismatch (JSESSIONID).
|
|
10
|
+
|
|
11
|
+
## Sequence Diagram
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
graph TD;
|
|
15
|
+
A@{ shape: subproc, label: "continueUserAuthorizationWithMRTDPoPChallenge" }
|
|
16
|
+
subgraph MRTD PoP
|
|
17
|
+
B[verifyAndParseChallengeInfo]
|
|
18
|
+
C[initChallenge]
|
|
19
|
+
E[validateChallenge]
|
|
20
|
+
end
|
|
21
|
+
F@{ shape: subproc, label: "completeUserAuthorizationWithQueryModeChallenge" }
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
A -.-> B
|
|
25
|
+
B --> C
|
|
26
|
+
C -->E
|
|
27
|
+
E -.-> F
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Verify and parse challenge info and extract challenge data: initialization url, session and nonce
|
|
35
|
+
const {
|
|
36
|
+
htu: initUrl,
|
|
37
|
+
mrtd_auth_session,
|
|
38
|
+
mrtd_pop_jwt_nonce,
|
|
39
|
+
} = await Credential.Issuance.MRTDPoP.verifyAndParseChallengeInfo(
|
|
40
|
+
issuerConf,
|
|
41
|
+
challenge_info,
|
|
42
|
+
{ wiaCryptoContext }
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Initialize challenge and obtain the challenge text to sign the CIE PACE protocol and validation url
|
|
46
|
+
const {
|
|
47
|
+
htu: validationUrl,
|
|
48
|
+
challenge,
|
|
49
|
+
mrtd_pop_nonce,
|
|
50
|
+
} = await Credential.Issuance.MRTDPoP.initChallenge(
|
|
51
|
+
issuerConf,
|
|
52
|
+
initUrl,
|
|
53
|
+
mrtd_auth_session,
|
|
54
|
+
mrtd_pop_jwt_nonce,
|
|
55
|
+
{
|
|
56
|
+
walletInstanceAttestation,
|
|
57
|
+
wiaCryptoContext,
|
|
58
|
+
appFetch,
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// CIE cryptographic interaction: you need to sign the challenge with the CIE through NFC interaction
|
|
63
|
+
const { nis, mrtds } = /* NFC interactions functions */
|
|
64
|
+
|
|
65
|
+
// Validate challenge
|
|
66
|
+
const { mrtd_val_pop_nonce, redirect_uri } =
|
|
67
|
+
await Credential.Issuance.MRTDPoP.validateChallenge(
|
|
68
|
+
issuerConf,
|
|
69
|
+
validationUrl,
|
|
70
|
+
mrtd_auth_session,
|
|
71
|
+
mrtd_pop_nonce,
|
|
72
|
+
mrtd,
|
|
73
|
+
ias,
|
|
74
|
+
{
|
|
75
|
+
walletInstanceAttestation,
|
|
76
|
+
wiaCryptoContext,
|
|
77
|
+
appFetch,
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Build the callback url
|
|
82
|
+
const { callbackUrl } = await Credential.Issuance.buildChallengeCallbackUrl(
|
|
83
|
+
redirect_uri,
|
|
84
|
+
mrtd_val_pop_nonce,
|
|
85
|
+
mrtd_auth_session
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
|
|
89
|
+
const authRedirectUrl = /* From a browser or webview redirect */
|
|
90
|
+
|
|
91
|
+
// Use the authRedirectUrl to continue the PID issuance flow
|
|
92
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { verifyAndParseChallengeInfo } from "./01-verify-and-parse-challenge-info";
|
|
2
|
+
import { initChallenge } from "./02-init-challenge";
|
|
3
|
+
import { validateChallenge, buildChallengeCallbackUrl } from "./03-validate-challenge";
|
|
4
|
+
export { verifyAndParseChallengeInfo, initChallenge, validateChallenge, buildChallengeCallbackUrl };
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["verifyAndParseChallengeInfo","initChallenge","validateChallenge","buildChallengeCallbackUrl"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/index.ts"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,sCAAsC;AAC7C,SAASC,aAAa,QAA4B,qBAAqB;AACvE,SACEC,iBAAiB,EACjBC,yBAAyB,QAGpB,yBAAyB;AAGhC,SACEH,2BAA2B,EAC3BC,aAAa,EACbC,iBAAiB,EACjBC,yBAAyB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
export const MrtdProofChallengeInfo = z.object({
|
|
3
|
+
protectedHeader: z.object({
|
|
4
|
+
typ: z.literal("mrtd-ias+jwt"),
|
|
5
|
+
alg: z.string(),
|
|
6
|
+
kid: z.string()
|
|
7
|
+
}),
|
|
8
|
+
payload: z.object({
|
|
9
|
+
iss: z.string(),
|
|
10
|
+
aud: z.string(),
|
|
11
|
+
iat: z.number(),
|
|
12
|
+
exp: z.number(),
|
|
13
|
+
status: z.literal("require_interaction"),
|
|
14
|
+
type: z.literal("mrtd+ias"),
|
|
15
|
+
mrtd_auth_session: z.string(),
|
|
16
|
+
state: z.string(),
|
|
17
|
+
mrtd_pop_jwt_nonce: z.string(),
|
|
18
|
+
htu: z.string(),
|
|
19
|
+
htm: z.literal("POST")
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
export const MrtdPoPChallenge = z.object({
|
|
23
|
+
protectedHeader: z.object({
|
|
24
|
+
typ: z.literal("mrtd-ias-pop+jwt"),
|
|
25
|
+
alg: z.string(),
|
|
26
|
+
kid: z.string()
|
|
27
|
+
}),
|
|
28
|
+
payload: z.object({
|
|
29
|
+
iss: z.string(),
|
|
30
|
+
aud: z.string(),
|
|
31
|
+
iat: z.number(),
|
|
32
|
+
exp: z.number(),
|
|
33
|
+
challenge: z.string(),
|
|
34
|
+
mrtd_pop_nonce: z.string(),
|
|
35
|
+
mrz: z.string().optional(),
|
|
36
|
+
htu: z.string(),
|
|
37
|
+
htm: z.literal("POST")
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
export const MrtdPopVerificationResult = z.object({
|
|
41
|
+
status: z.literal("require_interaction"),
|
|
42
|
+
type: z.literal("redirect_to_web"),
|
|
43
|
+
mrtd_val_pop_nonce: z.string(),
|
|
44
|
+
redirect_uri: z.string()
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["z","MrtdProofChallengeInfo","object","protectedHeader","typ","literal","alg","string","kid","payload","iss","aud","iat","number","exp","status","type","mrtd_auth_session","state","mrtd_pop_jwt_nonce","htu","htm","MrtdPoPChallenge","challenge","mrtd_pop_nonce","mrz","optional","MrtdPopVerificationResult","mrtd_val_pop_nonce","redirect_uri"],"sourceRoot":"../../../../../src","sources":["credential/issuance/mrtd-pop/types.ts"],"mappings":"AAAA,OAAO,KAAKA,CAAC,MAAM,KAAK;AAGxB,OAAO,MAAMC,sBAAsB,GAAGD,CAAC,CAACE,MAAM,CAAC;EAC7CC,eAAe,EAAEH,CAAC,CAACE,MAAM,CAAC;IACxBE,GAAG,EAAEJ,CAAC,CAACK,OAAO,CAAC,cAAc,CAAC;IAC9BC,GAAG,EAAEN,CAAC,CAACO,MAAM,CAAC,CAAC;IACfC,GAAG,EAAER,CAAC,CAACO,MAAM,CAAC;EAChB,CAAC,CAAC;EACFE,OAAO,EAAET,CAAC,CAACE,MAAM,CAAC;IAChBQ,GAAG,EAAEV,CAAC,CAACO,MAAM,CAAC,CAAC;IACfI,GAAG,EAAEX,CAAC,CAACO,MAAM,CAAC,CAAC;IACfK,GAAG,EAAEZ,CAAC,CAACa,MAAM,CAAC,CAAC;IACfC,GAAG,EAAEd,CAAC,CAACa,MAAM,CAAC,CAAC;IACfE,MAAM,EAAEf,CAAC,CAACK,OAAO,CAAC,qBAAqB,CAAC;IACxCW,IAAI,EAAEhB,CAAC,CAACK,OAAO,CAAC,UAAU,CAAC;IAC3BY,iBAAiB,EAAEjB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC7BW,KAAK,EAAElB,CAAC,CAACO,MAAM,CAAC,CAAC;IACjBY,kBAAkB,EAAEnB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC9Ba,GAAG,EAAEpB,CAAC,CAACO,MAAM,CAAC,CAAC;IACfc,GAAG,EAAErB,CAAC,CAACK,OAAO,CAAC,MAAM;EACvB,CAAC;AACH,CAAC,CAAC;AAGF,OAAO,MAAMiB,gBAAgB,GAAGtB,CAAC,CAACE,MAAM,CAAC;EACvCC,eAAe,EAAEH,CAAC,CAACE,MAAM,CAAC;IACxBE,GAAG,EAAEJ,CAAC,CAACK,OAAO,CAAC,kBAAkB,CAAC;IAClCC,GAAG,EAAEN,CAAC,CAACO,MAAM,CAAC,CAAC;IACfC,GAAG,EAAER,CAAC,CAACO,MAAM,CAAC;EAChB,CAAC,CAAC;EACFE,OAAO,EAAET,CAAC,CAACE,MAAM,CAAC;IAChBQ,GAAG,EAAEV,CAAC,CAACO,MAAM,CAAC,CAAC;IACfI,GAAG,EAAEX,CAAC,CAACO,MAAM,CAAC,CAAC;IACfK,GAAG,EAAEZ,CAAC,CAACa,MAAM,CAAC,CAAC;IACfC,GAAG,EAAEd,CAAC,CAACa,MAAM,CAAC,CAAC;IACfU,SAAS,EAAEvB,CAAC,CAACO,MAAM,CAAC,CAAC;IACrBiB,cAAc,EAAExB,CAAC,CAACO,MAAM,CAAC,CAAC;IAC1BkB,GAAG,EAAEzB,CAAC,CAACO,MAAM,CAAC,CAAC,CAACmB,QAAQ,CAAC,CAAC;IAC1BN,GAAG,EAAEpB,CAAC,CAACO,MAAM,CAAC,CAAC;IACfc,GAAG,EAAErB,CAAC,CAACK,OAAO,CAAC,MAAM;EACvB,CAAC;AACH,CAAC,CAAC;AAiBF,OAAO,MAAMsB,yBAAyB,GAAG3B,CAAC,CAACE,MAAM,CAAC;EAChDa,MAAM,EAAEf,CAAC,CAACK,OAAO,CAAC,qBAAqB,CAAC;EACxCW,IAAI,EAAEhB,CAAC,CAACK,OAAO,CAAC,iBAAiB,CAAC;EAClCuB,kBAAkB,EAAE5B,CAAC,CAACO,MAAM,CAAC,CAAC;EAC9BsB,YAAY,EAAE7B,CAAC,CAACO,MAAM,CAAC;AACzB,CAAC,CAAC"}
|
package/lib/module/utils/auth.js
CHANGED
|
@@ -32,4 +32,12 @@ export const AuthorizationErrorShape = z.object({
|
|
|
32
32
|
/**
|
|
33
33
|
* Type of the identification result.
|
|
34
34
|
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* MRTD PoP Challenge Info response structure
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
export const AuthorizationChallengeResultShape = z.object({
|
|
41
|
+
challenge_info: z.string()
|
|
42
|
+
});
|
|
35
43
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["z","AuthorizationResultShape","object","code","string","state","iss","optional","AuthorizationErrorShape","error","error_description","error_uri"],"sourceRoot":"../../../src","sources":["utils/auth.ts"],"mappings":"AAAA,OAAO,KAAKA,CAAC,MAAM,KAAK;;AAExB;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA,OAAO,MAAMC,wBAAwB,GAAGD,CAAC,CAACE,MAAM,CAAC;EAC/CC,IAAI,EAAEH,CAAC,CAACI,MAAM,CAAC,CAAC;EAChBC,KAAK,EAAEL,CAAC,CAACI,MAAM,CAAC,CAAC;EACjBE,GAAG,EAAEN,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC;AAC3B,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,uBAAuB,GAAGR,CAAC,CAACE,MAAM,CAAC;EAC9CO,KAAK,EAAET,CAAC,CAACI,MAAM,CAAC,CAAC;EAAE;EACnBM,iBAAiB,EAAEV,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC,CAAC;EACxCI,SAAS,EAAEX,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC,CAAC;EAChCF,KAAK,EAAEL,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC;AAC7B,CAAC,CAAC;;AAEF;AACA;AACA"}
|
|
1
|
+
{"version":3,"names":["z","AuthorizationResultShape","object","code","string","state","iss","optional","AuthorizationErrorShape","error","error_description","error_uri","AuthorizationChallengeResultShape","challenge_info"],"sourceRoot":"../../../src","sources":["utils/auth.ts"],"mappings":"AAAA,OAAO,KAAKA,CAAC,MAAM,KAAK;;AAExB;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA,OAAO,MAAMC,wBAAwB,GAAGD,CAAC,CAACE,MAAM,CAAC;EAC/CC,IAAI,EAAEH,CAAC,CAACI,MAAM,CAAC,CAAC;EAChBC,KAAK,EAAEL,CAAC,CAACI,MAAM,CAAC,CAAC;EACjBE,GAAG,EAAEN,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC;AAC3B,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,uBAAuB,GAAGR,CAAC,CAACE,MAAM,CAAC;EAC9CO,KAAK,EAAET,CAAC,CAACI,MAAM,CAAC,CAAC;EAAE;EACnBM,iBAAiB,EAAEV,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC,CAAC;EACxCI,SAAS,EAAEX,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC,CAAC;EAChCF,KAAK,EAAEL,CAAC,CAACI,MAAM,CAAC,CAAC,CAACG,QAAQ,CAAC;AAC7B,CAAC,CAAC;;AAEF;AACA;AACA;;AAGA;AACA;AACA;;AAEA,OAAO,MAAMK,iCAAiC,GAAGZ,CAAC,CAACE,MAAM,CAAC;EACxDW,cAAc,EAAEb,CAAC,CAACI,MAAM,CAAC;AAC3B,CAAC,CAAC"}
|