mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-07 20:53:54 -05:00
[Injimob 812] openID4VP sharing flow UI implementation (#1628)
* [INJIMOB-1629] add an api to fetch the trusted verifiers list for vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add screens for showing vcs matching openId4vp authorization request and selecting VCs Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add states in scan machine to show loader screen when vp sharing is started Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] send events to parent machine from openId4vp machine to update UI when performing vp sharing Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] change the position of check box in vc container in vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add styles to send vp screen elements to match wireframe and add context variable to store vp sharing purpose Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] show error screen if no credential in wallet matches with authorization request Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] show confirmation pop up when user decline the consent for sharing vp Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] show error screen if the verifier authentication is failed after scanning vp sharing qr code Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] move error and overlay details assigning logic to send vp screen controller Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add translations for vp sharing flow texts in all languages Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] make changes in card skeleton component to show vc card loader in vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] fix issues with reject button in sharing vp screen Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add support for sharing vp with out selfie from kebab menu Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add support for sharing vp with selfie from kebab menu Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add missing translations for error and overlay screen texts of vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add logic in scan screen to show error screens in vp sharing from kebab menu flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add logic to filter the VCs based on the type of the VC in vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: generate proof for vp token and send vp Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-1629] show generic error message if any error occured while validating vp qr code Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add logic to allow user retry vp sharing 3 times if any technical error occurred Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] add react method in native module to send the generated vp response metadata and make changes to show any errors occured after sharing vp Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1629] show success screen if vp is shared successfully to the verifier Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: refactor proof generation and integrate remote openid4vp package Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-812]: refactor proof generation and integrate remote openid4vp package Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-812] make changes in the code to fetch the VCs properly if scope is present in the authorization code Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fix the logic of checking if order field value in issuer wellknown is null and empty or not Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] move qr login and openid4vp red id's to scan actions Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] remove unnecessary logs Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] rename openid4vp actions to make them more meaningful and remove unnecessary events in send vp screen controller Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] show error screen if none of the selected VC has image but user chosen share with selfie option Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] add guard logic for checking if any of the selected VC has image or not Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: update package resolved with openid4vp library Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-812] fix the logic of retry button in technical error screen and perform the vp sharing again when user click on retry button Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] show specific error screen if required info is missing in qr code Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: refactor native module and wrapper with updated library name Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-1709]: Integrate VC verifier aar for Vc verification of Mosip VC (#1624) * [INJIMOB-1709]: integrate VC verifier aar for Vc verification of Mosip VC Signed-off-by: Alka Prasad <prasadalka1998@gmail.com> * [INJIMOB-1709]: Handling Response from Vc Verifier Library Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> * [INJIMOB-1709]: Removed mavenLocal from build.gradle Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> * [INJIMOB-1709]: Updated build.gradle Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> * [INJIMOB-1709]: Removed Certify from isMosipVC Condition. Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> * [INJIMOB-1709]: Adding isAndroid check and verifying using digital bazaar for iOs. Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> --------- Signed-off-by: Alka Prasad <prasadalka1998@gmail.com> Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> Co-authored-by: Alka Prasad <prasadalka1998@gmail.com> * [INJIMOB-1629] rename openId4VP to openID4VP in all files and address pr comments Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fetch trusted verifiers list from mimoto end point Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] rename openId4VP to openID4VP in all files Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] make trusted verifier api call cache preferred Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] rename openId4VP to openID4VP in all places Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: refactor and update openid4vp swift library Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-812] remove code related to scope in openid4vp flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] remove code related to type in openid4vp actions Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] rename response_uri to response_uris in openid4vp native wrappers Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] rename typegen file in openid4vp machine Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] change target state of checkFaceAuthConsent to getTrustedVerifiersList Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812]: update openid4vp swift package Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-2097]: bump up app version to 0.15.0 (#1631) Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> Co-authored-by: adityankannan-tw <adityan410pm@gmail.com> * [INJIMOB-1884] remove SetupKeySelectionScreen (#1632) Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> * [INJIMOB-812] change the conditional logic for checking if downloading error is generic or not in issuer guards and selectors Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fetch the trusted verifiers list properly from api response Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] send the list of selected vcs images to face scanner machine only if face is available Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fix home button navigation issue in error screen of vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] move openid4vp machine to showError state if there is any occurred in vp sharing flow and reset error when user navigates to home screen Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fix some flows in vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-812] fix the logic for showing the error screen as part of scan screen in vp sharing flow Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> --------- Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: adityankannan-tw <adityan410pm@gmail.com> Signed-off-by: Alka Prasad <prasadalka1998@gmail.com> Signed-off-by: BalachandarG <balachandar.g@thoughtworks.com> Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com> Co-authored-by: adityankannan-tw <adityan410pm@gmail.com> Co-authored-by: balachandarg-tw <115633327+balachandarg-tw@users.noreply.github.com> Co-authored-by: Alka Prasad <prasadalka1998@gmail.com> Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Co-authored-by: abhip2565 <74866247+abhip2565@users.noreply.github.com>
This commit is contained in:
67
.talismanrc
67
.talismanrc
@@ -2,7 +2,7 @@ fileignoreconfig:
|
||||
- filename: package.json
|
||||
checksum: 5b4fcb5ddc7cc96cc2d1733b544d56ea66e88cdab995a1052fbf9ac0e9c2dc21
|
||||
- filename: package-lock.json
|
||||
checksum: 98f4ef19f06521bac3ea3033d82810203214bf55b0469790a1d8acc20933c581
|
||||
checksum: 84234f4ae94673b929b5355259c4fa0e497d4ec327985f497120b6fb66bb8c63
|
||||
- filename: lib/jsonld-signatures/suites/ed255192018/ed25519.ts
|
||||
checksum: 493b6e31144116cb612c24d98b97d8adcad5609c0a52c865a6847ced0a0ddc3a
|
||||
- filename: components/PasscodeVerify.tsx
|
||||
@@ -38,9 +38,9 @@ fileignoreconfig:
|
||||
- filename: screens/Home/MyVcs/GetIdInputModal.tsx
|
||||
checksum: 5c736ed79a372d0ffa7c02eb33d0dc06edbbb08d120978ff287f5f06cd6c7746
|
||||
- filename: shared/openId4VCI/Utils.ts
|
||||
checksum: ee4db1768be8d51fac0eb876a7b16fd2ab1806abcc711f01056f672003d11f31
|
||||
checksum: 78473ce25cd52c8d07da9ba98683bdb64ae83a9de8fa907c62f2ebeca7dd21dc
|
||||
- filename: shared/cryptoutil/cryptoUtil.ts
|
||||
checksum: a8edd1047e33bfc9e37b73945b8edcd294b8e29baf380f86cb0f647b355c8e5a
|
||||
checksum: 281f061ae6eb722c75c2caf2bdfb5b1bf72f292b471318d972c898c5a972c65f
|
||||
- filename: shared/telemetry/TelemetryConstants.js
|
||||
checksum: fd8dc3a69cdef68855dc5f0531d8e634bfa2621bb4dc22f85b8247512a349c4c
|
||||
- filename: shared/telemetry/TelemetryUtils.js
|
||||
@@ -66,11 +66,11 @@ fileignoreconfig:
|
||||
- filename: screens/MainLayout.tsx
|
||||
checksum: 53ead79279c9609e42a8993db1a66bdb4649e1ae3b909d462b45b00c507c416e
|
||||
- filename: android/app/build.gradle
|
||||
checksum: 46a4054440361b25d13ecd75811bf239a6abb4830ce7f79b2b15ccd878758760
|
||||
checksum: 251ac12b49cf226d40931a02eb1b362a1d29e74cfe8f7a60ed128d66b0fea838
|
||||
- filename: .github/workflows/push-triggers.yml
|
||||
checksum: abc19ea38c8d7b79f15695d015709cc88a34a995181aaf12bc8344f940f3cbc4
|
||||
- filename: android/fastlane/Fastfile
|
||||
checksum: a25f155bcbbae7ab09563637c23771f7349738f12a6ddc8ae71c29c61ed535af
|
||||
checksum: a5e816489a80b0a7498f35b7a2919f2287d4307b660687c2a9c51412aa8eceb7
|
||||
- filename: .github/workflows/internal-build.yml
|
||||
checksum: e9b85cf0405d777faee9345269f6f9eb861ed205728dca63cf27a5db79c876a7
|
||||
- filename: assets/Issuer_search_clearing_button.svg
|
||||
@@ -82,7 +82,7 @@ fileignoreconfig:
|
||||
- filename: screens/Settings/BackupViaPassword.tsx
|
||||
checksum: d2a355356bcaf8f7ef3b53ba93710cec15fefd0fdf31efd779eebd2bfab61c19
|
||||
- filename: shared/api.ts
|
||||
checksum: 1c5d43058e8733a403e02d0b3fa5f56e11519efa4564e48c92b4491f4bd60508
|
||||
checksum: 05165e469008816a441126f8eed69d54c137d39c3c66e695f8710e8d33a9b038
|
||||
- filename: machines/backupWithEncryption.ts
|
||||
checksum: 038c12d30b2312fcbd9230a1c6ddb494d2e561fe0d09741335fa80ab67e2c550
|
||||
- filename: machines/backupWithEncryption.typegen.ts
|
||||
@@ -154,9 +154,9 @@ fileignoreconfig:
|
||||
- filename: injitest/README.md
|
||||
checksum: 82974a6b9363512472272245e9b433f92e63377e58ba306980876b745181a09c
|
||||
- filename: shared/VCMetadata.ts
|
||||
checksum: e93f988415bf91064e2cf5fbc09ff6c7226798baa5da721fa0715d5d0d6afddf
|
||||
checksum: 4c0f2acc58894e5a427e1317b38d04daff91f64d1e61d6ee2f246ee516ef97ca
|
||||
- filename: ios/Podfile.lock
|
||||
checksum: b8c97d58a88207bae811db83074388cff249a83055a1f92ea7dee2f59b7a32c9
|
||||
checksum: 43bd4742f2ba13357d8b9c44430bfa3cca0bf9bf8341984fd81174a929c85955
|
||||
- filename: components/BackupAndRestoreBannerNotification.tsx
|
||||
checksum: e465a9947727687d784d0cb9d8db1e28f765b0659bf4a3aa6d75643aa7b14102
|
||||
- filename: components/ActivityLogEvent.ts
|
||||
@@ -205,6 +205,10 @@ fileignoreconfig:
|
||||
checksum: 4b08ee05c9c6fe9154ed3cdc9d23324e4d9cfac8a7028686f2b22903424d4cef
|
||||
- filename: machines/VCItemMachine/VCItemGaurds.ts
|
||||
checksum: 4f32814fc26a0edaa54a42dbc9f9e1d899144eb059ac8da211d1738887871829
|
||||
- filename: machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts
|
||||
checksum: 797069975e6402527d506db8f4644586957b8437d9cf867eaed4b0000683c4f8
|
||||
- filename: ios/RNSecureKeystoreModule.m
|
||||
checksum: d3f70ec17eacdfc3ed64936be1fc14b83fd1004c9f861fb0827da8df28ac3bf9
|
||||
- filename: machines/VCItemMachine/VCItemServices.ts
|
||||
checksum: 51b4872a64abd76b124000358068c0b213d50fb131d735c122cd9a177cd8390c
|
||||
- filename: machines/VCItemMachine/VCItemActions.ts
|
||||
@@ -238,7 +242,7 @@ fileignoreconfig:
|
||||
- filename: machines/backupAndRestore/backupRestore.typegen.ts
|
||||
checksum: 64a8e42712083e0cbf0d6ce6b1139c62b59a14af17e4132d14f903d4d3bbe6b2
|
||||
- filename: machines/app.typegen.ts
|
||||
checksum: c310de13b855b22fe68e8fa954dc3a8a4728284c5904da99986a1f4eac8ea0be
|
||||
checksum: 6e194b5eb5f2ae91627bb1db832e0c9ab959f376593498df839db964fecf4258
|
||||
- filename: screens/Home/MyVcsTabMachine.typegen.ts
|
||||
checksum: 948efb4d61551e4f3cd9eb9913b927158daa5c3d16f49ad297e7cb63190bc023
|
||||
- filename: machines/IssuersMachine.typegen.ts
|
||||
@@ -246,7 +250,7 @@ fileignoreconfig:
|
||||
- filename: screens/Home/MyVcsTab.tsx
|
||||
checksum: ccf9ca41165b35628e026e7557f6fa7771f29dae8b09324d9af93c2c9b0eca6f
|
||||
- filename: machines/store.typegen.ts
|
||||
checksum: 46f3a7c2d15ed03fc70e27ecae5a12c128011c49913b35cdb8edba12b1a999db
|
||||
checksum: 077a1906c908daf79544f604632df36f3359075a7bc912e51ab5707cb178bf48
|
||||
- filename: machines/VerifiableCredential/VCMetaMachine/VCMetaMachine.ts
|
||||
checksum: 36244323200d1e965adb48205d359fba807a5a153f2fc3ce75fe34e8b1bdb01a
|
||||
- filename: machines/Issuers/IssuersMachine.ts
|
||||
@@ -256,18 +260,17 @@ fileignoreconfig:
|
||||
- filename: screens/Issuers/CredentialTypeSelectionScreen.tsx
|
||||
checksum: 144bbf59e86a89bf580ac7931645ca3eaed69a9409de36f6ce9f88a14091a9d3
|
||||
- filename: components/QrCodeOverlay.tsx
|
||||
checksum: 68758cbddafe575c7f53294327963112a0780d30fd23cd0b6bf82d7dcfd856fe
|
||||
checksum: 47220a4ebd8af702afe622a29b689f651eb23387bac1c623f241839beb413d25
|
||||
checksum: dacbce1d6cd7e702400897981f44b65288541f4a41ee970f1e6ac1146af150bb
|
||||
- filename: machines/Issuers/IssuersEvents.ts
|
||||
checksum: fd8c30e0cf43a784be883c9d79a3bff0d2bcd9075e937d225939040542998b10
|
||||
- filename: machines/Issuers/IssuersGuards.ts
|
||||
checksum: d87b6f4277c4be68f1884efa5c73e1b1d02a1afaefb276417b95a312f599578a
|
||||
checksum: 21783a057207ad04facdb4c71884f49b0230490def04158419d730e0cc60eb83
|
||||
- filename: machines/Issuers/IssuersActions.ts
|
||||
checksum: e865a33dcecfe185eff5ac06208f0f2e8ff6574f778e31da74d6ba74c67e285d
|
||||
checksum: 4414aa10588d2305293b1902982c5969895c858355e4b91d01dfaa8601c2dd62
|
||||
- filename: injitest/automation_trigger.sh
|
||||
checksum: f2f34839c99cb1b871dde17aed8508a071345d22738796e005ff709d2dab8644
|
||||
- filename: machines/Issuers/IssuersService.ts
|
||||
checksum: 1b54b0249488fc496e3582588c644034865fd50386757789baaaf1c3821464d5
|
||||
checksum: e3832dff27687abc28609d2b281e570b4b0017995b7cfb56627a6b96949c469a
|
||||
- filename: screens/Home/ViewVcModal.tsx
|
||||
checksum: cfb25d562185488432b76287c4ef93359c1c64d8e29f5755d4c0a726c1485442
|
||||
- filename: injitest/src/main/resources/TestData.json
|
||||
@@ -293,7 +296,7 @@ fileignoreconfig:
|
||||
- filename: machines/QrLogin/QrLoginMachine.ts
|
||||
checksum: f4234549baea9a7f69be98f52c30a04f2f6138f9b1f2b60a7b40f15f7e03345b
|
||||
- filename: machines/QrLogin/QrLoginServices.ts
|
||||
checksum: b20d0caa6d23078b4010ea5185f01270356422dd216edd7834b069cdedd3383d
|
||||
checksum: 0eee865344c9e15722bac2307b5dd6025fd809233b71456472609d578c9a365a
|
||||
- filename: machines/bleShare/scan/scanActions.ts
|
||||
checksum: e32e1be4a02f9247844771293e5fc285bd6e1bfd3f0451a6bbc5bd171931b6f2
|
||||
- filename: injitest/src/test/java/iosTestCases/ActivateVcTest.java
|
||||
@@ -303,7 +306,7 @@ fileignoreconfig:
|
||||
- filename: injitest/src/test/java/iosTestCases/VerifyHistoryTest.java
|
||||
checksum: 8a00278af4c8744c713c57328991bbca438eb5d5d89b492a7f5234c47362f44b
|
||||
- filename: machines/store.ts
|
||||
checksum: 93ffa32067d698ecc9b7c1eec3b58ce1b3bee44d53c0e7daded0467393e3f0d6
|
||||
checksum: 3fd2db0c41f8bd5f30ef922b856549cb5423997b2123c5364e643e47e5efd3cf
|
||||
- filename: components/BannerNotificationContainer.tsx
|
||||
checksum: 9e5b4a61b87e86666f0bee550d410df2b8576dfe5ec374de0ab139a468a234f7
|
||||
- filename: injitest/src/test/java/iosTestCases/PinVcTest.java
|
||||
@@ -323,32 +326,30 @@ fileignoreconfig:
|
||||
- filename: injitest/src/test/java/iosTestCases/VcDownloadAndVerifyUsingUinTest.java
|
||||
checksum: a6feabb768e2d97dfb0a1693f09d839686ce6be686523cf273b2d3ce614b34fd
|
||||
- filename: machines/Issuers/IssuersMachine.typegen.ts
|
||||
checksum: 08fd5e4eff836c219a0f16f6d4178a3511ec2581507076d3f9d32dcadbc01351
|
||||
checksum: 581e73b10471735aa6415590b0f01f6c9b503a196d3fca450a4c2a9e647487d3
|
||||
- filename: injitest/src/test/java/iosTestCases/DeletingVcTest.java
|
||||
checksum: 0816d4b9440da5384fd867d13555986d223124b9609280350226510a06ae96fa
|
||||
- filename: machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts
|
||||
checksum: fdc0c23a7107eb713d20f60fda675f9e9fe8ef29c981d798d90e581dfee340c8
|
||||
checksum: 8ac74d2e5c6de179e460b86899eb048ad4c5bd67abc3d28c015e92335b8afe24
|
||||
- filename: machines/VerifiableCredential/VCItemMachine/VCItemServices.ts
|
||||
checksum: 1ce38602f148388940eec172a5c9be83de7a600adcae0ba9e8ac27e5ebc44641
|
||||
checksum: 9ba47d5bda1f3ebec6b5f49b7f77ea6f22a62899c9c742964495926885c53766
|
||||
- filename: ios/RNPixelpassModule.m
|
||||
checksum: c91348eceec5edbffa03ba03f3f52a8e90ff7f942816c9609080d1647052fd66
|
||||
- filename: android/app/src/main/java/io/mosip/residentapp/InjiVciClientModule.java
|
||||
checksum: 17f55840bab193bc353034445ba4fce53e1ce466e95f616c15a1351f8d2f23bc
|
||||
- filename: ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved
|
||||
checksum: b168940c6b487dc96fd22f564f2e187dae46f4fa5e4a64cf81c4d810b1c1ae78
|
||||
checksum: 2d0a5899777bff2ff8412e4931e0b1087e44f63047a2e3525e82eda0dfe8791c
|
||||
- filename: injitest/src/main/resources/Vids.json
|
||||
checksum: 8bcffed7a6dd565ae695e1b29de0655e10bd5c5420af2718defd593a687b8817
|
||||
- filename: injitest/src/main/java/inji/utils/UpdateNetworkSettings.java
|
||||
checksum: e249ce3e6b7f47abc183fe5a3637bb39ccb06900ef75b9b2f08426d1535e22aa
|
||||
- filename: App.tsx
|
||||
checksum: d16d4a40b246abe25a5d2da7ec65163b5756fe8ba9390608a7fc7f8e721b2ed1
|
||||
- filename: machines/VerifiableCredential/VCItemMachine/VCItemServices.ts
|
||||
checksum: 46f5b7ad6e6dcd9de9f9872c79d2c07addcd228324a43cca18525f6b1f4ff7cb
|
||||
- filename: injitest/src/test/java/iosTestCases/ShareVcTest.java
|
||||
checksum: 1cf9b61d3fcea9b63b2b9f7dffe9b5a1848e196c39f77790b6c9d83f201c6197
|
||||
- filename: android/app/src/main/java/io/mosip/residentapp/RNSecureKeystoreModule.java
|
||||
checksum: f307f8273f72ec70b991baf799ae71f93c785c76e3e15847004f567558340e32
|
||||
checksum: db9d36d21607f247e2791d0ade02f2868700c432333636b0ae5a542649b69f3a
|
||||
- filename: injitest/src/test/java/androidTestCases/ShareVcTest.java
|
||||
checksum: a7e3e579b6ac05f95932638b61272142774d0690c13717c890e87374782ea509
|
||||
- filename: ios/RNPixelpassModule.m
|
||||
@@ -364,7 +365,7 @@ fileignoreconfig:
|
||||
- filename: ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved
|
||||
checksum: b168940c6b487dc96fd22f564f2e187dae46f4fa5e4a64cf81c4d810b1c1ae78
|
||||
- filename: ios/Inji.xcodeproj/project.pbxproj
|
||||
checksum: 4359976ed4d1ac3206d76b87d3458d070027199c8569ba123436c4b5343aba74
|
||||
checksum: 6e83472f832f71f75aa82ed06eb677d865195755074144e4bf832d6adb30e959
|
||||
- filename: screens/Settings/ReceivedCardsModal.tsx
|
||||
checksum: 6dee9153a61009b0252d294154c88d5e1b241a517c76e930b391a39d7bc52392
|
||||
- filename: components/FaceScanner/FaceCompare.tsx
|
||||
@@ -372,5 +373,21 @@ fileignoreconfig:
|
||||
- filename: components/FaceScanner/LivenessDetection.tsx
|
||||
checksum: d4140a42ee9ca0f7c90e490f762d181a723fd9dd20db891cbbe53bfbd8f81632
|
||||
- filename: machines/VerifiableCredential/VCItemMachine/VCItemActions.ts
|
||||
checksum: 9b68ccc45681459d164197f73a1875e6f8bdf473acede18c811f4a784fca00e0
|
||||
checksum: 037ddde01479c24f954e87be5088dd0f449f0379bd8bb0b34605ca40be0f3f6c
|
||||
- filename: machines/app.ts
|
||||
checksum: 5da59bb384d04e29c7745d773108903fa144275c57edc1aca1898fcae7baea84
|
||||
- filename: shared/cryptoutil/signFormatConverter.ts
|
||||
checksum: 3a0a03f4e719194858a61810d12180cdc407c53f04ce1455360d5d535556b7ac
|
||||
- filename: shared/CloudBackupAndRestoreUtils.ts
|
||||
checksum: dd98da030f7b4decb62ab2466604a2a28e6c5a1866eae9a5634b090b742497f6
|
||||
- filename: ios/RNSecureKeystoreModule.swift
|
||||
checksum: 98784857098e15aca851c6eb79e48016887408ff84bb82afafdc03b38ab2e338
|
||||
- filename: machines/Issuers/IssuersModel.ts
|
||||
checksum: fe4084d1f5dbf89e71cc3fbe9f845d3f2c9bb32901bd5b95a8addcb13257b414
|
||||
- filename: android/app/src/main/java/io/mosip/residentapp/InjiOpenId4VPModule.java
|
||||
checksum: 6b315164dca5de95c11e0dc8cbb480207b19c312b1c9135adc39ef74a1ff7e35
|
||||
- filename: screens/Scan/SendVPScreenController.ts
|
||||
checksum: f898ac7f1ecfa1df17e33b327d675f57debf2d5bd56052fc047dd03577354590
|
||||
- filename: machines/openID4VP/openID4VPMachine.typegen.ts
|
||||
checksum: 986c2743bba3554068c1cf52e6f3e7b1762699e62dab6f971a6f796e2ea3eb81
|
||||
version: ""
|
||||
|
||||
@@ -262,12 +262,14 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("io.mosip:inji-openID4VP:0.1.0-SNAPSHOT")
|
||||
implementation("com.facebook.react:react-android")
|
||||
implementation 'com.facebook.soloader:soloader:0.10.1+'
|
||||
implementation("io.mosip:pixelpass:0.2.1")
|
||||
implementation("io.mosip:secure-keystore:0.3.0-SNAPSHOT")
|
||||
implementation("io.mosip:tuvali:0.5.1")
|
||||
implementation("io.mosip:inji-vci-client:0.2.0-SNAPSHOT")
|
||||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
implementation("io.mosip:vcverifier-aar:1.2.0-SNAPSHOT")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package io.mosip.residentapp;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.mosip.openID4VP.OpenID4VP;
|
||||
import io.mosip.openID4VP.dto.VPResponseMetadata;
|
||||
import io.mosip.openID4VP.dto.Verifier;
|
||||
|
||||
public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
|
||||
private OpenID4VP openID4VP;
|
||||
private Gson gson;
|
||||
|
||||
InjiOpenID4VPModule(@Nullable ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "InjiOpenID4VP";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void init(String appId) {
|
||||
Log.d("InjiOpenID4VPModule", "Initializing InjiOpenID4VPModule with " + appId);
|
||||
openID4VP = new OpenID4VP(appId);
|
||||
gson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void authenticateVerifier(String encodedAuthorizationRequest, ReadableArray trustedVerifiers,
|
||||
Promise promise) {
|
||||
try {
|
||||
Map<String, String> authenticationResponse = openID4VP.authenticateVerifier(encodedAuthorizationRequest,
|
||||
convertReadableArrayToVerifierArray(trustedVerifiers));
|
||||
String authenticationResponseAsJson = gson.toJson(authenticationResponse, Map.class);
|
||||
promise.resolve(authenticationResponseAsJson);
|
||||
} catch (Exception exception) {
|
||||
promise.reject(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void constructVerifiablePresentationToken(ReadableMap selectedVCs, Promise promise) {
|
||||
try {
|
||||
Map<String, List<String>> selectedVCsMap = new HashMap<>();
|
||||
|
||||
ReadableMapKeySetIterator iterator = selectedVCs.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
ReadableArray valueArray = selectedVCs.getArray(key);
|
||||
|
||||
List<String> valueList = new ArrayList<>();
|
||||
for (int i = 0; i < valueArray.size(); i++) {
|
||||
valueList.add(valueArray.getString(i));
|
||||
}
|
||||
|
||||
selectedVCsMap.put(key, valueList);
|
||||
}
|
||||
String vpToken = openID4VP.constructVerifiablePresentationToken(selectedVCsMap);
|
||||
promise.resolve(vpToken);
|
||||
} catch (Exception exception) {
|
||||
promise.reject(exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void shareVerifiablePresentation(ReadableMap vpResponseMetadata, Promise promise) {
|
||||
try {
|
||||
VPResponseMetadata vpResMetadata = getVPResponseMetadata(vpResponseMetadata);
|
||||
String response = openID4VP.shareVerifiablePresentation(vpResMetadata);
|
||||
promise.resolve(response);
|
||||
} catch (Exception exception) {
|
||||
promise.reject(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private VPResponseMetadata getVPResponseMetadata(ReadableMap vpResponseMetadata) throws IllegalArgumentException {
|
||||
String jws = vpResponseMetadata.getString("jws");
|
||||
String signatureAlgorithm = vpResponseMetadata.getString("signatureAlgorithm");
|
||||
String publicKey = vpResponseMetadata.getString("publicKey");
|
||||
String domain = vpResponseMetadata.getString("domain");
|
||||
|
||||
return new VPResponseMetadata(jws, signatureAlgorithm, publicKey, domain);
|
||||
}
|
||||
|
||||
public List<Verifier> convertReadableArrayToVerifierArray(ReadableArray readableArray) {
|
||||
List<Verifier> trustedVerifiersList = new ArrayList<>();
|
||||
for (int i = 0; i < readableArray.size(); i++) {
|
||||
ReadableMap verifierMap = readableArray.getMap(i);
|
||||
String clientId = verifierMap.getString("client_id");
|
||||
ReadableArray responseUris = verifierMap.getArray("response_uris");
|
||||
List<String> responseUriList = new ArrayList<>();
|
||||
for (int j = 0; j < responseUris.size(); j++) {
|
||||
responseUriList.add(responseUris.getString(j));
|
||||
}
|
||||
trustedVerifiersList.add(new Verifier(clientId, responseUriList));
|
||||
}
|
||||
return trustedVerifiersList;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ public class InjiPackage implements ReactPackage {
|
||||
modules.add(new RNWalletModule(new RNEventEmitter(reactApplicationContext), new Wallet(reactApplicationContext), reactApplicationContext));
|
||||
modules.add(new RNVerifierModule(new RNEventEmitter(reactApplicationContext), new Verifier(reactApplicationContext), reactApplicationContext));
|
||||
modules.add(new RNQrLoginIntentModule(reactApplicationContext));
|
||||
modules.add(new InjiOpenID4VPModule(reactApplicationContext));
|
||||
modules.add(new RNVCVerifierModule(reactApplicationContext));
|
||||
return modules;
|
||||
}
|
||||
|
||||
11
assets/Selected_Check_Box.svg
Normal file
11
assets/Selected_Check_Box.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<g id="Group_58148" data-name="Group 58148" transform="translate(-0.114 -0.21)">
|
||||
<g id="Group_57140" data-name="Group 57140" transform="translate(-0.095)">
|
||||
<g id="Rectangle_7173" data-name="Rectangle 7173" transform="translate(0.209 0.21)" fill="#f2801d" stroke="#f2801d" stroke-width="2">
|
||||
<rect width="18" height="18" rx="4" stroke="none"/>
|
||||
<rect x="1" y="1" width="16" height="16" rx="3" fill="none"/>
|
||||
</g>
|
||||
<path id="check_circle_FILL0_wght400_GRAD0_opsz48" d="M9.739,14.057,15.4,8.414l-.921-.9L9.739,12.242l-2.4-2.393-.9.9Z" transform="translate(-1.615 -1.732)" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 804 B |
@@ -61,10 +61,7 @@ export class ActivityLog {
|
||||
|
||||
export function getActionText(activity: ActivityLog, t, wellknown: Object) {
|
||||
if (!!activity.credentialConfigurationId) {
|
||||
const cardType = getIdType(
|
||||
wellknown,
|
||||
activity.credentialConfigurationId,
|
||||
);
|
||||
const cardType = getIdType(wellknown, activity.credentialConfigurationId);
|
||||
return `${t(activity.type, {idType: cardType, id: activity.id})}`;
|
||||
}
|
||||
return `${t(activity.type, {idType: '', id: activity.id})}`;
|
||||
|
||||
@@ -41,7 +41,7 @@ export const FaceScanner: React.FC<FaceScannerProps> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isActive = useSelector(appService, selectIsActive);
|
||||
|
||||
const machine = useRef(createFaceScannerMachine(props.vcImage));
|
||||
const machine = useRef(createFaceScannerMachine(props.vcImages));
|
||||
const service = useInterpret(machine.current);
|
||||
|
||||
const [cameraType, setCameraType] = useState(Camera.Constants.Type.front);
|
||||
@@ -118,7 +118,7 @@ export const FaceScanner: React.FC<FaceScannerProps> = props => {
|
||||
|
||||
const result = await cropEyeAreaFromFace(
|
||||
picArray,
|
||||
props.vcImage,
|
||||
props.vcImages[0],
|
||||
faceToCompare,
|
||||
);
|
||||
return result ? props.onValid() : props.onInvalid();
|
||||
@@ -209,7 +209,7 @@ export const FaceScanner: React.FC<FaceScannerProps> = props => {
|
||||
}
|
||||
};
|
||||
interface FaceScannerProps {
|
||||
vcImage: string;
|
||||
vcImages: string[];
|
||||
onValid: () => void;
|
||||
onInvalid: () => void;
|
||||
isLiveness: boolean;
|
||||
|
||||
@@ -36,14 +36,18 @@ export const VCCardView: React.FC<VCItemProps> = props => {
|
||||
const [wellknown, setWellknown] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const {issuer, wellKnown, credentialConfigurationId, vcMetadata: {format}} =
|
||||
verifiableCredentialData;
|
||||
const {
|
||||
issuer,
|
||||
wellKnown,
|
||||
credentialConfigurationId,
|
||||
vcMetadata: {format},
|
||||
} = verifiableCredentialData;
|
||||
if (wellKnown) {
|
||||
getCredentialIssuersWellKnownConfig(
|
||||
issuer,
|
||||
CARD_VIEW_DEFAULT_FIELDS,
|
||||
credentialConfigurationId,
|
||||
format
|
||||
format,
|
||||
)
|
||||
.then(response => {
|
||||
setWellknown(response.matchingCredentialIssuerMetadata);
|
||||
@@ -59,7 +63,7 @@ export const VCCardView: React.FC<VCItemProps> = props => {
|
||||
}, [verifiableCredentialData?.wellKnown]);
|
||||
|
||||
if (!isVCLoaded(controller.credential, fields)) {
|
||||
return <VCCardSkeleton />;
|
||||
return <VCCardSkeleton flow={props.flow} />;
|
||||
}
|
||||
|
||||
const CardViewContent = props => (
|
||||
|
||||
@@ -27,21 +27,37 @@ import {useCopilot} from 'react-native-copilot';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export const VCCardViewContent: React.FC<VCItemContentProps> = props => {
|
||||
const isVCSelectable = props.selectable && (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={
|
||||
<Icon name="check-circle" type="material" color={Theme.Colors.Icon} />
|
||||
}
|
||||
uncheckedIcon={
|
||||
<Icon
|
||||
name="radio-button-unchecked"
|
||||
color={Theme.Colors.uncheckedIcon}
|
||||
/>
|
||||
}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
);
|
||||
const checkBoxForVCSharing = props.selectable &&
|
||||
props.flow !== VCItemContainerFlowType.OPENID4VP && (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={
|
||||
<Icon name="check-circle" type="material" color={Theme.Colors.Icon} />
|
||||
}
|
||||
uncheckedIcon={
|
||||
<Icon
|
||||
name="radio-button-unchecked"
|
||||
color={Theme.Colors.uncheckedIcon}
|
||||
/>
|
||||
}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
);
|
||||
const checkBoxForVPSharing = props.selectable &&
|
||||
props.flow === VCItemContainerFlowType.OPENID4VP && (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={SvgImage.selectedCheckBox()}
|
||||
uncheckedIcon={
|
||||
<Icon
|
||||
name="check-box-outline-blank"
|
||||
color={Theme.Colors.uncheckedIcon}
|
||||
size={22}
|
||||
/>
|
||||
}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
);
|
||||
const issuerLogo = props.verifiableCredentialData.issuerLogo;
|
||||
const faceImage = props.verifiableCredentialData.face;
|
||||
const {start} = useCopilot();
|
||||
@@ -63,6 +79,7 @@ export const VCCardViewContent: React.FC<VCItemContentProps> = props => {
|
||||
: undefined
|
||||
}>
|
||||
<Row crossAlign="center" padding="3 0 0 3">
|
||||
{checkBoxForVPSharing}
|
||||
{VcItemContainerProfileImage(props)}
|
||||
<Column fill align={'space-around'} margin="0 10 0 10">
|
||||
<VCItemFieldValue
|
||||
@@ -115,7 +132,7 @@ export const VCCardViewContent: React.FC<VCItemContentProps> = props => {
|
||||
</Pressable>
|
||||
</>
|
||||
)}
|
||||
{isVCSelectable}
|
||||
{checkBoxForVCSharing}
|
||||
</Row>
|
||||
|
||||
<WalletBinding service={props.service} vcMetadata={props.vcMetadata} />
|
||||
|
||||
@@ -4,9 +4,52 @@ import {ImageBackground, View} from 'react-native';
|
||||
import React from 'react';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import ShimmerPlaceholder from 'react-native-shimmer-placeholder';
|
||||
import {VCItemContainerFlowType, VCShareFlowType} from '../../../shared/Utils';
|
||||
|
||||
export const VCCardSkeleton = () => {
|
||||
return (
|
||||
export const VCCardSkeleton: React.FC<VCCardSkeletonProps> = props => {
|
||||
return props.flow === VCItemContainerFlowType.OPENID4VP ? (
|
||||
<View style={Theme.Styles.closeCardBgContainer}>
|
||||
<ImageBackground
|
||||
source={Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
style={Theme.Styles.vertloadingContainer}>
|
||||
<Column>
|
||||
<Row crossAlign="center">
|
||||
<ShimmerPlaceholder
|
||||
LinearGradient={LinearGradient}
|
||||
width={22}
|
||||
height={22}
|
||||
style={{borderRadius: 5, marginRight: 20, marginLeft: 15}}
|
||||
/>
|
||||
<ShimmerPlaceholder
|
||||
LinearGradient={LinearGradient}
|
||||
width={40}
|
||||
height={53}
|
||||
style={{borderRadius: 5}}
|
||||
/>
|
||||
<Column fill align={'space-around'} margin={'0 10 0 10'}>
|
||||
<ShimmerPlaceholder
|
||||
LinearGradient={LinearGradient}
|
||||
width={100}
|
||||
style={{borderRadius: 5, marginTop: 5}}
|
||||
/>
|
||||
<ShimmerPlaceholder
|
||||
LinearGradient={LinearGradient}
|
||||
width={50}
|
||||
style={{borderRadius: 5, marginTop: 5}}
|
||||
/>
|
||||
</Column>
|
||||
<ShimmerPlaceholder
|
||||
LinearGradient={LinearGradient}
|
||||
width={35}
|
||||
height={35}
|
||||
style={{borderRadius: 5}}
|
||||
/>
|
||||
</Row>
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
</View>
|
||||
) : (
|
||||
<View style={Theme.Styles.closeCardBgContainer}>
|
||||
<ImageBackground
|
||||
source={Theme.CloseCard}
|
||||
@@ -50,3 +93,7 @@ export const VCCardSkeleton = () => {
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export interface VCCardSkeletonProps {
|
||||
flow?: string;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import ColoredInfo from '../../assets/Colored_Info.svg';
|
||||
import Info from '../../assets/Info.svg';
|
||||
import Search from '../../assets/Search.svg';
|
||||
import CloudUploadDoneIcon from '../../assets/Cloud_Upload_Done_Icon.svg';
|
||||
import SelectedCheckBox from '../../assets/Selected_Check_Box.svg';
|
||||
|
||||
export class SvgImage {
|
||||
static MosipLogo(props: LogoProps) {
|
||||
@@ -64,6 +65,10 @@ export class SvgImage {
|
||||
return <KebabIcon {...testIDProps(testId)} />;
|
||||
}
|
||||
|
||||
static selectedCheckBox() {
|
||||
return <SelectedCheckBox />;
|
||||
}
|
||||
|
||||
static walletActivatedIcon() {
|
||||
return (
|
||||
<WalletActivatedIcon
|
||||
|
||||
@@ -114,6 +114,7 @@ export const DefaultTheme = {
|
||||
GrayText: Colors.GrayText,
|
||||
errorGrayText: Colors.mediumDarkGrey,
|
||||
gradientBtn: ['#F59B4B', '#E86E04'],
|
||||
selectIDTextGradient: ['#F5F5F5', '#FFFFFF'],
|
||||
dotColor: Colors.dorColor,
|
||||
plainText: Colors.plainText,
|
||||
IconBackground: Colors.LightOrange,
|
||||
@@ -1592,6 +1593,29 @@ export const DefaultTheme = {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
}),
|
||||
VPSharingStyles: StyleSheet.create({
|
||||
purposeContainer: {
|
||||
backgroundColor: Colors.TimeoutHintBoxColor,
|
||||
borderColor: Colors.TimeoutHintBoxBorder,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
},
|
||||
purposeText: {
|
||||
fontSize: 13,
|
||||
position: 'relative',
|
||||
fontFamily: 'Inter_500Medium',
|
||||
},
|
||||
cardsSelectedText: {
|
||||
fontFamily: 'Inter_500Medium',
|
||||
color: '#000000',
|
||||
fontSize: 14,
|
||||
},
|
||||
selectIDText: {
|
||||
position: 'relative',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 16,
|
||||
},
|
||||
}),
|
||||
CameraDisabledStyles: StyleSheet.create({
|
||||
container: {
|
||||
position: 'absolute',
|
||||
|
||||
@@ -117,6 +117,7 @@ export const PurpleTheme = {
|
||||
DefaultToggle: Colors.LightPurple,
|
||||
GrayText: Colors.GrayText,
|
||||
gradientBtn: Colors.GradientColors,
|
||||
selectIDTextGradient: ['#F5F5F5', '#FFFFFF'],
|
||||
dotColor: Colors.dorColor,
|
||||
plainText: Colors.plainText,
|
||||
IconBackground: Colors.LightPurple,
|
||||
@@ -1593,6 +1594,29 @@ export const PurpleTheme = {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
}),
|
||||
VPSharingStyles: StyleSheet.create({
|
||||
purposeContainer: {
|
||||
backgroundColor: Colors.TimeoutHintBoxColor,
|
||||
borderColor: Colors.TimeoutHintBoxBorder,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
},
|
||||
purposeText: {
|
||||
fontSize: 13,
|
||||
position: 'relative',
|
||||
fontFamily: 'Inter_500Medium',
|
||||
},
|
||||
cardsSelectedText: {
|
||||
fontFamily: 'Inter_500Medium',
|
||||
color: '#000000',
|
||||
fontSize: 14,
|
||||
},
|
||||
selectIDText: {
|
||||
position: 'relative',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 16,
|
||||
},
|
||||
}),
|
||||
CameraDisabledStyles: StyleSheet.create({
|
||||
container: {
|
||||
position: 'absolute',
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 60;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
1E6875E92CA554E80086D870 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 1E6875E82CA554E80086D870 /* OpenID4VP */; };
|
||||
1E6875EB2CA554FD0086D870 /* RNOpenID4VPModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */; };
|
||||
1E6875ED2CA5550F0086D870 /* RNOpenID4VPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */; };
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||
73295844242A4AD3AA52D0BE /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98B96A488E54CBDB286B26F /* noop-file.swift */; };
|
||||
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Inji.a */; };
|
||||
@@ -61,6 +64,8 @@
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Inji/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Inji/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Inji/main.m; sourceTree = "<group>"; };
|
||||
1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNOpenID4VPModule.m; sourceTree = "<group>"; };
|
||||
1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNOpenID4VPModule.swift; sourceTree = "<group>"; };
|
||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Inji.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Inji.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2E3173556A471DD304B334 /* Pods-Inji.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Inji.debug.xcconfig"; path = "Target Support Files/Pods-Inji/Pods-Inji.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-Inji.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Inji.release.xcconfig"; path = "Target Support Files/Pods-Inji/Pods-Inji.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -110,6 +115,7 @@
|
||||
files = (
|
||||
E8AF2B9D2C0D93E800E775F6 /* VCIClient in Frameworks */,
|
||||
9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */,
|
||||
1E6875E92CA554E80086D870 /* OpenID4VP in Frameworks */,
|
||||
9CE34B1F2BFE03E4001AF414 /* pixelpass in Frameworks */,
|
||||
9C7CDF492C89802C00243A9A /* securekeystore in Frameworks */,
|
||||
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */,
|
||||
@@ -144,6 +150,8 @@
|
||||
D98B96A488E54CBDB286B26F /* noop-file.swift */,
|
||||
E86208142C0335C5007C3E24 /* RNVCIClientModule.swift */,
|
||||
E86208162C0335EC007C3E24 /* RNVCIClientModule.m */,
|
||||
1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */,
|
||||
1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */,
|
||||
);
|
||||
name = Inji;
|
||||
sourceTree = "<group>";
|
||||
@@ -276,6 +284,7 @@
|
||||
E8AF2B9C2C0D93E800E775F6 /* VCIClient */,
|
||||
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */,
|
||||
9C7CDF482C89802C00243A9A /* securekeystore */,
|
||||
1E6875E82CA554E80086D870 /* OpenID4VP */,
|
||||
);
|
||||
productName = Inji;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* Inji.app */;
|
||||
@@ -308,6 +317,7 @@
|
||||
E86208242C039895007C3E24 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
|
||||
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */,
|
||||
9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */,
|
||||
1E6875E72CA554E80086D870 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */,
|
||||
);
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -486,11 +496,13 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1E6875ED2CA5550F0086D870 /* RNOpenID4VPModule.swift in Sources */,
|
||||
9C48504F2C3E59B5002ECBD5 /* RNVersionModule.swift in Sources */,
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
9C0E86BB2BEE36C300E9F9F6 /* RNPixelpassModule.m in Sources */,
|
||||
E86208152C0335C5007C3E24 /* RNVCIClientModule.swift in Sources */,
|
||||
9C0E86B52BEE357A00E9F9F6 /* RNPixelpassModule.swift in Sources */,
|
||||
1E6875EB2CA554FD0086D870 /* RNOpenID4VPModule.m in Sources */,
|
||||
9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
9C4850502C3E59B5002ECBD5 /* RNEventMapper.swift in Sources */,
|
||||
@@ -732,6 +744,14 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
1E6875E72CA554E80086D870 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/mosip/inji-openid4vp-ios-swift";
|
||||
requirement = {
|
||||
branch = develop;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/mosip/tuvali-ios-swift/";
|
||||
@@ -767,6 +787,11 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
1E6875E82CA554E80086D870 /* OpenID4VP */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 1E6875E72CA554E80086D870 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */;
|
||||
productName = OpenID4VP;
|
||||
};
|
||||
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */;
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
"version" : "5.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "inji-openid4vp-ios-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/mosip/inji-openid4vp-ios-swift",
|
||||
"state" : {
|
||||
"branch" : "develop",
|
||||
"revision" : "c7bc1e7f6c98c8d857c88062aabe55453482b82c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "inji-vci-client-ios-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
23
ios/RNOpenID4VPModule.m
Normal file
23
ios/RNOpenID4VPModule.m
Normal file
@@ -0,0 +1,23 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "React/RCTBridgeModule.h"
|
||||
|
||||
@interface RCT_EXTERN_MODULE(InjiOpenID4VP, NSObject)
|
||||
|
||||
RCT_EXTERN_METHOD(init:(NSString *)appId)
|
||||
|
||||
RCT_EXTERN_METHOD(authenticateVerifier:(NSString *)encodedAuthorizationRequest
|
||||
trustedVerifierJSON:(id)trustedVerifierJSON
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(constructVerifiablePresentationToken:(id)credentialsMap
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(shareVerifiablePresentation:(id)vpResponseMetadata
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(requiresMainQueueSetup:(BOOL))
|
||||
|
||||
@end
|
||||
113
ios/RNOpenID4VPModule.swift
Normal file
113
ios/RNOpenID4VPModule.swift
Normal file
@@ -0,0 +1,113 @@
|
||||
import Foundation
|
||||
import OpenID4VP
|
||||
import React
|
||||
|
||||
@objc(InjiOpenID4VP)
|
||||
class RNOpenId4VpModule: NSObject, RCTBridgeModule {
|
||||
|
||||
private var openID4VP: OpenID4VP?
|
||||
|
||||
static func moduleName() -> String {
|
||||
return "InjiOpenID4VP"
|
||||
}
|
||||
|
||||
@objc
|
||||
func `init`(_ appId: String) {
|
||||
openID4VP = OpenID4VP(traceabilityId: appId)
|
||||
}
|
||||
|
||||
@objc
|
||||
func authenticateVerifier(_ encodedAuthorizationRequest: String,
|
||||
trustedVerifierJSON: AnyObject,
|
||||
resolver resolve: @escaping RCTPromiseResolveBlock,
|
||||
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
do {
|
||||
guard let verifierMeta = trustedVerifierJSON as? [[String:Any]] else {
|
||||
reject("OPENID4VP", "Invalid verifier meta format", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let trustedVerifiersList: [Verifier] = try verifierMeta.map { verifierDict in
|
||||
guard let clientId = verifierDict["client_id"] as? String,
|
||||
let responseUris = verifierDict["response_uris"] as? [String] else {
|
||||
throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid Verifier data"])
|
||||
}
|
||||
return Verifier(clientId: clientId, responseUris: responseUris)
|
||||
}
|
||||
|
||||
let authenticationResponse: AuthenticationResponse = try await openID4VP!.authenticateVerifier(encodedAuthorizationRequest: encodedAuthorizationRequest, trustedVerifierJSON: trustedVerifiersList)
|
||||
let response = try toJsonString(authenticationResponse.response)
|
||||
resolve(response)
|
||||
} catch {
|
||||
reject("OPENID4VP", "Unable to authenticate the Verifier", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func constructVerifiablePresentationToken(_ credentialsMap: AnyObject, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
do {
|
||||
guard let credentialsMap = credentialsMap as? [String:[String]] else {
|
||||
reject("OPENID4VP", "Invalid credentials map format", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let response = try await openID4VP?.constructVerifiablePresentationToken(credentialsMap: credentialsMap)
|
||||
resolve(response)
|
||||
|
||||
} catch {
|
||||
reject("OPENID4VP","Failed to construct verifiable presentation",error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func shareVerifiablePresentation(_ vpResponseMetadata: AnyObject, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
do {
|
||||
guard let vpResponse = vpResponseMetadata as? [String:String] else {
|
||||
reject("OPENID4VP", "Invalid vp response meta format", nil)
|
||||
return
|
||||
}
|
||||
|
||||
guard let jws = vpResponse["jws"] as String?,
|
||||
let signatureAlgorithm = vpResponse["signatureAlgorithm"] as String?,
|
||||
let publicKey = vpResponse["publicKey"] as String?,
|
||||
let domain = vpResponse["domain"] as String?
|
||||
else {
|
||||
reject("OPENID4VP", "Invalid vp response metat", nil)
|
||||
return
|
||||
}
|
||||
|
||||
let vpResponseMeta = VPResponseMetadata(jws: jws, signatureAlgorithm: signatureAlgorithm, publicKey: publicKey, domain: domain)
|
||||
|
||||
let response = try await openID4VP?.shareVerifiablePresentation(vpResponseMetadata: vpResponseMeta)
|
||||
|
||||
resolve(response)
|
||||
} catch {
|
||||
reject("OPENID4VP","Failed to send verifiable presentation",error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toJsonString(_ jsonObject: Any) throws -> String {
|
||||
guard let jsonDict = jsonObject as? [String: String] else {
|
||||
throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid JSON object type"])
|
||||
}
|
||||
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: jsonDict, options: [])
|
||||
guard let jsonString = String(data: jsonData, encoding: .utf8) else {
|
||||
throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to convert data to string"])
|
||||
}
|
||||
|
||||
return jsonString
|
||||
}
|
||||
|
||||
@objc
|
||||
static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -787,6 +787,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "الطالب",
|
||||
"cardsSelected": "البطاقات المختارة",
|
||||
"cardSelected": "card selected",
|
||||
"unCheck": "البطاقة مختارة",
|
||||
"checkAll": "تحقق من الكل",
|
||||
"consentDialog": {
|
||||
"title": "الموافقة مطلوبة",
|
||||
"message": "نحن نطلب موافقتك على مشاركة بيانات الاعتماد الخاصة بك التي يمكن التحقق منها. وهذا سيمكننا من التحقق من هويتك وتلبية طلبات الخدمة الخاصة بك. اختر \"نعم، متابعة\" للموافقة أو \"رفض\" إذا كنت لا ترغب في المشاركة.",
|
||||
"confirmButton": "نعم، تابع",
|
||||
"cancelButton": "انخفاض"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "هل أنت متأكد؟",
|
||||
"message": "سيؤدي رفض الموافقة إلى منعك من مشاركة بيانات اعتمادك التي يمكن التحقق منها.",
|
||||
"confirmButton": "نعم، تابع",
|
||||
"cancelButton": "عُد"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "لم يتم العثور على بيانات اعتماد مطابقة!",
|
||||
"message": "أعد محاولة المشاركة بعد تنزيل بيانات الاعتماد."
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "حدث خطأ!",
|
||||
"message": "لم يتم التعرف على المدقق. يرجى الحصول على رمز QR صالح من أداة التحقق."
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "حدث خطأ!",
|
||||
"message": "تم اكتشاف عدم تطابق في بيانات الاعتماد. تأكد من أنك قمت باختيار الخيار الصحيح ثم حاول مرة أخرى."
|
||||
},
|
||||
"genericError": {
|
||||
"title": "أُووبس! حدث خطأ.",
|
||||
"message": "بسبب خطأ فني، لا يمكننا مشاركة البطاقة. انقر فوق إعادة المحاولة لإعادة المشاركة أو انقر فوق الصفحة الرئيسية للخروج من عملية المشاركة."
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "رمز الاستجابة السريعة غير صالح",
|
||||
"message": "رمز الاستجابة السريعة غير صالح لأن بعض المعلومات المطلوبة مفقودة. يرجى مطالبة المدقق بتقديم رمز QR صالح لمشاركة بيانات الاعتماد الخاصة بك."
|
||||
},
|
||||
"noImage": {
|
||||
"title": "حدث خطأ!",
|
||||
"message": "يتطلب التحقق من الوجه صورة في بيانات الاعتماد المحددة. الرجاء استخدام خيار المشاركة أو تحديد بيانات اعتماد تتضمن صورة."
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "التحقق من الوجه",
|
||||
"status": {
|
||||
|
||||
@@ -795,6 +795,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "Requester",
|
||||
"cardsSelected": "cards selected",
|
||||
"cardSelected": "card selected",
|
||||
"unCheck": "Uncheck",
|
||||
"checkAll": "Check All",
|
||||
"consentDialog": {
|
||||
"title": "Consent Required",
|
||||
"message": "We require your consent to share your verifiable credentials. This will enable us to verify your identity and fulfil your service requests. Choose \"Yes, Proceed\" to consent or \"Decline\" if you do not wish to share.",
|
||||
"confirmButton": "Yes, Proceed",
|
||||
"cancelButton": "Decline"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "Are you sure?",
|
||||
"message": "Declining the consent will prevent you from sharing your verifiable credentials.",
|
||||
"confirmButton": "Yes, Proceed",
|
||||
"cancelButton": "Go Back"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "No matching credentials found!",
|
||||
"message": "Retry sharing after downloading the credentials."
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "An Error Occured!",
|
||||
"message": "The verifier is not recognized. Please obtain a valid QR code from the verifier."
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "An Error Occured!",
|
||||
"message": "Credential mismatch detected. Ensure you have selected the right one and try again."
|
||||
},
|
||||
"genericError": {
|
||||
"title": "Oops! An Error Occured.",
|
||||
"message": "Due to technical error, we are unable to share the card. Click on retry to re-share or click on Home to exit the sharing process."
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "Invalid QR code",
|
||||
"message": "The QR code is invalid because some required information is missing. Please ask the verifier to provide a valid QR code to share your credentials."
|
||||
},
|
||||
"noImage": {
|
||||
"title": "An Error Occured!",
|
||||
"message": "Face verification requires a photo in the selected credential(s). Please use the Share option or select a credential that includes an image."
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "Face Verification",
|
||||
"status": {
|
||||
@@ -948,4 +993,4 @@
|
||||
"keyManagementTitle": "Key Management",
|
||||
"keyManagementDesc": "Select the key generation method that aligns with your preference, putting you in control of your credential security.\nDrag and arrange the keys, with the top one being your highest priority."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -786,6 +786,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "Humihiling",
|
||||
"cardsSelected": "mga card na napili",
|
||||
"cardSelected": "card pinili",
|
||||
"unCheck": "Alisin ang check",
|
||||
"checkAll": "Suriin Lahat",
|
||||
"consentDialog": {
|
||||
"title": "Kinakailangan ang Pahintulot",
|
||||
"message": "Hinihiling namin ang iyong pahintulot na ibahagi ang iyong mga nabe-verify na kredensyal. Ito ay magbibigay-daan sa amin na i-verify ang iyong pagkakakilanlan at matupad ang iyong mga kahilingan sa serbisyo. Piliin ang \"Oo, Magpatuloy\" sa pagsang-ayon o \"Tanggihan\" kung ayaw mong ibahagi.",
|
||||
"confirmButton": "Oo, Magpatuloy",
|
||||
"cancelButton": "Tanggihan"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "Sigurado ka ba?",
|
||||
"message": "Ang pagtanggi sa pahintulot ay mapipigilan ka sa pagbabahagi ng iyong mga nabe-verify na kredensyal.",
|
||||
"confirmButton": "Oo, Magpatuloy",
|
||||
"cancelButton": "Bumalik ka"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "Walang nakitang katugmang mga kredensyal!",
|
||||
"message": "Subukang muli ang pagbabahagi pagkatapos i-download ang mga kredensyal."
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "Isang Error ang Naganap!",
|
||||
"message": "Hindi nakikilala ang verifier. Mangyaring kumuha ng wastong QR code mula sa verifier."
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "Isang Error ang Naganap!",
|
||||
"message": "May nakitang hindi pagkakatugma ng kredensyal. Tiyaking napili mo ang tama at subukang muli."
|
||||
},
|
||||
"genericError": {
|
||||
"title": "Oops! Isang Error ang Naganap.",
|
||||
"message": "Dahil sa teknikal na error, hindi namin maibahagi ang card. Mag-click sa subukang muli upang muling ibahagi o mag-click sa Home upang lumabas sa proseso ng pagbabahagi."
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "Di-wastong QR code",
|
||||
"message": "Di-wasto ang QR code dahil nawawala ang ilang kinakailangang impormasyon. Mangyaring hilingin sa verifier na magbigay ng wastong QR code upang ibahagi ang iyong mga kredensyal."
|
||||
},
|
||||
"noImage": {
|
||||
"title": "Isang Error ang Naganap!",
|
||||
"message": "Ang pag-verify ng mukha ay nangangailangan ng larawan sa napiling (mga) kredensyal. Pakigamit ang opsyong Ibahagi o pumili ng kredensyal na may kasamang larawan."
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "Pagpapatunay ng Mukha",
|
||||
"status": {
|
||||
|
||||
@@ -789,6 +789,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "अनुरोधकर्ता",
|
||||
"cardsSelected": "कार्ड चयनित",
|
||||
"cardSelected": "कार्ड चयनित",
|
||||
"unCheck": "सही का निशान हटाएँ",
|
||||
"checkAll": "सभी चेक करें",
|
||||
"consentDialog": {
|
||||
"title": "सहमति आवश्यक",
|
||||
"message": "हमें आपकी सत्यापन योग्य साख साझा करने के लिए आपकी सहमति की आवश्यकता है। इससे हमें आपकी पहचान सत्यापित करने और आपके सेवा अनुरोधों को पूरा करने में मदद मिलेगी। यदि आप साझा नहीं करना चाहते हैं तो सहमति के लिए \"हां, आगे बढ़ें\" या \"अस्वीकार करें\" चुनें।",
|
||||
"confirmButton": "हाँ, आगे बढ़ें",
|
||||
"cancelButton": "गिरावट"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "क्या आपको यकीन है?",
|
||||
"message": "सहमति को अस्वीकार करने से आपको अपनी सत्यापन योग्य साख साझा करने से रोका जा सकेगा।",
|
||||
"confirmButton": "हाँ, आगे बढ़ें",
|
||||
"cancelButton": "वापस जाओ"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "कोई मेल खाता क्रेडेंशियल नहीं मिला!",
|
||||
"message": "क्रेडेंशियल डाउनलोड करने के बाद साझा करने का पुनः प्रयास करें।"
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "एक त्रुटि हुई!",
|
||||
"message": "सत्यापनकर्ता को पहचाना नहीं गया है. कृपया सत्यापनकर्ता से एक वैध क्यूआर कोड प्राप्त करें।"
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "एक त्रुटि हुई!",
|
||||
"message": "क्रेडेंशियल बेमेल का पता चला. सुनिश्चित करें कि आपने सही का चयन किया है और पुनः प्रयास करें।"
|
||||
},
|
||||
"genericError": {
|
||||
"title": "उफ़! एक त्रुटि हुई।",
|
||||
"message": "तकनीकी त्रुटि के कारण हम कार्ड साझा करने में असमर्थ हैं। पुनः साझा करने के लिए पुनः प्रयास करें पर क्लिक करें या साझाकरण प्रक्रिया से बाहर निकलने के लिए होम पर क्लिक करें।"
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "अमान्य क्यूआर कोड",
|
||||
"message": "क्यूआर कोड अमान्य है क्योंकि कुछ आवश्यक जानकारी गायब है। कृपया सत्यापनकर्ता से अपने क्रेडेंशियल साझा करने के लिए एक वैध क्यूआर कोड प्रदान करने के लिए कहें।"
|
||||
},
|
||||
"noImage": {
|
||||
"title": "एक त्रुटि हुई!",
|
||||
"message": "चेहरे के सत्यापन के लिए चयनित क्रेडेंशियल में एक फोटो की आवश्यकता होती है। कृपया शेयर विकल्प का उपयोग करें या एक क्रेडेंशियल चुनें जिसमें एक छवि शामिल हो।"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "चेहरा सत्यापन",
|
||||
"status": {
|
||||
|
||||
@@ -787,6 +787,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "ವಿನಂತಿಸುವವರು",
|
||||
"cardsSelected": "ಕಾರ್ಡ್ಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ",
|
||||
"cardSelected": "ಕಾರ್ಡ್ ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ",
|
||||
"unCheck": "ಅನ್ಚೆಕ್ ಮಾಡಿ",
|
||||
"checkAll": "ಎಲ್ಲವನ್ನೂ ಪರಿಶೀಲಿಸಿ",
|
||||
"consentDialog": {
|
||||
"title": "ಒಪ್ಪಿಗೆ ಅಗತ್ಯವಿದೆ",
|
||||
"message": "ನಿಮ್ಮ ಪರಿಶೀಲಿಸಬಹುದಾದ ರುಜುವಾತುಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ನಮಗೆ ನಿಮ್ಮ ಒಪ್ಪಿಗೆಯ ಅಗತ್ಯವಿದೆ. ಇದು ನಿಮ್ಮ ಗುರುತನ್ನು ಪರಿಶೀಲಿಸಲು ಮತ್ತು ನಿಮ್ಮ ಸೇವಾ ವಿನಂತಿಗಳನ್ನು ಪೂರೈಸಲು ನಮಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. ಒಪ್ಪಿಗೆ ನೀಡಲು \"ಹೌದು, ಮುಂದುವರೆಯಿರಿ\" ಅಥವಾ ನೀವು ಹಂಚಿಕೊಳ್ಳಲು ಬಯಸದಿದ್ದರೆ \"ನಿರಾಕರಿಸಿ\" ಆಯ್ಕೆಮಾಡಿ.",
|
||||
"confirmButton": "ಹೌದು, ಮುಂದುವರೆಯಿರಿ",
|
||||
"cancelButton": "ನಿರಾಕರಿಸು"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ?",
|
||||
"message": "ಸಮ್ಮತಿಯನ್ನು ನಿರಾಕರಿಸುವುದರಿಂದ ನಿಮ್ಮ ಪರಿಶೀಲಿಸಬಹುದಾದ ರುಜುವಾತುಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳುವುದರಿಂದ ನಿಮ್ಮನ್ನು ತಡೆಯುತ್ತದೆ.",
|
||||
"confirmButton": "ಹೌದು, ಮುಂದುವರೆಯಿರಿ",
|
||||
"cancelButton": "ಹಿಂತಿರುಗಿ"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "ಯಾವುದೇ ಹೊಂದಾಣಿಕೆಯ ರುಜುವಾತುಗಳು ಕಂಡುಬಂದಿಲ್ಲ!",
|
||||
"message": "ರುಜುವಾತುಗಳನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಿದ ನಂತರ ಹಂಚಿಕೊಳ್ಳಲು ಮರುಪ್ರಯತ್ನಿಸಿ."
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ!",
|
||||
"message": "ಪರಿಶೀಲಕನನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ. ದಯವಿಟ್ಟು ಪರಿಶೀಲಕರಿಂದ ಮಾನ್ಯವಾದ QR ಕೋಡ್ ಅನ್ನು ಪಡೆದುಕೊಳ್ಳಿ."
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ!",
|
||||
"message": "ರುಜುವಾತು ಹೊಂದಿಕೆಯಾಗದಿರುವುದು ಪತ್ತೆಯಾಗಿದೆ. ನೀವು ಸರಿಯಾದದನ್ನು ಆರಿಸಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
|
||||
},
|
||||
"genericError": {
|
||||
"title": "ಓಹ್! ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ.",
|
||||
"message": "ತಾಂತ್ರಿಕ ದೋಷದಿಂದಾಗಿ, ಕಾರ್ಡ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ನಮಗೆ ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಮರು-ಹಂಚಿಕೊಳ್ಳಲು ಮರುಪ್ರಯತ್ನದ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ ಅಥವಾ ಹಂಚಿಕೆ ಪ್ರಕ್ರಿಯೆಯಿಂದ ನಿರ್ಗಮಿಸಲು ಹೋಮ್ ಮೇಲೆ ಕ್ಲಿಕ್ ಮಾಡಿ."
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "ಅಮಾನ್ಯವಾದ QR ಕೋಡ್",
|
||||
"message": "QR ಕೋಡ್ ಅಮಾನ್ಯವಾಗಿದೆ ಏಕೆಂದರೆ ಕೆಲವು ಅಗತ್ಯ ಮಾಹಿತಿಯು ಕಾಣೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ನಿಮ್ಮ ರುಜುವಾತುಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಮಾನ್ಯವಾದ QR ಕೋಡ್ ಒದಗಿಸಲು ಪರಿಶೀಲಕರನ್ನು ಕೇಳಿ."
|
||||
},
|
||||
"noImage": {
|
||||
"title": "ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ!",
|
||||
"message": "ಮುಖ ಪರಿಶೀಲನೆಗೆ ಆಯ್ಕೆಮಾಡಿದ ರುಜುವಾತು(ಗಳಲ್ಲಿ) ಫೋಟೋ ಅಗತ್ಯವಿದೆ. ದಯವಿಟ್ಟು ಹಂಚಿಕೆ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ ಅಥವಾ ಚಿತ್ರವನ್ನು ಒಳಗೊಂಡಿರುವ ರುಜುವಾತುಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ."
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "ಮುಖ ಪರಿಶೀಲನೆ",
|
||||
"status": {
|
||||
|
||||
@@ -787,6 +787,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SendVPScreen": {
|
||||
"requester": "கோரிக்கையாளர்",
|
||||
"cardsSelected": "அட்டைகள் தேர்ந்தெடுக்கப்பட்டன",
|
||||
"cardSelected": "அட்டை தேர்ந்தெடுக்கப்பட்டது",
|
||||
"unCheck": "தேர்வுநீக்கவும்",
|
||||
"checkAll": "அனைத்தையும் சரிபார்க்கவும்",
|
||||
"consentDialog": {
|
||||
"title": "ஒப்புதல் தேவை",
|
||||
"message": "உங்களின் சரிபார்க்கக்கூடிய நற்சான்றிதழ்களைப் பகிர உங்கள் ஒப்புதல் தேவை. இது உங்கள் அடையாளத்தைச் சரிபார்க்கவும் உங்கள் சேவை கோரிக்கைகளை நிறைவேற்றவும் எங்களுக்கு உதவும். ஒப்புக்கொள்ள \"ஆம், தொடரவும்\" அல்லது நீங்கள் பகிர விரும்பவில்லை என்றால் \"நிராகரி\" என்பதைத் தேர்ந்தெடுக்கவும்.",
|
||||
"confirmButton": "ஆம், தொடரவும்",
|
||||
"cancelButton": "நிராகரி"
|
||||
},
|
||||
"confirmationDialog": {
|
||||
"title": "நீங்கள் உறுதியாக இருக்கிறீர்களா?",
|
||||
"message": "ஒப்புதலை நிராகரிப்பது உங்கள் சரிபார்க்கக்கூடிய நற்சான்றிதழ்களைப் பகிர்வதைத் தடுக்கும்.",
|
||||
"confirmButton": "ஆம், தொடரவும்",
|
||||
"cancelButton": "திரும்பி செல்"
|
||||
},
|
||||
"errors": {
|
||||
"noMatchingCredentials": {
|
||||
"title": "பொருந்தக்கூடிய சான்றுகள் எதுவும் கிடைக்கவில்லை!",
|
||||
"message": "நற்சான்றிதழ்களைப் பதிவிறக்கிய பிறகு மீண்டும் பகிர முயற்சிக்கவும்."
|
||||
},
|
||||
"invalidVerifier": {
|
||||
"title": "ஒரு பிழை ஏற்பட்டது!",
|
||||
"message": "சரிபார்ப்பவர் அங்கீகரிக்கப்படவில்லை. சரிபார்ப்பாளரிடமிருந்து சரியான QR குறியீட்டைப் பெறவும்."
|
||||
},
|
||||
"credentialsMismatch": {
|
||||
"title": "ஒரு பிழை ஏற்பட்டது!",
|
||||
"message": "நற்சான்றிதழ் பொருத்தமின்மை கண்டறியப்பட்டது. நீங்கள் சரியானதைத் தேர்ந்தெடுத்துள்ளீர்கள் என்பதை உறுதிசெய்து, மீண்டும் முயற்சிக்கவும்."
|
||||
},
|
||||
"genericError": {
|
||||
"title": "அச்சச்சோ! ஒரு பிழை ஏற்பட்டது.",
|
||||
"message": "தொழில்நுட்ப பிழை காரணமாக, கார்டை எங்களால் பகிர முடியவில்லை. மறுபகிர்வதற்கு மீண்டும் முயற்சி என்பதைக் கிளிக் செய்யவும் அல்லது பகிர்தல் செயல்முறையிலிருந்து வெளியேற முகப்பு என்பதைக் கிளிக் செய்யவும்."
|
||||
},
|
||||
"invalidQrCode": {
|
||||
"title": "தவறான QR குறியீடு",
|
||||
"message": "தேவையான சில தகவல்கள் இல்லாததால், QR குறியீடு தவறானது. உங்கள் நற்சான்றிதழ்களைப் பகிர சரியான QR குறியீட்டை வழங்குமாறு சரிபார்ப்பாளரிடம் கேட்கவும்."
|
||||
},
|
||||
"noImage": {
|
||||
"title": "ஒரு பிழை ஏற்பட்டது!",
|
||||
"message": "முகம் சரிபார்ப்புக்கு தேர்ந்தெடுக்கப்பட்ட நற்சான்றிதழில்(களில்) ஒரு புகைப்படம் தேவை. பகிர் விருப்பத்தைப் பயன்படுத்தவும் அல்லது படத்தை உள்ளடக்கிய நற்சான்றிதழைத் தேர்ந்தெடுக்கவும்."
|
||||
}
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"faceAuth": "முக சரிபார்ப்பு",
|
||||
"status": {
|
||||
|
||||
@@ -49,7 +49,7 @@ export const IssuersGuards = () => {
|
||||
event.data instanceof BiometricCancellationError,
|
||||
isGenericError: (_: any, event: any) => {
|
||||
const errorMessage = event.data.message;
|
||||
return !errorMessage.includes(NETWORK_REQUEST_FAILED);
|
||||
return errorMessage === ErrorMessage.GENERIC;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,7 +32,10 @@ export function selectIsBiometricCancelled(state: State) {
|
||||
}
|
||||
|
||||
export function selectIsNonGenericError(state: State) {
|
||||
return state.context.errorMessage !== ErrorMessage.GENERIC;
|
||||
return (
|
||||
state.context.errorMessage !== ErrorMessage.GENERIC &&
|
||||
state.context.errorMessage !== ''
|
||||
);
|
||||
}
|
||||
|
||||
export function selectIsDone(state: State) {
|
||||
|
||||
@@ -2,10 +2,7 @@ import {assign, send} from 'xstate';
|
||||
import {CommunicationDetails} from '../../../shared/Utils';
|
||||
import {StoreEvents} from '../../store';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {
|
||||
MIMOTO_BASE_URL,
|
||||
MY_VCS_STORE_KEY,
|
||||
} from '../../../shared/constants';
|
||||
import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants';
|
||||
import i18n from '../../../i18n';
|
||||
import {getHomeMachineService} from '../../../screens/Home/HomeScreenController';
|
||||
import {DownloadProps} from '../../../shared/api';
|
||||
@@ -459,7 +456,8 @@ export const VCItemActions = model => {
|
||||
type: 'VC_DOWNLOADED',
|
||||
id: context.vcMetadata.displayId,
|
||||
issuer: context.vcMetadata.issuer!!,
|
||||
credentialConfigurationId: context.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
});
|
||||
@@ -472,7 +470,8 @@ export const VCItemActions = model => {
|
||||
(context: any, _) => {
|
||||
const vcMetadata = VCMetadata.fromVC(context.vcMetadata);
|
||||
return ActivityLogEvents.LOG_ACTIVITY({
|
||||
credentialConfigurationId: context.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
id: vcMetadata.displayId,
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
@@ -491,7 +490,8 @@ export const VCItemActions = model => {
|
||||
return ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
type: 'WALLET_BINDING_SUCCESSFULL',
|
||||
credentialConfigurationId: context.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
id: vcMetadata.displayId,
|
||||
timestamp: Date.now(),
|
||||
@@ -510,7 +510,8 @@ export const VCItemActions = model => {
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
type: 'WALLET_BINDING_FAILURE',
|
||||
id: vcMetadata.displayId,
|
||||
credentialConfigurationId: context.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import {NativeModules} from 'react-native';
|
||||
import Cloud from '../../../shared/CloudBackupAndRestoreUtils';
|
||||
import getAllConfigurations, {API_URLS, CACHED_API, DownloadProps,} from '../../../shared/api';
|
||||
import {fetchKeyPair, generateKeyPair,} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import getAllConfigurations, {
|
||||
API_URLS,
|
||||
CACHED_API,
|
||||
DownloadProps,
|
||||
} from '../../../shared/api';
|
||||
import {
|
||||
fetchKeyPair,
|
||||
generateKeyPair,
|
||||
} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {CredentialDownloadResponse, request} from '../../../shared/request';
|
||||
import {WalletBindingResponse} from '../VCMetaMachine/vc';
|
||||
import {verifyCredential} from '../../../shared/vcjs/verifyCredential';
|
||||
@@ -106,8 +113,8 @@ export const VCItemServices = model => {
|
||||
);
|
||||
try {
|
||||
return getMatchingCredentialIssuerMetadata(
|
||||
wellknownResponse,
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
wellknownResponse,
|
||||
context.verifiableCredential.credentialConfigurationId,
|
||||
);
|
||||
} catch (error) {
|
||||
return {};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {StateFrom} from 'xstate';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {vcMetaMachine} from './VCMetaMachine';
|
||||
import {VC} from './vc';
|
||||
|
||||
type State = StateFrom<typeof vcMetaMachine>;
|
||||
|
||||
@@ -19,6 +20,12 @@ export function selectShareableVcsMetadata(state: State): VCMetadata[] {
|
||||
);
|
||||
}
|
||||
|
||||
export function selectShareableVcs(state: State): VC[] {
|
||||
return Object.values(state.context.myVcs).filter(
|
||||
vc => vc?.verifiableCredential != null,
|
||||
);
|
||||
}
|
||||
|
||||
export function selectReceivedVcsMetadata(state: State): VCMetadata[] {
|
||||
return state.context.receivedVcsMetadata;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
'done.invoke.app.init.checkKeyPairs:invocation[0]': {
|
||||
type: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.app.init.generateKeyPairs:invocation[0]': {
|
||||
type: 'done.invoke.app.init.generateKeyPairs:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.app.ready.focus.active:invocation[0]': {
|
||||
type: 'done.invoke.app.ready.focus.active:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform.app.init.checkKeyPairs:invocation[0]': {
|
||||
type: 'error.platform.app.init.checkKeyPairs:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
checkFocusState: 'done.invoke.app.ready.focus:invocation[0]';
|
||||
checkKeyPairs: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
|
||||
checkNetworkState: 'done.invoke.app.ready.network:invocation[0]';
|
||||
generateKeyPairsAndStoreOrder: 'done.invoke.app.init.generateKeyPairs:invocation[0]';
|
||||
getAppInfo: 'done.invoke.app.init.info:invocation[0]';
|
||||
isQrLoginByDeepLink: 'done.invoke.app.ready.focus.active:invocation[0]';
|
||||
resetQRLoginDeepLinkData: 'done.invoke.app.ready.focus.active:invocation[1]';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: 'forwardToServices';
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
forwardToServices: 'ACTIVE' | 'INACTIVE' | 'OFFLINE' | 'ONLINE';
|
||||
loadCredentialRegistryHostFromStorage: 'READY';
|
||||
loadCredentialRegistryInConstants: 'STORE_RESPONSE';
|
||||
loadEsignetHostFromConstants: 'STORE_RESPONSE';
|
||||
loadEsignetHostFromStorage: 'READY';
|
||||
logServiceEvents: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
|
||||
logStoreEvents:
|
||||
| 'KEY_INVALIDATE_ERROR'
|
||||
| 'RESET_KEY_INVALIDATE_ERROR_DISMISS'
|
||||
| 'xstate.init';
|
||||
requestDeviceInfo: 'REQUEST_DEVICE_INFO';
|
||||
resetKeyInvalidateError: 'READY' | 'RESET_KEY_INVALIDATE_ERROR_DISMISS';
|
||||
resetLinkCode: 'RESET_LINKCODE';
|
||||
setAppInfo: 'APP_INFO_RECEIVED';
|
||||
setIsDecryptError: 'DECRYPT_ERROR';
|
||||
setIsReadError: 'ERROR';
|
||||
setLinkCode: 'done.invoke.app.ready.focus.active:invocation[0]';
|
||||
spawnServiceActors: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
|
||||
spawnStoreActor:
|
||||
| 'KEY_INVALIDATE_ERROR'
|
||||
| 'RESET_KEY_INVALIDATE_ERROR_DISMISS'
|
||||
| 'xstate.init';
|
||||
unsetIsDecryptError: 'DECRYPT_ERROR_DISMISS' | 'READY';
|
||||
unsetIsReadError: 'READY';
|
||||
updateKeyInvalidateError: 'ERROR' | 'KEY_INVALIDATE_ERROR';
|
||||
};
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {};
|
||||
eventsCausingServices: {
|
||||
checkFocusState: 'APP_INFO_RECEIVED';
|
||||
checkKeyPairs:
|
||||
| 'READY'
|
||||
| 'done.invoke.app.init.generateKeyPairs:invocation[0]';
|
||||
checkNetworkState: 'APP_INFO_RECEIVED';
|
||||
generateKeyPairsAndStoreOrder: 'error.platform.app.init.checkKeyPairs:invocation[0]';
|
||||
getAppInfo: 'STORE_RESPONSE';
|
||||
isQrLoginByDeepLink: 'ACTIVE';
|
||||
resetQRLoginDeepLinkData: 'ACTIVE';
|
||||
};
|
||||
matchesStates:
|
||||
| 'init'
|
||||
| 'init.checkKeyPairs'
|
||||
| 'init.credentialRegistry'
|
||||
| 'init.generateKeyPairs'
|
||||
| 'init.info'
|
||||
| 'init.services'
|
||||
| 'init.store'
|
||||
| 'ready'
|
||||
| 'ready.focus'
|
||||
| 'ready.focus.active'
|
||||
| 'ready.focus.checking'
|
||||
| 'ready.focus.inactive'
|
||||
| 'ready.network'
|
||||
| 'ready.network.checking'
|
||||
| 'ready.network.offline'
|
||||
| 'ready.network.online'
|
||||
| 'waiting'
|
||||
| {
|
||||
init?:
|
||||
| 'checkKeyPairs'
|
||||
| 'credentialRegistry'
|
||||
| 'generateKeyPairs'
|
||||
| 'info'
|
||||
| 'services'
|
||||
| 'store';
|
||||
ready?:
|
||||
| 'focus'
|
||||
| 'network'
|
||||
| {
|
||||
focus?: 'active' | 'checking' | 'inactive';
|
||||
network?: 'checking' | 'offline' | 'online';
|
||||
};
|
||||
};
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -618,7 +618,9 @@ export const requestMachine =
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
type: context.receiveLogType,
|
||||
id: vcMetadata.displayId,
|
||||
credentialConfigurationId: context.incomingVc.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.incomingVc.verifiableCredential
|
||||
.credentialConfigurationId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
timestamp: Date.now(),
|
||||
deviceName:
|
||||
|
||||
@@ -30,7 +30,8 @@ export function selectVerifiableCredentialData(state: State) {
|
||||
getMosipLogo(),
|
||||
issuer: vcMetadata.issuer,
|
||||
wellKnown: state.context.incomingVc?.verifiableCredential?.wellKnown,
|
||||
credentialConfigurationId: state.context.incomingVc?.verifiableCredential?.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
state.context.incomingVc?.verifiableCredential?.credentialConfigurationId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,16 @@ import {ActivityLogEvents} from '../../activityLog';
|
||||
import {StoreEvents} from '../../store';
|
||||
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
|
||||
import {NativeModules} from 'react-native';
|
||||
|
||||
import {wallet} from '../../../shared/tuvali';
|
||||
import {createOpenID4VPMachine} from '../../openID4VP/openID4VPMachine';
|
||||
|
||||
export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
const QR_LOGIN_REF_ID = 'QrLogin';
|
||||
const OPENID4VP_REF_ID = 'OpenID4VP';
|
||||
|
||||
export const ScanActions = (model: any) => {
|
||||
const {RNPixelpassModule} = NativeModules;
|
||||
return {
|
||||
setChildRef: assign({
|
||||
setQrLoginRef: assign({
|
||||
QrLoginRef: (context: any) => {
|
||||
const service = spawn(
|
||||
createQrLoginMachine(context.serviceRefs),
|
||||
@@ -45,6 +48,17 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
},
|
||||
}),
|
||||
|
||||
setOpenId4VPRef: assign({
|
||||
OpenId4VPRef: (context: any) => {
|
||||
const service = spawn(
|
||||
createOpenID4VPMachine(context.serviceRefs),
|
||||
OPENID4VP_REF_ID,
|
||||
);
|
||||
service.subscribe(logState);
|
||||
return service;
|
||||
},
|
||||
}),
|
||||
|
||||
resetLinkCode: model.assign({
|
||||
linkcode: '',
|
||||
}),
|
||||
@@ -85,6 +99,14 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
selectedVc: context.selectedVc,
|
||||
}),
|
||||
|
||||
sendVPScanData: context =>
|
||||
context.OpenId4VPRef.send({
|
||||
type: 'AUTHENTICATE',
|
||||
encodedAuthRequest: context.linkCode,
|
||||
flowType: context.openID4VPFlowType,
|
||||
selectedVC: context.selectedVc,
|
||||
}),
|
||||
|
||||
openBluetoothSettings: () => {
|
||||
isAndroid()
|
||||
? BluetoothStateManager.openSettings().catch()
|
||||
@@ -145,10 +167,28 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
flowType: (_context, event) => event.flowType,
|
||||
}),
|
||||
|
||||
setOpenId4VPFlowType: assign({
|
||||
openID4VPFlowType: (context: any) => {
|
||||
let flowType = VCShareFlowType.OPENID4VP;
|
||||
if (context.flowType === VCShareFlowType.MINI_VIEW_SHARE) {
|
||||
flowType = VCShareFlowType.MINI_VIEW_SHARE_OPENID4VP;
|
||||
} else if (
|
||||
context.flowType === VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE
|
||||
) {
|
||||
flowType = VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP;
|
||||
}
|
||||
return flowType;
|
||||
},
|
||||
}),
|
||||
|
||||
resetFlowType: assign({
|
||||
flowType: VCShareFlowType.SIMPLE_SHARE,
|
||||
}),
|
||||
|
||||
resetOpenID4VPFlowType: assign({
|
||||
openID4VPFlowType: '',
|
||||
}),
|
||||
|
||||
registerLoggers: assign({
|
||||
loggers: () => {
|
||||
if (__DEV__) {
|
||||
@@ -200,7 +240,8 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
? context.shareLogType
|
||||
: 'VC_SHARED_WITH_VERIFICATION_CONSENT',
|
||||
id: vcMetadata.displayId,
|
||||
credentialConfigurationId: context.selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
timestamp: Date.now(),
|
||||
deviceName:
|
||||
@@ -217,7 +258,8 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
type: 'PRESENCE_VERIFICATION_FAILED',
|
||||
timestamp: Date.now(),
|
||||
credentialConfigurationId: context.selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
context.selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
id: vcMetadata.displayId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
deviceName:
|
||||
@@ -228,8 +270,10 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
),
|
||||
|
||||
setLinkCode: assign({
|
||||
linkCode: (_, event) =>
|
||||
new URL(event.params).searchParams.get('linkCode'),
|
||||
linkCode: (context: any, event) =>
|
||||
context.openID4VPFlowType.startsWith('OpenID4VP')
|
||||
? event.params
|
||||
: new URL(event.params).searchParams.get('linkCode'),
|
||||
}),
|
||||
|
||||
setLinkCodeFromDeepLink: assign({
|
||||
@@ -282,7 +326,8 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
_vcKey: '',
|
||||
id: vcMetadata.displayId,
|
||||
issuer: vcMetadata.issuer!!,
|
||||
credentialConfigurationId: selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
credentialConfigurationId:
|
||||
selectedVc.verifiableCredential.credentialConfigurationId,
|
||||
type: 'QRLOGIN_SUCCESFULL',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
|
||||
@@ -24,6 +24,10 @@ export const ScanGuards = () => {
|
||||
}
|
||||
},
|
||||
|
||||
isOnlineSharing: (_, event) => {
|
||||
return event.params.startsWith('openid4vp://authorize');
|
||||
},
|
||||
|
||||
uptoAndroid11: () => isAndroid() && androidVersion < 31,
|
||||
|
||||
isIOS: () => isIOS(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import {EventFrom, send, StateFrom} from 'xstate';
|
||||
import {actions, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {AppServices} from '../../../shared/GlobalContext';
|
||||
import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants';
|
||||
import {
|
||||
@@ -12,9 +12,9 @@ import {ScanActions} from './scanActions';
|
||||
import {ScanGuards} from './scanGuards';
|
||||
import {ScanModel} from './scanModel';
|
||||
import {ScanServices} from './scanServices';
|
||||
import {openID4VPMachine} from '../../openID4VP/openID4VPMachine';
|
||||
|
||||
const model = ScanModel;
|
||||
const QR_LOGIN_REF_ID = 'QrLogin';
|
||||
export const ScanEvents = model.events;
|
||||
|
||||
export const scanMachine =
|
||||
@@ -35,6 +35,7 @@ export const scanMachine =
|
||||
initial: 'inactive',
|
||||
on: {
|
||||
SCREEN_BLUR: {
|
||||
actions: 'resetOpenID4VPFlowType',
|
||||
target: '#scan.disconnectDevice',
|
||||
},
|
||||
SCREEN_FOCUS: {
|
||||
@@ -75,6 +76,7 @@ export const scanMachine =
|
||||
},
|
||||
},
|
||||
checkStorage: {
|
||||
entry: 'setOpenId4VPRef',
|
||||
invoke: {
|
||||
src: 'checkStorageAvailability',
|
||||
onDone: [
|
||||
@@ -319,7 +321,6 @@ export const scanMachine =
|
||||
'removeLoggers',
|
||||
'registerLoggers',
|
||||
'clearUri',
|
||||
'setChildRef',
|
||||
'resetFaceCaptureBannerStatus',
|
||||
],
|
||||
on: {
|
||||
@@ -338,7 +339,16 @@ export const scanMachine =
|
||||
{
|
||||
target: 'showQrLogin',
|
||||
cond: 'isQrLogin',
|
||||
actions: ['sendVcSharingStartEvent', 'setLinkCode'],
|
||||
actions: [
|
||||
'setQrLoginRef',
|
||||
'sendVcSharingStartEvent',
|
||||
'setLinkCode',
|
||||
],
|
||||
},
|
||||
{
|
||||
target: 'startVPSharing',
|
||||
cond: 'isOnlineSharing',
|
||||
actions: ['setOpenId4VPFlowType', 'setLinkCode'],
|
||||
},
|
||||
{
|
||||
target: 'decodeQuickShareData',
|
||||
@@ -351,6 +361,89 @@ export const scanMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
startVPSharing: {
|
||||
entry: [
|
||||
'sendVPScanData',
|
||||
() =>
|
||||
sendStartEvent(
|
||||
getStartEventData(TelemetryConstants.FlowType.vpSharing),
|
||||
),
|
||||
],
|
||||
invoke: {
|
||||
id: 'OpenId4VP',
|
||||
src: openID4VPMachine,
|
||||
onDone: {},
|
||||
},
|
||||
on: {
|
||||
IN_PROGRESS: {
|
||||
target: '.inProgress',
|
||||
},
|
||||
TIMEOUT: {
|
||||
target: '.timeout',
|
||||
},
|
||||
DISMISS: [
|
||||
{
|
||||
cond: 'isFlowTypeSimpleShare',
|
||||
actions: 'resetOpenID4VPFlowType',
|
||||
target: 'checkStorage',
|
||||
},
|
||||
{
|
||||
target: 'checkStorage',
|
||||
},
|
||||
],
|
||||
SHOW_ERROR: {
|
||||
target: '.showError',
|
||||
},
|
||||
SUCCESS: {
|
||||
target: '.success',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
success: {},
|
||||
showError: {},
|
||||
inProgress: {
|
||||
on: {
|
||||
CANCEL: [
|
||||
{
|
||||
cond: 'isFlowTypeSimpleShare',
|
||||
actions: 'resetOpenID4VPFlowType',
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
{
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
timeout: {
|
||||
on: {
|
||||
STAY_IN_PROGRESS: {
|
||||
target: 'inProgress',
|
||||
},
|
||||
CANCEL: [
|
||||
{
|
||||
cond: 'isFlowTypeSimpleShare',
|
||||
actions: 'resetOpenID4VPFlowType',
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
{
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
],
|
||||
RETRY: [
|
||||
{
|
||||
cond: 'isFlowTypeSimpleShare',
|
||||
actions: 'resetOpenID4VPFlowType',
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
{
|
||||
target: '#scan.checkStorage',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
decodeQuickShareData: {
|
||||
entry: 'loadMetaDataToMemory',
|
||||
on: {
|
||||
@@ -781,10 +874,8 @@ export const scanMachine =
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: ScanActions(model, QR_LOGIN_REF_ID),
|
||||
|
||||
actions: ScanActions(model),
|
||||
services: ScanServices(model),
|
||||
|
||||
guards: ScanGuards(),
|
||||
delays: {
|
||||
DESTROY_TIMEOUT: 500,
|
||||
|
||||
@@ -57,12 +57,14 @@ export interface Typegen0 {
|
||||
| 'resetFaceCaptureBannerStatus'
|
||||
| 'resetFlowType'
|
||||
| 'resetLinkCode'
|
||||
| 'resetOpenID4VPFlowType'
|
||||
| 'resetSelectedVc'
|
||||
| 'resetShowQuickShareSuccessBanner'
|
||||
| 'sendBLEConnectionErrorEvent'
|
||||
| 'sendScanData'
|
||||
| 'sendVCShareFlowCancelEndEvent'
|
||||
| 'sendVCShareFlowTimeoutEndEvent'
|
||||
| 'sendVPScanData'
|
||||
| 'sendVcShareSuccessEvent'
|
||||
| 'sendVcSharingStartEvent'
|
||||
| 'setBleError'
|
||||
@@ -70,6 +72,9 @@ export interface Typegen0 {
|
||||
| 'setFlowType'
|
||||
| 'setLinkCode'
|
||||
| 'setLinkCodeFromDeepLink'
|
||||
| 'setOpenId4VPFlowType'
|
||||
| 'setOpenId4VPRef'
|
||||
| 'setQrLoginRef'
|
||||
| 'setQuickShareData'
|
||||
| 'setReadyForBluetoothStateCheck'
|
||||
| 'setReceiverInfo'
|
||||
@@ -92,6 +97,7 @@ export interface Typegen0 {
|
||||
| 'isFlowTypeSimpleShare'
|
||||
| 'isIOS'
|
||||
| 'isMinimumStorageRequiredForAuditEntryReached'
|
||||
| 'isOnlineSharing'
|
||||
| 'isOpenIdQr'
|
||||
| 'isQrLogin'
|
||||
| 'isQuickShare'
|
||||
@@ -156,6 +162,7 @@ export interface Typegen0 {
|
||||
| 'SCREEN_FOCUS'
|
||||
| 'SELECT_VC'
|
||||
| 'xstate.stop';
|
||||
resetOpenID4VPFlowType: 'CANCEL' | 'DISMISS' | 'RETRY' | 'SCREEN_BLUR';
|
||||
resetSelectedVc:
|
||||
| 'DISCONNECT'
|
||||
| 'DISMISS'
|
||||
@@ -169,13 +176,23 @@ export interface Typegen0 {
|
||||
sendScanData: 'QRLOGIN_VIA_DEEP_LINK' | 'SCAN';
|
||||
sendVCShareFlowCancelEndEvent: 'CANCEL';
|
||||
sendVCShareFlowTimeoutEndEvent: 'CANCEL' | 'RETRY';
|
||||
sendVPScanData: 'SCAN';
|
||||
sendVcShareSuccessEvent: 'VC_ACCEPTED';
|
||||
sendVcSharingStartEvent: 'SCAN';
|
||||
setBleError: 'BLE_ERROR';
|
||||
setChildRef: 'QRLOGIN_VIA_DEEP_LINK' | 'STORE_RESPONSE';
|
||||
setChildRef: 'QRLOGIN_VIA_DEEP_LINK';
|
||||
setFlowType: 'SELECT_VC';
|
||||
setLinkCode: 'SCAN';
|
||||
setLinkCodeFromDeepLink: 'QRLOGIN_VIA_DEEP_LINK';
|
||||
setOpenId4VPFlowType: 'SCAN';
|
||||
setOpenId4VPRef:
|
||||
| 'CANCEL'
|
||||
| 'DISMISS'
|
||||
| 'RESET'
|
||||
| 'RETRY'
|
||||
| 'SCREEN_FOCUS'
|
||||
| 'SELECT_VC';
|
||||
setQrLoginRef: 'SCAN';
|
||||
setQuickShareData: 'SCAN';
|
||||
setReadyForBluetoothStateCheck: 'BLUETOOTH_PERMISSION_ENABLED';
|
||||
setReceiverInfo: 'CONNECTED';
|
||||
@@ -200,9 +217,10 @@ export interface Typegen0 {
|
||||
eventsCausingGuards: {
|
||||
isFlowTypeMiniViewShare: 'CHECK_FLOW_TYPE';
|
||||
isFlowTypeMiniViewShareWithSelfie: 'CHECK_FLOW_TYPE' | 'DISMISS';
|
||||
isFlowTypeSimpleShare: 'CANCEL' | 'CHECK_FLOW_TYPE' | 'DISMISS';
|
||||
isFlowTypeSimpleShare: 'CANCEL' | 'CHECK_FLOW_TYPE' | 'DISMISS' | 'RETRY';
|
||||
isIOS: 'BLUETOOTH_STATE_DISABLED' | 'START_PERMISSION_CHECK';
|
||||
isMinimumStorageRequiredForAuditEntryReached: 'done.invoke.scan.checkStorage:invocation[0]';
|
||||
isOnlineSharing: 'SCAN';
|
||||
isOpenIdQr: 'SCAN';
|
||||
isQrLogin: 'SCAN';
|
||||
isQuickShare: 'SCAN';
|
||||
@@ -210,6 +228,7 @@ export interface Typegen0 {
|
||||
uptoAndroid11: '' | 'START_PERMISSION_CHECK';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
OpenId4VP: 'SCAN';
|
||||
QrLogin: 'QRLOGIN_VIA_DEEP_LINK' | 'SCAN';
|
||||
checkBluetoothPermission:
|
||||
| ''
|
||||
@@ -220,7 +239,13 @@ export interface Typegen0 {
|
||||
checkLocationPermission: 'LOCATION_ENABLED';
|
||||
checkLocationStatus: '' | 'APP_ACTIVE' | 'LOCATION_REQUEST';
|
||||
checkNearByDevicesPermission: 'APP_ACTIVE' | 'START_PERMISSION_CHECK';
|
||||
checkStorageAvailability: 'RESET' | 'SCREEN_FOCUS' | 'SELECT_VC';
|
||||
checkStorageAvailability:
|
||||
| 'CANCEL'
|
||||
| 'DISMISS'
|
||||
| 'RESET'
|
||||
| 'RETRY'
|
||||
| 'SCREEN_FOCUS'
|
||||
| 'SELECT_VC';
|
||||
disconnect: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'RETRY' | 'SCREEN_BLUR';
|
||||
monitorConnection: 'DISMISS' | 'SCREEN_BLUR' | 'xstate.init';
|
||||
requestBluetooth: 'BLUETOOTH_STATE_DISABLED';
|
||||
@@ -293,6 +318,11 @@ export interface Typegen0 {
|
||||
| 'showQrLogin.navigatingToHistory'
|
||||
| 'showQrLogin.storing'
|
||||
| 'startPermissionCheck'
|
||||
| 'startVPSharing'
|
||||
| 'startVPSharing.inProgress'
|
||||
| 'startVPSharing.showError'
|
||||
| 'startVPSharing.success'
|
||||
| 'startVPSharing.timeout'
|
||||
| {
|
||||
checkBluetoothPermission?: 'checking' | 'enabled';
|
||||
checkBluetoothState?: 'checking' | 'enabled' | 'requesting';
|
||||
@@ -322,6 +352,7 @@ export interface Typegen0 {
|
||||
| 'verifyingIdentity'
|
||||
| {sendingVc?: 'inProgress' | 'sent' | 'timeout'};
|
||||
showQrLogin?: 'idle' | 'navigatingToHistory' | 'storing';
|
||||
startVPSharing?: 'inProgress' | 'showError' | 'success' | 'timeout';
|
||||
};
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {qrLoginMachine} from '../../QrLogin/QrLoginMachine';
|
||||
import {VC} from '../../VerifiableCredential/VCMetaMachine/vc';
|
||||
import {ActivityLogType} from '../../activityLog';
|
||||
import {BLEError} from '../types';
|
||||
import {openID4VPMachine} from '../../openID4VP/openID4VPMachine';
|
||||
|
||||
const ScanEvents = {
|
||||
SELECT_VC: (vc: VC, flowType: string) => ({vc, flowType}),
|
||||
@@ -55,6 +56,10 @@ const ScanEvents = {
|
||||
}),
|
||||
ALLOWED: () => ({}),
|
||||
DENIED: () => ({}),
|
||||
SHOW_ERROR: () => ({}),
|
||||
SUCCESS: () => ({}),
|
||||
IN_PROGRESS: () => ({}),
|
||||
TIMEOUT: () => ({}),
|
||||
QRLOGIN_VIA_DEEP_LINK: (linkCode: string) => ({linkCode}),
|
||||
};
|
||||
|
||||
@@ -68,10 +73,12 @@ export const ScanModel = createModel(
|
||||
loggers: [] as EmitterSubscription[],
|
||||
vcName: '',
|
||||
flowType: VCShareFlowType.SIMPLE_SHARE,
|
||||
openID4VPFlowType: '',
|
||||
verificationImage: {} as CameraCapturedPicture,
|
||||
openId4VpUri: '',
|
||||
shareLogType: '' as ActivityLogType,
|
||||
QrLoginRef: {} as ActorRefFrom<typeof qrLoginMachine>,
|
||||
OpenId4VPRef: {} as ActorRefFrom<typeof openID4VPMachine>,
|
||||
showQuickShareSuccessBanner: false,
|
||||
linkCode: '',
|
||||
quickShareData: {},
|
||||
|
||||
@@ -9,6 +9,10 @@ export function selectFlowType(state: State) {
|
||||
return state.context.flowType;
|
||||
}
|
||||
|
||||
export function selectOpenID4VPFlowType(state: State) {
|
||||
return state.context.openID4VPFlowType;
|
||||
}
|
||||
|
||||
export function selectReceiverInfo(state: State) {
|
||||
return state.context.receiverInfo;
|
||||
}
|
||||
@@ -18,26 +22,28 @@ export function selectVcName(state: State) {
|
||||
}
|
||||
|
||||
export function selectCredential(state: State) {
|
||||
return (
|
||||
return [
|
||||
state.context.selectedVc?.verifiableCredential?.credential ||
|
||||
state.context.selectedVc?.verifiableCredential
|
||||
);
|
||||
state.context.selectedVc?.verifiableCredential,
|
||||
];
|
||||
}
|
||||
|
||||
export function selectVerifiableCredentialData(state: State) {
|
||||
const vcMetadata = new VCMetadata(state.context.selectedVc?.vcMetadata);
|
||||
return {
|
||||
vcMetadata: vcMetadata,
|
||||
issuer: vcMetadata.issuer,
|
||||
issuerLogo:
|
||||
state.context.selectedVc?.verifiableCredential?.issuerLogo ||
|
||||
getMosipLogo(),
|
||||
face:
|
||||
state.context.selectedVc?.verifiableCredential?.credential
|
||||
?.credentialSubject?.face ||
|
||||
state.context.selectedVc?.credential?.biometrics?.face,
|
||||
wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown,
|
||||
};
|
||||
return [
|
||||
{
|
||||
vcMetadata: vcMetadata,
|
||||
issuer: vcMetadata.issuer,
|
||||
issuerLogo:
|
||||
state.context.selectedVc?.verifiableCredential?.issuerLogo ||
|
||||
getMosipLogo(),
|
||||
face:
|
||||
state.context.selectedVc?.verifiableCredential?.credential
|
||||
?.credentialSubject?.face ||
|
||||
state.context.selectedVc?.credential?.biometrics?.face,
|
||||
wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function selectQrLoginRef(state: State) {
|
||||
@@ -71,6 +77,18 @@ export function selectIsSendingVc(state: State) {
|
||||
return state.matches('reviewing.sendingVc.inProgress');
|
||||
}
|
||||
|
||||
export function selectIsSendingVP(state: State) {
|
||||
return state.matches('startVPSharing.inProgress');
|
||||
}
|
||||
|
||||
export function selectIsSendingVPError(state: State) {
|
||||
return state.matches('startVPSharing.showError');
|
||||
}
|
||||
|
||||
export function selectIsSendingVPSuccess(state: State) {
|
||||
return state.matches('startVPSharing.success');
|
||||
}
|
||||
|
||||
export function selectIsFaceIdentityVerified(state: State) {
|
||||
return (
|
||||
state.matches('reviewing.sendingVc.inProgress') &&
|
||||
@@ -82,6 +100,10 @@ export function selectIsSendingVcTimeout(state: State) {
|
||||
return state.matches('reviewing.sendingVc.timeout');
|
||||
}
|
||||
|
||||
export function selectIsSendingVPTimeout(state: State) {
|
||||
return state.matches('startVPSharing.timeout');
|
||||
}
|
||||
|
||||
export function selectIsSent(state: State) {
|
||||
return state.matches('reviewing.sendingVc.sent');
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const model = createModel(
|
||||
|
||||
export const FaceScannerEvents = model.events;
|
||||
|
||||
export const createFaceScannerMachine = (vcImage: string) =>
|
||||
export const createFaceScannerMachine = (vcImages: string[]) =>
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QDMCGBjMBldqB2eYATgHQCWeZALiegBZjoDWFUACsQLZmyxkD2eAMQARAKIA5AJJiRiUAAd+fKgLzyQAD0QBaAIwAmAyQMBWAJwBmABzWADHoAslywfOOANCACeu0wHYANhJ-R389SzC3QJjLAF84rzRMHHxCUgpqWgZmVg4ibl41IQBxACUAQQkAFVkNJRU1DW0EHUc7YxtA6z09c2sza3NzQK9fVtdjO0s7fqHrAL1-U0CEpIxsXAJickoaBS4ePkERMEpIIQB5NkkAfSwxauqpCRKseuVqJqQtXW6Q6aGbrWGymIzWMa6AwzEiOCwrZwzbp6QL+NYgZKbNI7TL7Q5FE5nMgXCpsNi3ABilwAwgBVB5yH4NL6CZpQ-zGazhQKmUwdSIGVGQiZGEjTWahIYGOx2azOdGY1LbDJ7EhEMAARwArnBVHh2PjjsJylVaozFJ9VKyfi03I4TJFHI4YqYFkEucL9JY9LC9GDxX7zAYQQqNkr0rssurtbq8obiuJpHUmZbvqAWkt7Y5g45ek7A4EnZ7JmLvQ4ZbKrI5zGjEhiw1sI7ihGUxBURABND6Na3pvzBfwclzWQs80y9CE+RBTexcwWy8eFlyhlKNnawRusEiYin8LV4CBCCCCMC7ABu-CYp8Va9IG7SW53e4PCAoF9wVrwAG07ABdbssuoNq6JY5gkN6gSGCiIJ6HYoTLMKrrGMssH2P4lj+HYzp6CuWLKiQ94EI+Gy7vuh7EEQ-CkAoAA2qBUMgVGcNuDbYnem76ixmCkS+b78B+ag-v+KY9kBfYINKpgkCMrpOg42auIEljCu0PqymEMyyoEBi5qsdY3mxBEcVAQgUgAMlI5LUhUACyYiVABn5sggegLCE-gDCCBiDjWIL+MKM6yhygQLiiiK4eG67GaZFTUmItziLU1Jmo5aa-BMIUkNpMzOOYQUYaMU4SWY0lBiM3qWK6qKOAkdZ4PwEBwBoBn4bi2SMCw+r5IURqpb26VtHYwQYUOSlWEYNieiF1ghGEhg1g47jLvprGtaqBwFEcainOcEB9WJA3mD6fIVQYfozNKLjFsGJCmDMIxLTl47xCtq6GW1G09WoJREPgVCQPtzmDfaGEYQMvJOpYSmehE-jSX6rmQdM7gRNYEW3pGNDRjqsB6gam0EgdzJOcBLkeSE0odIO-go3B-lFTorqWCYc2RA4yxBAY6PvXsgOk20MIjdCY2TJNDPIrN4TBpB0puOY3P4YRlCcU+ZF8+JjMzbYjhcjW7TmHyQTCjYM2yehMojjpKIKxGSvEZgEj8FQPF7SJgFA5l2aonK4Rgk43mmAF2awmVmGVa5zpwjbUUPvq6sDTY9rZVhoH5UbRVOHYpXRNmZgebMXOvXhTacKgMDUqgChUFq6quxaolA+0YoxAjbgjrYRjCuhUlmO0Osophzq1usb3KvHLRtKYIODsLoGi5O4w6Ms9rwYYyxGL0ha1XEQA */
|
||||
model.createMachine(
|
||||
{
|
||||
@@ -198,12 +198,34 @@ export const createFaceScannerMachine = (vcImage: string) =>
|
||||
});
|
||||
},
|
||||
|
||||
verifyImage: context => {
|
||||
verifyImage: async context => {
|
||||
context.cameraRef.pausePreview();
|
||||
const rxDataURI =
|
||||
/data:(?<mime>[\w/\-.]+);(?<encoding>\w+),(?<data>.*)/;
|
||||
const matches = rxDataURI.exec(vcImage).groups;
|
||||
return faceCompare(context.capturedImage.base64, matches.data);
|
||||
|
||||
let isMatchFound = false;
|
||||
for (const vcImage of vcImages) {
|
||||
const matches = rxDataURI.exec(vcImage).groups;
|
||||
|
||||
try {
|
||||
isMatchFound = await faceCompare(
|
||||
context.capturedImage.base64,
|
||||
matches.data,
|
||||
);
|
||||
if (isMatchFound) {
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return isMatchFound;
|
||||
},
|
||||
|
||||
checkNetworkStatus: async () => {
|
||||
const state = await NetInfo.fetch();
|
||||
return state.isConnected;
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ export interface Typegen0 {
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
flipWhichCamera: 'FLIP_CAMERA';
|
||||
openSettings: 'OPEN_SETTINGS';
|
||||
setCameraRef: 'READY';
|
||||
setCaptureError: 'error.platform.faceScanner.capturing:invocation[0]';
|
||||
|
||||
193
machines/openID4VP/openID4VPActions.ts
Normal file
193
machines/openID4VP/openID4VPActions.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import {assign} from 'xstate';
|
||||
import {send, sendParent} from 'xstate/lib/actions';
|
||||
import {SHOW_FACE_AUTH_CONSENT_SHARE_FLOW} from '../../shared/constants';
|
||||
import {VC} from '../VerifiableCredential/VCMetaMachine/vc';
|
||||
import {StoreEvents} from '../store';
|
||||
|
||||
import {VCShareFlowType} from '../../shared/Utils';
|
||||
|
||||
// TODO - get this presentation definition list which are alias for scope param
|
||||
// from the verifier end point after the endpoint is created and exposed.
|
||||
|
||||
export const openID4VPActions = (model: any) => {
|
||||
return {
|
||||
setAuthenticationResponse: model.assign({
|
||||
authenticationResponse: (_, event) => event.data,
|
||||
}),
|
||||
|
||||
setEncodedAuthorizationRequest: model.assign({
|
||||
encodedAuthorizationRequest: (_, event) => event.encodedAuthRequest,
|
||||
}),
|
||||
|
||||
setFlowType: model.assign({
|
||||
flowType: (_, event) => event.flowType,
|
||||
}),
|
||||
|
||||
getVcsMatchingAuthRequest: model.assign({
|
||||
vcsMatchingAuthRequest: (context, event) => {
|
||||
let vcs = event.vcs;
|
||||
let matchingVCs = {} as Record<string, [VC]>;
|
||||
let presentationDefinition;
|
||||
const response = context.authenticationResponse;
|
||||
if ('presentation_definition' in response) {
|
||||
presentationDefinition = JSON.parse(
|
||||
response['presentation_definition'],
|
||||
);
|
||||
}
|
||||
vcs.forEach(vc => {
|
||||
presentationDefinition['input_descriptors'].forEach(
|
||||
inputDescriptor => {
|
||||
let isMatched = true;
|
||||
inputDescriptor.constraints.fields?.forEach(field => {
|
||||
field.path.forEach(path => {
|
||||
const pathSegments = path.substring(2).split('.');
|
||||
|
||||
const pathData = pathSegments.reduce(
|
||||
(obj, key) => obj?.[key],
|
||||
vc.verifiableCredential.credential,
|
||||
);
|
||||
|
||||
if (
|
||||
path === undefined ||
|
||||
(pathSegments[pathSegments.length - 1] !== 'type' &&
|
||||
(field.filter?.type !== typeof pathData ||
|
||||
!pathData.includes(field.filter?.pattern)))
|
||||
) {
|
||||
isMatched = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isMatched) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (isMatched) {
|
||||
matchingVCs[inputDescriptor.id]?.push(vc) ||
|
||||
(matchingVCs[inputDescriptor.id] = [vc]);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
return matchingVCs;
|
||||
},
|
||||
purpose: context => {
|
||||
const response = context.authenticationResponse;
|
||||
if ('presentation_definition' in response) {
|
||||
const pd = JSON.parse(response['presentation_definition']);
|
||||
return pd.purpose ?? '';
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
setSelectedVCs: model.assign({
|
||||
selectedVCs: (_, event) => event.selectedVCs,
|
||||
}),
|
||||
|
||||
compareAndStoreSelectedVC: model.assign({
|
||||
selectedVCs: context => {
|
||||
const matchingVcs = {};
|
||||
Object.entries(context.vcsMatchingAuthRequest).map(
|
||||
([inputDescriptorId, vcs]) =>
|
||||
(vcs as VC[]).map(vcData => {
|
||||
if (
|
||||
vcData.vcMetadata.requestId ===
|
||||
context.miniViewSelectedVC.vcMetadata.requestId
|
||||
) {
|
||||
matchingVcs[inputDescriptorId] = [vcData];
|
||||
}
|
||||
}),
|
||||
);
|
||||
return matchingVcs;
|
||||
},
|
||||
}),
|
||||
|
||||
setMiniViewShareSelectedVC: model.assign({
|
||||
miniViewSelectedVC: (_, event) => event.selectedVC,
|
||||
}),
|
||||
|
||||
setIsShareWithSelfie: model.assign({
|
||||
isShareWithSelfie: (_, event) =>
|
||||
event.flowType ===
|
||||
VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP,
|
||||
}),
|
||||
|
||||
setShowFaceAuthConsent: model.assign({
|
||||
showFaceAuthConsent: (_, event) => {
|
||||
return !event.isDoNotAskAgainChecked;
|
||||
},
|
||||
}),
|
||||
|
||||
storeShowFaceAuthConsent: send(
|
||||
(_, event) =>
|
||||
StoreEvents.SET(
|
||||
SHOW_FACE_AUTH_CONSENT_SHARE_FLOW,
|
||||
!event.isDoNotAskAgainChecked,
|
||||
),
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
getFaceAuthConsent: send(
|
||||
StoreEvents.GET(SHOW_FACE_AUTH_CONSENT_SHARE_FLOW),
|
||||
{
|
||||
to: (context: any) => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
updateShowFaceAuthConsent: model.assign({
|
||||
showFaceAuthConsent: (_, event) => {
|
||||
return event.response || event.response === null;
|
||||
},
|
||||
}),
|
||||
|
||||
forwardToParent: sendParent('DISMISS'),
|
||||
|
||||
setError: model.assign({
|
||||
error: (_, event) => {
|
||||
console.error('Error:', event.data.message);
|
||||
return event.data.message;
|
||||
},
|
||||
}),
|
||||
|
||||
resetError: model.assign({
|
||||
error: () => '',
|
||||
}),
|
||||
|
||||
loadKeyPair: assign({
|
||||
publicKey: (_, event: any) => event.data?.publicKey as string,
|
||||
privateKey: (context: any, event: any) =>
|
||||
event.data?.privateKey
|
||||
? event.data.privateKey
|
||||
: (context.privateKey as string),
|
||||
}),
|
||||
|
||||
incrementOpenID4VPRetryCount: model.assign({
|
||||
openID4VPRetryCount: context => context.openID4VPRetryCount + 1,
|
||||
}),
|
||||
|
||||
resetOpenID4VPRetryCount: model.assign({
|
||||
openID4VPRetryCount: () => 0,
|
||||
}),
|
||||
|
||||
setAuthenticationError: model.assign({
|
||||
error: (_, event) => {
|
||||
console.error('Error:', event.data.message);
|
||||
return 'vc validation - ' + event.data.message;
|
||||
},
|
||||
}),
|
||||
|
||||
setTrustedVerifiersApiCallError: model.assign({
|
||||
error: (_, event) => {
|
||||
console.error('Error:', event.data.message);
|
||||
return 'api error - ' + event.data.message;
|
||||
},
|
||||
}),
|
||||
|
||||
setTrustedVerifiers: model.assign({
|
||||
trustedVerifiers: (_: any, event: any) => event.data.response.verifiers,
|
||||
}),
|
||||
};
|
||||
};
|
||||
33
machines/openID4VP/openID4VPGuards.ts
Normal file
33
machines/openID4VP/openID4VPGuards.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {VCShareFlowType} from '../../shared/Utils';
|
||||
|
||||
export const openID4VPGuards = () => {
|
||||
return {
|
||||
showFaceAuthConsentScreen: (context, event) => {
|
||||
return context.showFaceAuthConsent && context.isShareWithSelfie;
|
||||
},
|
||||
|
||||
isShareWithSelfie: context => context.isShareWithSelfie,
|
||||
|
||||
isSimpleOpenID4VPShare: context =>
|
||||
context.flowType === VCShareFlowType.OPENID4VP,
|
||||
|
||||
isSelectedVCMatchingRequest: context =>
|
||||
Object.values(context.selectedVCs).length === 1,
|
||||
|
||||
isFlowTypeSimpleShare: context =>
|
||||
context.flowType === VCShareFlowType.SIMPLE_SHARE,
|
||||
|
||||
hasKeyPair: (context: any) => {
|
||||
return !!context.publicKey;
|
||||
},
|
||||
|
||||
isAnyVCHasImage: (context: any) => {
|
||||
const hasImage = Object.values(context.selectedVCs)
|
||||
.flatMap(vc => vc)
|
||||
.some(
|
||||
vc => vc.verifiableCredential?.credential?.credentialSubject.face,
|
||||
);
|
||||
return !!hasImage;
|
||||
},
|
||||
};
|
||||
};
|
||||
335
machines/openID4VP/openID4VPMachine.ts
Normal file
335
machines/openID4VP/openID4VPMachine.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
import {EventFrom} from 'xstate';
|
||||
import {openID4VPModel} from './openID4VPModel';
|
||||
import {openID4VPServices} from './openID4VPServices';
|
||||
import {openID4VPActions} from './openID4VPActions';
|
||||
import {AppServices} from '../../shared/GlobalContext';
|
||||
import {openID4VPGuards} from './openID4VPGuards';
|
||||
import {send, sendParent} from 'xstate/lib/actions';
|
||||
|
||||
const model = openID4VPModel;
|
||||
|
||||
export const OpenID4VPEvents = model.events;
|
||||
|
||||
export const openID4VPMachine = model.createMachine(
|
||||
{
|
||||
predictableActionArguments: true,
|
||||
preserveActionOrder: true,
|
||||
tsTypes: {} as import('./openID4VPMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
context: model.initialContext,
|
||||
events: {} as EventFrom<typeof model>,
|
||||
},
|
||||
id: 'OpenID4VP',
|
||||
initial: 'waitingForData',
|
||||
|
||||
states: {
|
||||
waitingForData: {
|
||||
on: {
|
||||
AUTHENTICATE: {
|
||||
actions: [
|
||||
'setEncodedAuthorizationRequest',
|
||||
'setFlowType',
|
||||
'setMiniViewShareSelectedVC',
|
||||
'setIsShareWithSelfie',
|
||||
],
|
||||
target: 'checkFaceAuthConsent',
|
||||
},
|
||||
},
|
||||
},
|
||||
checkFaceAuthConsent: {
|
||||
entry: 'getFaceAuthConsent',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: 'updateShowFaceAuthConsent',
|
||||
target: 'getTrustedVerifiersList',
|
||||
},
|
||||
},
|
||||
},
|
||||
getTrustedVerifiersList: {
|
||||
invoke: {
|
||||
src: 'fetchTrustedVerifiers',
|
||||
onDone: {
|
||||
actions: 'setTrustedVerifiers',
|
||||
target: 'getKeyPairFromKeystore',
|
||||
},
|
||||
onError: {
|
||||
actions: 'setTrustedVerifiersApiCallError',
|
||||
},
|
||||
},
|
||||
},
|
||||
getKeyPairFromKeystore: {
|
||||
invoke: {
|
||||
src: 'getKeyPair',
|
||||
onDone: {
|
||||
actions: ['loadKeyPair'],
|
||||
target: 'checkKeyPair',
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
actions: 'setError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
checkKeyPair: {
|
||||
description: 'checks whether key pair is generated',
|
||||
invoke: {
|
||||
src: 'getSelectedKey',
|
||||
onDone: {
|
||||
cond: 'hasKeyPair',
|
||||
target: 'authenticateVerifier',
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
actions: 'setError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
authenticateVerifier: {
|
||||
invoke: {
|
||||
src: 'getAuthenticationResponse',
|
||||
onDone: {
|
||||
actions: 'setAuthenticationResponse',
|
||||
target: 'getVCsSatisfyingAuthRequest',
|
||||
},
|
||||
onError: {
|
||||
actions: 'setAuthenticationError',
|
||||
target: 'showError',
|
||||
},
|
||||
},
|
||||
},
|
||||
getVCsSatisfyingAuthRequest: {
|
||||
on: {
|
||||
DOWNLOADED_VCS: [
|
||||
{
|
||||
cond: 'isSimpleOpenID4VPShare',
|
||||
actions: 'getVcsMatchingAuthRequest',
|
||||
target: 'selectingVCs',
|
||||
},
|
||||
{
|
||||
actions: 'getVcsMatchingAuthRequest',
|
||||
target: 'setSelectedVC',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
setSelectedVC: {
|
||||
entry: send('SET_SELECTED_VC'),
|
||||
on: {
|
||||
SET_SELECTED_VC: [
|
||||
{
|
||||
actions: 'compareAndStoreSelectedVC',
|
||||
target: 'checkIfMatchingVCsHasSelectedVC',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
checkIfMatchingVCsHasSelectedVC: {
|
||||
entry: send('CHECK_SELECTED_VC'),
|
||||
on: {
|
||||
CHECK_SELECTED_VC: [
|
||||
{
|
||||
cond: 'isSelectedVCMatchingRequest',
|
||||
target: 'getConsentForVPSharing',
|
||||
},
|
||||
{
|
||||
actions: [
|
||||
model.assign({
|
||||
error: () => 'credential mismatch detected',
|
||||
}),
|
||||
],
|
||||
target: 'showError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selectingVCs: {
|
||||
on: {
|
||||
VERIFY_AND_ACCEPT_REQUEST: {
|
||||
actions: [
|
||||
'setSelectedVCs',
|
||||
model.assign({isShareWithSelfie: () => true}),
|
||||
],
|
||||
target: 'getConsentForVPSharing',
|
||||
},
|
||||
ACCEPT_REQUEST: {
|
||||
target: 'getConsentForVPSharing',
|
||||
actions: [
|
||||
'setSelectedVCs',
|
||||
'setShareLogTypeUnverified',
|
||||
'resetFaceCaptureBannerStatus',
|
||||
],
|
||||
},
|
||||
CANCEL: {
|
||||
actions: 'forwardToParent',
|
||||
target: 'waitingForData',
|
||||
},
|
||||
},
|
||||
},
|
||||
getConsentForVPSharing: {
|
||||
on: {
|
||||
CONFIRM: [
|
||||
{
|
||||
cond: 'showFaceAuthConsentScreen',
|
||||
target: 'faceVerificationConsent',
|
||||
},
|
||||
{
|
||||
cond: 'isShareWithSelfie',
|
||||
target: 'checkIfAnySelectedVCHasImage',
|
||||
},
|
||||
{
|
||||
target: 'sendingVP',
|
||||
},
|
||||
],
|
||||
CANCEL: {
|
||||
target: 'showConfirmationPopup',
|
||||
},
|
||||
},
|
||||
},
|
||||
showConfirmationPopup: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
actions: sendParent('DISMISS'),
|
||||
},
|
||||
GO_BACK: {
|
||||
target: 'getConsentForVPSharing',
|
||||
},
|
||||
},
|
||||
},
|
||||
faceVerificationConsent: {
|
||||
on: {
|
||||
FACE_VERIFICATION_CONSENT: [
|
||||
{
|
||||
cond: 'isSimpleOpenID4VPShare',
|
||||
actions: ['setShowFaceAuthConsent', 'storeShowFaceAuthConsent'],
|
||||
target: 'checkIfAnySelectedVCHasImage',
|
||||
},
|
||||
{
|
||||
actions: ['setShowFaceAuthConsent', 'storeShowFaceAuthConsent'],
|
||||
target: 'verifyingIdentity',
|
||||
},
|
||||
],
|
||||
DISMISS: [
|
||||
{
|
||||
cond: 'isSimpleOpenID4VPShare',
|
||||
target: 'selectingVCs',
|
||||
},
|
||||
{
|
||||
actions: sendParent('DISMISS'),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
checkIfAnySelectedVCHasImage: {
|
||||
entry: send('CHECK_FOR_IMAGE'),
|
||||
on: {
|
||||
CHECK_FOR_IMAGE: [
|
||||
{
|
||||
cond: 'isAnyVCHasImage',
|
||||
target: 'verifyingIdentity',
|
||||
},
|
||||
{
|
||||
actions: model.assign({
|
||||
error: () => 'none of the selected VC has image',
|
||||
}),
|
||||
target: 'showError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
verifyingIdentity: {
|
||||
on: {
|
||||
FACE_VALID: [
|
||||
{
|
||||
cond: 'hasKeyPair',
|
||||
target: 'sendingVP',
|
||||
},
|
||||
{
|
||||
target: 'checkKeyPair',
|
||||
},
|
||||
],
|
||||
FACE_INVALID: {
|
||||
target: 'invalidIdentity',
|
||||
actions: 'logFailedVerification',
|
||||
},
|
||||
CANCEL: [
|
||||
{
|
||||
cond: 'isSimpleOpenID4VPShare',
|
||||
actions: model.assign({isShareWithSelfie: () => false}),
|
||||
target: 'selectingVCs',
|
||||
},
|
||||
{
|
||||
actions: sendParent('DISMISS'),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
invalidIdentity: {
|
||||
on: {
|
||||
DISMISS: [
|
||||
{
|
||||
cond: 'isSimpleOpenID4VPShare',
|
||||
target: 'selectingVCs',
|
||||
},
|
||||
{
|
||||
actions: sendParent('DISMISS'),
|
||||
},
|
||||
],
|
||||
RETRY_VERIFICATION: {
|
||||
target: 'verifyingIdentity',
|
||||
},
|
||||
},
|
||||
},
|
||||
sendingVP: {
|
||||
entry: sendParent('IN_PROGRESS'),
|
||||
invoke: {
|
||||
src: 'sendVP',
|
||||
onDone: {
|
||||
actions: sendParent('SUCCESS'),
|
||||
target: 'success',
|
||||
},
|
||||
onError: {
|
||||
actions: ['setError', sendParent('SHOW_ERROR')],
|
||||
target: 'showError',
|
||||
},
|
||||
},
|
||||
after: {
|
||||
SHARING_TIMEOUT: {
|
||||
actions: sendParent('TIMEOUT'),
|
||||
},
|
||||
},
|
||||
},
|
||||
showError: {
|
||||
on: {
|
||||
RETRY: {
|
||||
actions: ['incrementOpenID4VPRetryCount'],
|
||||
target: 'sendingVP',
|
||||
},
|
||||
RESET_RETRY_COUNT: {
|
||||
actions: 'resetOpenID4VPRetryCount',
|
||||
},
|
||||
RESET_ERROR: {
|
||||
actions: 'resetError',
|
||||
},
|
||||
},
|
||||
},
|
||||
success: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: openID4VPActions(model),
|
||||
services: openID4VPServices(),
|
||||
guards: openID4VPGuards(),
|
||||
delays: {
|
||||
SHARING_TIMEOUT: 15 * 1000,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export function createOpenID4VPMachine(serviceRefs: AppServices) {
|
||||
return openID4VPMachine.withContext({
|
||||
...openID4VPMachine.context,
|
||||
serviceRefs,
|
||||
});
|
||||
}
|
||||
177
machines/openID4VP/openID4VPMachine.typegen.ts
Normal file
177
machines/openID4VP/openID4VPMachine.typegen.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
'done.invoke.OpenID4VP.authenticateVerifier:invocation[0]': {
|
||||
type: 'done.invoke.OpenID4VP.authenticateVerifier:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.OpenID4VP.checkKeyPair:invocation[0]': {
|
||||
type: 'done.invoke.OpenID4VP.checkKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.OpenID4VP.getKeyPairFromKeystore:invocation[0]': {
|
||||
type: 'done.invoke.OpenID4VP.getKeyPairFromKeystore:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.OpenID4VP.getTrustedVerifiersList:invocation[0]': {
|
||||
type: 'done.invoke.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.OpenID4VP.sendingVP:invocation[0]': {
|
||||
type: 'done.invoke.OpenID4VP.sendingVP:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform.OpenID4VP.authenticateVerifier:invocation[0]': {
|
||||
type: 'error.platform.OpenID4VP.authenticateVerifier:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.OpenID4VP.checkKeyPair:invocation[0]': {
|
||||
type: 'error.platform.OpenID4VP.checkKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.OpenID4VP.getKeyPairFromKeystore:invocation[0]': {
|
||||
type: 'error.platform.OpenID4VP.getKeyPairFromKeystore:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.OpenID4VP.getTrustedVerifiersList:invocation[0]': {
|
||||
type: 'error.platform.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.OpenID4VP.sendingVP:invocation[0]': {
|
||||
type: 'error.platform.OpenID4VP.sendingVP:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
fetchTrustedVerifiers: 'done.invoke.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
getAuthenticationResponse: 'done.invoke.OpenID4VP.authenticateVerifier:invocation[0]';
|
||||
getKeyPair: 'done.invoke.OpenID4VP.getKeyPairFromKeystore:invocation[0]';
|
||||
getSelectedKey: 'done.invoke.OpenID4VP.checkKeyPair:invocation[0]';
|
||||
sendVP: 'done.invoke.OpenID4VP.sendingVP:invocation[0]';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions:
|
||||
| 'compareAndStoreSelectedVC'
|
||||
| 'forwardToParent'
|
||||
| 'getFaceAuthConsent'
|
||||
| 'getVcsMatchingAuthRequest'
|
||||
| 'incrementOpenID4VPRetryCount'
|
||||
| 'loadKeyPair'
|
||||
| 'logFailedVerification'
|
||||
| 'resetError'
|
||||
| 'resetFaceCaptureBannerStatus'
|
||||
| 'resetOpenID4VPRetryCount'
|
||||
| 'setAuthenticationError'
|
||||
| 'setAuthenticationResponse'
|
||||
| 'setEncodedAuthorizationRequest'
|
||||
| 'setError'
|
||||
| 'setFlowType'
|
||||
| 'setIsShareWithSelfie'
|
||||
| 'setMiniViewShareSelectedVC'
|
||||
| 'setSelectedVCs'
|
||||
| 'setShareLogTypeUnverified'
|
||||
| 'setShowFaceAuthConsent'
|
||||
| 'setTrustedVerifiers'
|
||||
| 'setTrustedVerifiersApiCallError'
|
||||
| 'storeShowFaceAuthConsent'
|
||||
| 'updateShowFaceAuthConsent';
|
||||
delays: never;
|
||||
guards:
|
||||
| 'hasKeyPair'
|
||||
| 'isAnyVCHasImage'
|
||||
| 'isSelectedVCMatchingRequest'
|
||||
| 'isShareWithSelfie'
|
||||
| 'isSimpleOpenID4VPShare'
|
||||
| 'showFaceAuthConsentScreen';
|
||||
services:
|
||||
| 'fetchTrustedVerifiers'
|
||||
| 'getAuthenticationResponse'
|
||||
| 'getKeyPair'
|
||||
| 'getSelectedKey'
|
||||
| 'sendVP';
|
||||
};
|
||||
eventsCausingActions: {
|
||||
compareAndStoreSelectedVC: 'SET_SELECTED_VC';
|
||||
forwardToParent: 'CANCEL';
|
||||
getFaceAuthConsent: 'AUTHENTICATE';
|
||||
getVcsMatchingAuthRequest: 'DOWNLOADED_VCS';
|
||||
incrementOpenID4VPRetryCount: 'RETRY';
|
||||
loadKeyPair: 'done.invoke.OpenID4VP.getKeyPairFromKeystore:invocation[0]';
|
||||
logFailedVerification: 'FACE_INVALID';
|
||||
resetError: 'RESET_ERROR';
|
||||
resetFaceCaptureBannerStatus: 'ACCEPT_REQUEST';
|
||||
resetOpenID4VPRetryCount: 'RESET_RETRY_COUNT';
|
||||
setAuthenticationError: 'error.platform.OpenID4VP.authenticateVerifier:invocation[0]';
|
||||
setAuthenticationResponse: 'done.invoke.OpenID4VP.authenticateVerifier:invocation[0]';
|
||||
setEncodedAuthorizationRequest: 'AUTHENTICATE';
|
||||
setError:
|
||||
| 'error.platform.OpenID4VP.checkKeyPair:invocation[0]'
|
||||
| 'error.platform.OpenID4VP.getKeyPairFromKeystore:invocation[0]'
|
||||
| 'error.platform.OpenID4VP.sendingVP:invocation[0]';
|
||||
setFlowType: 'AUTHENTICATE';
|
||||
setIsShareWithSelfie: 'AUTHENTICATE';
|
||||
setMiniViewShareSelectedVC: 'AUTHENTICATE';
|
||||
setSelectedVCs: 'ACCEPT_REQUEST' | 'VERIFY_AND_ACCEPT_REQUEST';
|
||||
setShareLogTypeUnverified: 'ACCEPT_REQUEST';
|
||||
setShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
|
||||
setTrustedVerifiers: 'done.invoke.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
setTrustedVerifiersApiCallError: 'error.platform.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
storeShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
|
||||
updateShowFaceAuthConsent: 'STORE_RESPONSE';
|
||||
};
|
||||
eventsCausingDelays: {
|
||||
SHARING_TIMEOUT: 'CONFIRM' | 'FACE_VALID' | 'RETRY';
|
||||
};
|
||||
eventsCausingGuards: {
|
||||
hasKeyPair:
|
||||
| 'FACE_VALID'
|
||||
| 'done.invoke.OpenID4VP.checkKeyPair:invocation[0]';
|
||||
isAnyVCHasImage: 'CHECK_FOR_IMAGE';
|
||||
isSelectedVCMatchingRequest: 'CHECK_SELECTED_VC';
|
||||
isShareWithSelfie: 'CONFIRM';
|
||||
isSimpleOpenID4VPShare:
|
||||
| 'CANCEL'
|
||||
| 'DISMISS'
|
||||
| 'DOWNLOADED_VCS'
|
||||
| 'FACE_VERIFICATION_CONSENT';
|
||||
showFaceAuthConsentScreen: 'CONFIRM';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
fetchTrustedVerifiers: 'STORE_RESPONSE';
|
||||
getAuthenticationResponse: 'done.invoke.OpenID4VP.checkKeyPair:invocation[0]';
|
||||
getKeyPair: 'done.invoke.OpenID4VP.getTrustedVerifiersList:invocation[0]';
|
||||
getSelectedKey:
|
||||
| 'FACE_VALID'
|
||||
| 'done.invoke.OpenID4VP.getKeyPairFromKeystore:invocation[0]';
|
||||
sendVP: 'CONFIRM' | 'FACE_VALID' | 'RETRY';
|
||||
};
|
||||
matchesStates:
|
||||
| 'authenticateVerifier'
|
||||
| 'checkFaceAuthConsent'
|
||||
| 'checkIfAnySelectedVCHasImage'
|
||||
| 'checkIfMatchingVCsHasSelectedVC'
|
||||
| 'checkKeyPair'
|
||||
| 'faceVerificationConsent'
|
||||
| 'getConsentForVPSharing'
|
||||
| 'getKeyPairFromKeystore'
|
||||
| 'getTrustedVerifiersList'
|
||||
| 'getVCsSatisfyingAuthRequest'
|
||||
| 'invalidIdentity'
|
||||
| 'selectingVCs'
|
||||
| 'sendingVP'
|
||||
| 'setSelectedVC'
|
||||
| 'showConfirmationPopup'
|
||||
| 'showError'
|
||||
| 'success'
|
||||
| 'verifyingIdentity'
|
||||
| 'waitingForData';
|
||||
tags: never;
|
||||
}
|
||||
68
machines/openID4VP/openID4VPModel.ts
Normal file
68
machines/openID4VP/openID4VPModel.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {AppServices} from '../../shared/GlobalContext';
|
||||
import {VC} from '../VerifiableCredential/VCMetaMachine/vc';
|
||||
import {KeyTypes} from '../../shared/cryptoutil/KeyTypes';
|
||||
const openID4VPEvents = {
|
||||
AUTHENTICATE: (
|
||||
encodedAuthRequest: string,
|
||||
flowType: string,
|
||||
selectedVC: any,
|
||||
) => ({encodedAuthRequest, flowType, selectedVC}),
|
||||
DOWNLOADED_VCS: (vcs: VC[]) => ({vcs}),
|
||||
SELECT_VC: (vcKey: string, inputDescriptorId: any) => ({
|
||||
vcKey,
|
||||
inputDescriptorId,
|
||||
}),
|
||||
ACCEPT_REQUEST: (selectedVCs: Record<string, VC[]>) => ({
|
||||
selectedVCs,
|
||||
}),
|
||||
VERIFY_AND_ACCEPT_REQUEST: (selectedVCs: Record<string, VC[]>) => ({
|
||||
selectedVCs,
|
||||
}),
|
||||
CONFIRM: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) => ({
|
||||
isDoNotAskAgainChecked,
|
||||
}),
|
||||
FACE_VALID: () => ({}),
|
||||
FACE_INVALID: () => ({}),
|
||||
DISMISS: () => ({}),
|
||||
RETRY_VERIFICATION: () => ({}),
|
||||
STORE_RESPONSE: (response: any) => ({response}),
|
||||
GO_BACK: () => ({}),
|
||||
CHECK_SELECTED_VC: () => ({}),
|
||||
SET_SELECTED_VC: () => ({}),
|
||||
CHECK_FOR_IMAGE: () => ({}),
|
||||
RETRY: () => ({}),
|
||||
RESET_RETRY_COUNT: () => ({}),
|
||||
RESET_ERROR: () => ({}),
|
||||
};
|
||||
|
||||
export const openID4VPModel = createModel(
|
||||
{
|
||||
serviceRefs: {} as AppServices,
|
||||
encodedAuthorizationRequest: '' as string,
|
||||
authenticationResponse: {},
|
||||
vcsMatchingAuthRequest: {} as Record<string, VC[]>,
|
||||
checkedAll: false as boolean,
|
||||
selectedVCs: {} as Record<string, VC[]>,
|
||||
isShareWithSelfie: false as boolean,
|
||||
showFaceAuthConsent: true as boolean,
|
||||
purpose: '' as string,
|
||||
error: '' as string,
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
keyType: KeyTypes.RS256,
|
||||
flowType: '' as string,
|
||||
miniViewSelectedVC: {} as VC,
|
||||
openID4VPRetryCount: 0,
|
||||
trustedVerifiers: [] as VerifierType[],
|
||||
},
|
||||
{events: openID4VPEvents},
|
||||
);
|
||||
|
||||
interface VerifierType {
|
||||
client_id: string;
|
||||
redirect_uri: string;
|
||||
response_uri: string;
|
||||
}
|
||||
96
machines/openID4VP/openID4VPSelectors.ts
Normal file
96
machines/openID4VP/openID4VPSelectors.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import {StateFrom} from 'xstate';
|
||||
import {openID4VPMachine} from './openID4VPMachine';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {getMosipLogo} from '../../components/VC/common/VCUtils';
|
||||
import {VerifiableCredentialData} from '../VerifiableCredential/VCMetaMachine/vc';
|
||||
|
||||
type State = StateFrom<typeof openID4VPMachine>;
|
||||
|
||||
export function selectIsGetVCsSatisfyingAuthRequest(state: State) {
|
||||
return state.matches('getVCsSatisfyingAuthRequest');
|
||||
}
|
||||
|
||||
export function selectVCsMatchingAuthRequest(state: State) {
|
||||
return state.context.vcsMatchingAuthRequest;
|
||||
}
|
||||
|
||||
export function selectSelectedVCs(state: State) {
|
||||
return state.context.selectedVCs;
|
||||
}
|
||||
|
||||
export function selectAreAllVCsChecked(state: State) {
|
||||
return state.context.checkedAll;
|
||||
}
|
||||
|
||||
export function selectIsGetVPSharingConsent(state: State) {
|
||||
return state.matches('getConsentForVPSharing');
|
||||
}
|
||||
|
||||
export function selectIsFaceVerificationConsent(state: State) {
|
||||
return state.matches('faceVerificationConsent');
|
||||
}
|
||||
|
||||
export function selectIsVerifyingIdentity(state: State) {
|
||||
return state.matches('verifyingIdentity');
|
||||
}
|
||||
|
||||
export function selectIsInvalidIdentity(state: State) {
|
||||
return state.matches('invalidIdentity');
|
||||
}
|
||||
|
||||
export function selectIsSharingVP(state: State) {
|
||||
return state.matches('sendingVP');
|
||||
}
|
||||
|
||||
export function selectCredentials(state: State) {
|
||||
let selectedCredentials: Credential[] = [];
|
||||
Object.values(state.context.selectedVCs).map(vcs => {
|
||||
vcs.map(vcData => {
|
||||
const credential =
|
||||
vcData?.verifiableCredential?.credential ||
|
||||
vcData?.verifiableCredential;
|
||||
selectedCredentials.push(credential);
|
||||
});
|
||||
});
|
||||
return selectedCredentials;
|
||||
}
|
||||
|
||||
export function selectVerifiableCredentialsData(state: State) {
|
||||
let verifiableCredentialsData: VerifiableCredentialData[] = [];
|
||||
Object.values(state.context.selectedVCs).map(vcs => {
|
||||
vcs.map(vcData => {
|
||||
const vcMetadata = new VCMetadata(vcData.vcMetadata);
|
||||
verifiableCredentialsData.push({
|
||||
vcMetadata: vcMetadata,
|
||||
issuer: vcMetadata.issuer,
|
||||
issuerLogo: vcData?.verifiableCredential?.issuerLogo || getMosipLogo(),
|
||||
face:
|
||||
vcData?.verifiableCredential?.credential?.credentialSubject?.face ||
|
||||
vcData?.credential?.biometrics?.face,
|
||||
wellKnown: vcData?.verifiableCredential?.wellKnown,
|
||||
credentialTypes: vcData?.verifiableCredential?.credentialTypes,
|
||||
});
|
||||
});
|
||||
});
|
||||
return verifiableCredentialsData;
|
||||
}
|
||||
|
||||
export function selectPurpose(state: State) {
|
||||
return state.context.purpose;
|
||||
}
|
||||
|
||||
export function selectShowConfirmationPopup(state: State) {
|
||||
return state.matches('showConfirmationPopup');
|
||||
}
|
||||
|
||||
export function selectIsSelectingVcs(state: State) {
|
||||
return state.matches('selectingVCs');
|
||||
}
|
||||
|
||||
export function selectIsError(state: State) {
|
||||
return state.context.error;
|
||||
}
|
||||
|
||||
export function selectOpenID4VPRetryCount(state: State) {
|
||||
return state.context.openID4VPRetryCount;
|
||||
}
|
||||
56
machines/openID4VP/openID4VPServices.ts
Normal file
56
machines/openID4VP/openID4VPServices.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {CACHED_API} from '../../shared/api';
|
||||
import {fetchKeyPair} from '../../shared/cryptoutil/cryptoUtil';
|
||||
import {
|
||||
constructProofJWT,
|
||||
OpenID4VP,
|
||||
OpenID4VP_Domain,
|
||||
OpenID4VP_Proof_Algo_Type,
|
||||
} from '../../shared/openID4VP/OpenID4VP';
|
||||
|
||||
export const openID4VPServices = () => {
|
||||
return {
|
||||
fetchTrustedVerifiers: async () => {
|
||||
return await CACHED_API.fetchTrustedVerifiersList();
|
||||
},
|
||||
|
||||
getAuthenticationResponse: (context: any) => async () => {
|
||||
OpenID4VP.initialize();
|
||||
const serviceRes = await OpenID4VP.authenticateVerifier(
|
||||
context.encodedAuthorizationRequest,
|
||||
context.trustedVerifiers,
|
||||
);
|
||||
return serviceRes;
|
||||
},
|
||||
|
||||
getKeyPair: async (context: any) => {
|
||||
if (!!(await fetchKeyPair(context.keyType)).publicKey) {
|
||||
return await fetchKeyPair(context.keyType);
|
||||
}
|
||||
},
|
||||
|
||||
getSelectedKey: async (context: any) => {
|
||||
return await fetchKeyPair(context.keyType);
|
||||
},
|
||||
|
||||
sendVP: (context: any) => async () => {
|
||||
const vpToken = await OpenID4VP.constructVerifiablePresentationToken(
|
||||
context.selectedVCs,
|
||||
);
|
||||
|
||||
const proofJWT = await constructProofJWT(
|
||||
context.publicKey,
|
||||
context.privateKey,
|
||||
JSON.parse(vpToken),
|
||||
context.keyType,
|
||||
);
|
||||
|
||||
const vpResponseMetadata = {
|
||||
jws: proofJWT,
|
||||
signatureAlgorithm: OpenID4VP_Proof_Algo_Type,
|
||||
publicKey: context.publicKey,
|
||||
domain: OpenID4VP_Domain,
|
||||
};
|
||||
return await OpenID4VP.shareVerifiablePresentation(vpResponseMetadata);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export const BOTTOM_TAB_ROUTES = {
|
||||
export const SCAN_ROUTES = {
|
||||
ScanScreen: 'ScanScreen' as keyof ScanStackParamList,
|
||||
SendVcScreen: 'SendVcScreen' as keyof ScanStackParamList,
|
||||
SendVPScreen: 'SendVPScreen' as keyof ScanStackParamList,
|
||||
};
|
||||
|
||||
export const REQUEST_ROUTES = {
|
||||
@@ -25,6 +26,7 @@ export const SETTINGS_ROUTES = {
|
||||
export type ScanStackParamList = {
|
||||
ScanScreen: undefined;
|
||||
SendVcScreen: undefined;
|
||||
SendVPScreen: undefined;
|
||||
};
|
||||
|
||||
export type RequestStackParamList = {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button, Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { useReceiveVcScreen } from './ReceiveVcScreenController';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useOverlayVisibleAfterTimeout } from '../../shared/hooks/useOverlayVisibleAfterTimeout';
|
||||
import { VcDetailsContainer } from '../../components/VC/VcDetailsContainer';
|
||||
import { SharingStatusModal } from '../Scan/SharingStatusModal';
|
||||
import { SvgImage } from '../../components/ui/svg';
|
||||
import { DETAIL_VIEW_DEFAULT_FIELDS } from '../../components/VC/common/VCUtils';
|
||||
import { getDetailedViewFields } from '../../shared/openId4VCI/Utils';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {DeviceInfoList} from '../../components/DeviceInfoList';
|
||||
import {Button, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {useReceiveVcScreen} from './ReceiveVcScreenController';
|
||||
import {MessageOverlay} from '../../components/MessageOverlay';
|
||||
import {useOverlayVisibleAfterTimeout} from '../../shared/hooks/useOverlayVisibleAfterTimeout';
|
||||
import {VcDetailsContainer} from '../../components/VC/VcDetailsContainer';
|
||||
import {SharingStatusModal} from '../Scan/SharingStatusModal';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
import {DETAIL_VIEW_DEFAULT_FIELDS} from '../../components/VC/common/VCUtils';
|
||||
import {getDetailedViewFields} from '../../shared/openId4VCI/Utils';
|
||||
|
||||
export const ReceiveVcScreen: React.FC = () => {
|
||||
const {t} = useTranslation('ReceiveVcScreen');
|
||||
|
||||
@@ -16,13 +16,13 @@ import {View, I18nManager} from 'react-native';
|
||||
import {Text} from './../../components/ui';
|
||||
import {BannerStatusType} from '../../components/BannerNotification';
|
||||
import {LIVENESS_CHECK} from '../../shared/constants';
|
||||
import {SendVPScreen} from './SendVPScreen';
|
||||
|
||||
const ScanStack = createNativeStackNavigator();
|
||||
|
||||
export const ScanLayout: React.FC = () => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
const controller = useScanLayout();
|
||||
|
||||
if (
|
||||
controller.statusOverlay != null &&
|
||||
!controller.isAccepted &&
|
||||
@@ -37,7 +37,8 @@ export const ScanLayout: React.FC = () => {
|
||||
isHintVisible={
|
||||
controller.isStayInProgress ||
|
||||
controller.isBleError ||
|
||||
controller.isSendingVc
|
||||
controller.isSendingVc ||
|
||||
controller.isSendingVP
|
||||
}
|
||||
onRetry={controller.statusOverlay?.onRetry}
|
||||
showBanner={controller.isFaceIdentityVerified}
|
||||
@@ -113,10 +114,43 @@ export const ScanLayout: React.FC = () => {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{controller.openID4VPFlowType === VCShareFlowType.OPENID4VP && (
|
||||
<ScanStack.Screen
|
||||
name={SCAN_ROUTES.SendVPScreen}
|
||||
component={SendVPScreen}
|
||||
options={{
|
||||
title: t('SendVPScreen:requester'),
|
||||
headerTitle: props => (
|
||||
<View style={Theme.Styles.scanLayoutHeaderContainer}>
|
||||
<Text style={Theme.Styles.scanLayoutHeaderTitle}>
|
||||
{props.children}
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
headerBackVisible: false,
|
||||
headerRight: () =>
|
||||
!I18nManager.isRTL && (
|
||||
<Icon
|
||||
name="close"
|
||||
color={Theme.Colors.blackIcon}
|
||||
onPress={controller.DISMISS}
|
||||
/>
|
||||
),
|
||||
headerLeft: () =>
|
||||
I18nManager.isRTL && (
|
||||
<Icon
|
||||
name="close"
|
||||
color={Theme.Colors.blackIcon}
|
||||
onPress={controller.DISMISS}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ScanStack.Navigator>
|
||||
|
||||
<SharingStatusModal
|
||||
isVisible={controller.isAccepted}
|
||||
isVisible={controller.isAccepted || controller.isVPSharingSuccess}
|
||||
testId={'sharingSuccessModal'}
|
||||
buttonStatus={'homeAndHistoryIcons'}
|
||||
title={t('status.accepted.title')}
|
||||
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
selectIsFaceIdentityVerified,
|
||||
selectCredential,
|
||||
selectVerifiableCredentialData,
|
||||
selectIsSendingVPTimeout,
|
||||
selectIsSendingVP,
|
||||
selectIsQrLoginViaDeepLink,
|
||||
selectOpenID4VPFlowType,
|
||||
selectIsSendingVPSuccess,
|
||||
} from '../../machines/bleShare/scan/scanSelectors';
|
||||
import {
|
||||
selectBleError,
|
||||
@@ -67,6 +71,7 @@ export function useScanLayout() {
|
||||
const isBleError = useSelector(scanService, selectIsHandlingBleError);
|
||||
const isInvalidIdentity = useSelector(scanService, selectIsInvalidIdentity);
|
||||
const flowType = useSelector(scanService, selectFlowType);
|
||||
const openID4VPFlowType = useSelector(scanService, selectOpenID4VPFlowType);
|
||||
const isVerifyingIdentity = useSelector(
|
||||
scanService,
|
||||
selectIsVerifyingIdentity,
|
||||
@@ -133,9 +138,12 @@ export function useScanLayout() {
|
||||
const isSent = useSelector(scanService, selectIsSent);
|
||||
const isOffline = useSelector(scanService, selectIsOffline);
|
||||
const isSendingVc = useSelector(scanService, selectIsSendingVc);
|
||||
const isSendingVP = useSelector(scanService, selectIsSendingVP);
|
||||
const isSendingVcTimeout = useSelector(scanService, selectIsSendingVcTimeout);
|
||||
const isSendingVPTimeout = useSelector(scanService, selectIsSendingVPTimeout);
|
||||
const isDisconnected = useSelector(scanService, selectIsDisconnected);
|
||||
const isStayInProgress = isConnectingTimeout || isSendingVcTimeout;
|
||||
const isStayInProgress =
|
||||
isConnectingTimeout || isSendingVcTimeout || isSendingVPTimeout;
|
||||
let isFaceIdentityVerified = useSelector(
|
||||
scanService,
|
||||
selectIsFaceIdentityVerified,
|
||||
@@ -183,7 +191,7 @@ export function useScanLayout() {
|
||||
onButtonPress: CANCEL,
|
||||
progress: true,
|
||||
};
|
||||
} else if (isSendingVc) {
|
||||
} else if (isSendingVc || isSendingVP) {
|
||||
statusOverlay = {
|
||||
title: t('status.sharing.title'),
|
||||
hint: t('status.sharing.hint'),
|
||||
@@ -196,7 +204,7 @@ export function useScanLayout() {
|
||||
hint: t('status.sharing.hint'),
|
||||
progress: true,
|
||||
};
|
||||
} else if (isSendingVcTimeout) {
|
||||
} else if (isSendingVcTimeout || isSendingVPTimeout) {
|
||||
statusOverlay = {
|
||||
title: t('status.sharing.title'),
|
||||
hint: t('status.sharing.timeoutHint'),
|
||||
@@ -282,6 +290,9 @@ export function useScanLayout() {
|
||||
) {
|
||||
changeTabBarVisible('none');
|
||||
navigation.navigate(SCAN_ROUTES.SendVcScreen);
|
||||
} else if (openID4VPFlowType === VCShareFlowType.OPENID4VP) {
|
||||
changeTabBarVisible('none');
|
||||
navigation.navigate(SCAN_ROUTES.SendVPScreen);
|
||||
} else if (isScanning) {
|
||||
changeTabBarVisible('flex');
|
||||
navigation.navigate(SCAN_ROUTES.ScanScreen);
|
||||
@@ -296,6 +307,7 @@ export function useScanLayout() {
|
||||
isQrLoginDone,
|
||||
isBleError,
|
||||
flowType,
|
||||
openID4VPFlowType,
|
||||
isAccepted,
|
||||
isQrLoginViaDeepLink,
|
||||
linkCode,
|
||||
@@ -321,7 +333,10 @@ export function useScanLayout() {
|
||||
onRetry,
|
||||
CANCEL,
|
||||
isSendingVc,
|
||||
isSendingVP,
|
||||
isVPSharingSuccess: useSelector(scanService, selectIsSendingVPSuccess),
|
||||
flowType,
|
||||
openID4VPFlowType,
|
||||
isVerifyingIdentity,
|
||||
isInvalidIdentity,
|
||||
FACE_INVALID,
|
||||
|
||||
@@ -11,19 +11,32 @@ import {QrLogin} from '../QrLogin/QrLogin';
|
||||
import {useScanScreen} from './ScanScreenController';
|
||||
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
|
||||
import {Linking} from 'react-native';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
import {isIOS, LIVENESS_CHECK} from '../../shared/constants';
|
||||
import {BannerNotificationContainer} from '../../components/BannerNotificationContainer';
|
||||
import {SharingStatusModal} from './SharingStatusModal';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
import {LocationPermissionRational} from './LocationPermissionRational';
|
||||
import {FaceVerificationAlertOverlay} from './FaceVerificationAlertOverlay';
|
||||
import {useSendVcScreen} from './SendVcScreenController';
|
||||
import {useSendVPScreen} from './SendVPScreenController';
|
||||
import {Error} from '../../components/ui/Error';
|
||||
import {VPShareOverlay} from './VPShareOverlay';
|
||||
import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
|
||||
import {VCShareFlowType} from '../../shared/Utils';
|
||||
|
||||
export const ScanScreen: React.FC = () => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
const scanScreenController = useScanScreen();
|
||||
const sendVcScreenController = useSendVcScreen();
|
||||
const sendVPScreenController = useSendVPScreen();
|
||||
const [isBluetoothOn, setIsBluetoothOn] = useState(false);
|
||||
const showErrorModal =
|
||||
sendVPScreenController.scanScreenError ||
|
||||
(sendVPScreenController.errorModal.show &&
|
||||
(sendVPScreenController.flowType ===
|
||||
VCShareFlowType.MINI_VIEW_SHARE_OPENID4VP ||
|
||||
sendVPScreenController.flowType ===
|
||||
VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP));
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -54,6 +67,11 @@ export const ScanScreen: React.FC = () => {
|
||||
Linking.openSettings();
|
||||
};
|
||||
|
||||
const handleTextButtonEvent = () => {
|
||||
sendVPScreenController.GO_TO_HOME();
|
||||
sendVPScreenController.RESET_RETRY_COUNT();
|
||||
};
|
||||
|
||||
function noShareableVcText() {
|
||||
return (
|
||||
<Text
|
||||
@@ -226,14 +244,21 @@ export const ScanScreen: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const faceVerificationController = sendVPScreenController.flowType.startsWith(
|
||||
'OpenID4VP',
|
||||
)
|
||||
? sendVPScreenController
|
||||
: sendVcScreenController;
|
||||
|
||||
return (
|
||||
<Column fill backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<BannerNotificationContainer />
|
||||
<FaceVerificationAlertOverlay
|
||||
isVisible={sendVcScreenController.isFaceVerificationConsent}
|
||||
onConfirm={sendVcScreenController.FACE_VERIFICATION_CONSENT}
|
||||
close={sendVcScreenController.DISMISS}
|
||||
isVisible={faceVerificationController.isFaceVerificationConsent}
|
||||
onConfirm={faceVerificationController.FACE_VERIFICATION_CONSENT}
|
||||
close={faceVerificationController.DISMISS}
|
||||
/>
|
||||
|
||||
<Centered
|
||||
padding="24 0"
|
||||
align="space-evenly"
|
||||
@@ -252,6 +277,76 @@ export const ScanScreen: React.FC = () => {
|
||||
/>
|
||||
</Centered>
|
||||
{displayStorageLimitReachedError()}
|
||||
|
||||
{sendVPScreenController.flowType.startsWith('OpenID4VP') &&
|
||||
sendVPScreenController.flowType !== VCShareFlowType.OPENID4VP &&
|
||||
sendVPScreenController.overlayDetails !== null && (
|
||||
<VPShareOverlay
|
||||
isVisible={sendVPScreenController.overlayDetails !== null}
|
||||
title={sendVPScreenController.overlayDetails.title}
|
||||
titleTestID={sendVPScreenController.overlayDetails.titleTestID}
|
||||
message={sendVPScreenController.overlayDetails.message}
|
||||
messageTestID={sendVPScreenController.overlayDetails.messageTestID}
|
||||
primaryButtonTestID={
|
||||
sendVPScreenController.overlayDetails.primaryButtonTestID
|
||||
}
|
||||
primaryButtonText={
|
||||
sendVPScreenController.overlayDetails.primaryButtonText
|
||||
}
|
||||
primaryButtonEvent={
|
||||
sendVPScreenController.overlayDetails.primaryButtonEvent
|
||||
}
|
||||
secondaryButtonTestID={
|
||||
sendVPScreenController.overlayDetails.secondaryButtonTestID
|
||||
}
|
||||
secondaryButtonText={
|
||||
sendVPScreenController.overlayDetails.secondaryButtonText
|
||||
}
|
||||
secondaryButtonEvent={
|
||||
sendVPScreenController.overlayDetails.secondaryButtonEvent
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<>
|
||||
<Error
|
||||
isModal
|
||||
alignActionsOnEnd
|
||||
showClose={false}
|
||||
isVisible={showErrorModal}
|
||||
title={sendVPScreenController.errorModal.title}
|
||||
message={sendVPScreenController.errorModal.message}
|
||||
image={SvgImage.PermissionDenied()}
|
||||
primaryButtonTestID={'retry'}
|
||||
primaryButtonText={
|
||||
sendVPScreenController.errorModal.showRetryButton &&
|
||||
sendVPScreenController.openID4VPRetryCount < 3
|
||||
? t('ScanScreen:status.retry')
|
||||
: undefined
|
||||
}
|
||||
primaryButtonEvent={sendVPScreenController.RETRY}
|
||||
textButtonTestID={'home'}
|
||||
textButtonText={t('ScanScreen:status.accepted.home')}
|
||||
textButtonEvent={handleTextButtonEvent}
|
||||
customImageStyles={{paddingBottom: 0, marginBottom: -6}}
|
||||
customStyles={{marginTop: '30%'}}
|
||||
testID={'vpShareError'}
|
||||
/>
|
||||
|
||||
<VerifyIdentityOverlay
|
||||
credential={sendVPScreenController.credentials}
|
||||
verifiableCredentialData={
|
||||
sendVPScreenController.verifiableCredentialsData
|
||||
}
|
||||
isVerifyingIdentity={sendVPScreenController.isVerifyingIdentity}
|
||||
onCancel={sendVPScreenController.CANCEL}
|
||||
onFaceValid={sendVPScreenController.FACE_VALID}
|
||||
onFaceInvalid={sendVPScreenController.FACE_INVALID}
|
||||
isInvalidIdentity={sendVPScreenController.isInvalidIdentity}
|
||||
onNavigateHome={sendVPScreenController.GO_TO_HOME}
|
||||
onRetryVerification={sendVPScreenController.RETRY_VERIFICATION}
|
||||
isLivenessEnabled={LIVENESS_CHECK}
|
||||
/>
|
||||
</>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
247
screens/Scan/SendVPScreen.tsx
Normal file
247
screens/Scan/SendVPScreen.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {BackHandler, View} from 'react-native';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {VcItemContainer} from '../../components/VC/VcItemContainer';
|
||||
import {LIVENESS_CHECK} from '../../shared/constants';
|
||||
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {
|
||||
getImpressionEventData,
|
||||
sendImpressionEvent,
|
||||
} from '../../shared/telemetry/TelemetryUtils';
|
||||
import {isMosipVC, VCItemContainerFlowType} from '../../shared/Utils';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
|
||||
import {VPShareOverlay} from './VPShareOverlay';
|
||||
import {FaceVerificationAlertOverlay} from './FaceVerificationAlertOverlay';
|
||||
import {useSendVPScreen} from './SendVPScreenController';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import {Error} from '../../components/ui/Error';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
|
||||
export const SendVPScreen: React.FC = () => {
|
||||
const {t} = useTranslation('SendVPScreen');
|
||||
const controller = useSendVPScreen();
|
||||
|
||||
const vcsMatchingAuthRequest = controller.vcsMatchingAuthRequest;
|
||||
|
||||
useEffect(() => {
|
||||
sendImpressionEvent(
|
||||
getImpressionEventData(
|
||||
TelemetryConstants.FlowType.senderVcShare,
|
||||
TelemetryConstants.Screens.vcList,
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
const onBackPress = () => true;
|
||||
|
||||
const disableBackHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
onBackPress,
|
||||
);
|
||||
|
||||
return () => disableBackHandler.remove();
|
||||
}, []),
|
||||
);
|
||||
|
||||
const handleTextButtonEvent = () => {
|
||||
controller.GO_TO_HOME();
|
||||
controller.RESET_RETRY_COUNT();
|
||||
};
|
||||
|
||||
const getVcKey = vcData => {
|
||||
return VCMetadata.fromVcMetadataString(vcData.vcMetadata).getVcKey();
|
||||
};
|
||||
|
||||
const noOfCardsSelected = controller.areAllVCsChecked
|
||||
? Object.values(controller.vcsMatchingAuthRequest).length
|
||||
: Object.keys(controller.selectedVCKeys).length;
|
||||
|
||||
const cardsSelectedText =
|
||||
noOfCardsSelected === 1
|
||||
? noOfCardsSelected + ' ' + t('cardSelected')
|
||||
: noOfCardsSelected + ' ' + t('cardsSelected');
|
||||
|
||||
const areAllVcsChecked =
|
||||
noOfCardsSelected ===
|
||||
Object.values(controller.vcsMatchingAuthRequest).flatMap(vc => vc).length;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{Object.keys(vcsMatchingAuthRequest).length > 0 && (
|
||||
<>
|
||||
{controller.purpose !== '' && (
|
||||
<View style={{backgroundColor: Theme.Colors.whiteBackgroundColor}}>
|
||||
<Column
|
||||
padding="14 12 14 12"
|
||||
margin="20 20 20 20"
|
||||
style={Theme.VPSharingStyles.purposeContainer}>
|
||||
<Text
|
||||
color={Theme.Colors.TimeoutHintText}
|
||||
style={Theme.VPSharingStyles.purposeText}>
|
||||
{controller.purpose}
|
||||
</Text>
|
||||
</Column>
|
||||
</View>
|
||||
)}
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<LinearGradient colors={Theme.Colors.selectIDTextGradient}>
|
||||
<Column>
|
||||
<Text
|
||||
margin="15 0 13 24"
|
||||
color={Theme.Colors.textValue}
|
||||
style={Theme.VPSharingStyles.selectIDText}>
|
||||
{t('SendVcScreen:pleaseSelectAnId')}
|
||||
</Text>
|
||||
</Column>
|
||||
</LinearGradient>
|
||||
<Row
|
||||
padding="11 24 11 24"
|
||||
style={{
|
||||
backgroundColor: '#FAFAFA',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Text style={Theme.VPSharingStyles.cardsSelectedText}>
|
||||
{cardsSelectedText}
|
||||
</Text>
|
||||
<Text
|
||||
style={{color: '#F2801D', fontFamily: 'Inter_600SemiBold'}}
|
||||
onPress={
|
||||
areAllVcsChecked
|
||||
? controller.UNCHECK_ALL
|
||||
: controller.CHECK_ALL
|
||||
}>
|
||||
{areAllVcsChecked ? t('unCheck') : t('checkAll')}
|
||||
</Text>
|
||||
</Row>
|
||||
<Column scroll backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
{Object.entries(vcsMatchingAuthRequest).map(
|
||||
([inputDescriptorId, vcs]) =>
|
||||
vcs.map(vcData => (
|
||||
<VcItemContainer
|
||||
key={getVcKey(vcData)}
|
||||
vcMetadata={vcData.vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.SELECT_VC_ITEM(
|
||||
getVcKey(vcData),
|
||||
inputDescriptorId,
|
||||
)}
|
||||
selectable
|
||||
selected={
|
||||
controller.areAllVCsChecked ||
|
||||
Object.keys(controller.selectedVCKeys).includes(
|
||||
getVcKey(vcData),
|
||||
)
|
||||
}
|
||||
flow={VCItemContainerFlowType.OPENID4VP}
|
||||
isPinned={vcData.vcMetadata.isPinned}
|
||||
/>
|
||||
)),
|
||||
)}
|
||||
</Column>
|
||||
<Column
|
||||
style={[
|
||||
Theme.SendVcScreenStyles.shareOptionButtonsContainer,
|
||||
{position: 'relative'},
|
||||
]}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Button
|
||||
type="gradient"
|
||||
styles={{marginTop: 12}}
|
||||
title={t('SendVcScreen:acceptRequest')}
|
||||
disabled={Object.keys(controller.selectedVCKeys).length === 0}
|
||||
onPress={controller.ACCEPT_REQUEST}
|
||||
/>
|
||||
{controller.checkIfAnyMatchingVCHasImage() && (
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('SendVcScreen:acceptRequestAndVerify')}
|
||||
styles={{marginTop: 12}}
|
||||
disabled={Object.keys(controller.selectedVCKeys).length === 0}
|
||||
onPress={controller.VERIFY_AND_ACCEPT_REQUEST}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="clear"
|
||||
loading={controller.isCancelling}
|
||||
title={t('SendVcScreen:reject')}
|
||||
onPress={controller.CANCEL}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
<VerifyIdentityOverlay
|
||||
credential={controller.credentials}
|
||||
verifiableCredentialData={controller.verifiableCredentialsData}
|
||||
isVerifyingIdentity={controller.isVerifyingIdentity}
|
||||
onCancel={controller.CANCEL}
|
||||
onFaceValid={controller.FACE_VALID}
|
||||
onFaceInvalid={controller.FACE_INVALID}
|
||||
isInvalidIdentity={controller.isInvalidIdentity}
|
||||
onNavigateHome={controller.GO_TO_HOME}
|
||||
onRetryVerification={controller.RETRY_VERIFICATION}
|
||||
isLivenessEnabled={LIVENESS_CHECK}
|
||||
/>
|
||||
|
||||
{controller.overlayDetails !== null && (
|
||||
<VPShareOverlay
|
||||
isVisible={controller.overlayDetails !== null}
|
||||
title={controller.overlayDetails.title}
|
||||
titleTestID={controller.overlayDetails.titleTestID}
|
||||
message={controller.overlayDetails.message}
|
||||
messageTestID={controller.overlayDetails.messageTestID}
|
||||
primaryButtonTestID={
|
||||
controller.overlayDetails.primaryButtonTestID
|
||||
}
|
||||
primaryButtonText={controller.overlayDetails.primaryButtonText}
|
||||
primaryButtonEvent={controller.overlayDetails.primaryButtonEvent}
|
||||
secondaryButtonTestID={
|
||||
controller.overlayDetails.secondaryButtonTestID
|
||||
}
|
||||
secondaryButtonText={
|
||||
controller.overlayDetails.secondaryButtonText
|
||||
}
|
||||
secondaryButtonEvent={
|
||||
controller.overlayDetails.secondaryButtonEvent
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FaceVerificationAlertOverlay
|
||||
isVisible={controller.isFaceVerificationConsent}
|
||||
onConfirm={controller.FACE_VERIFICATION_CONSENT}
|
||||
close={controller.DISMISS}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Error
|
||||
isModal
|
||||
alignActionsOnEnd
|
||||
showClose={false}
|
||||
isVisible={controller.errorModal.show}
|
||||
title={controller.errorModal.title}
|
||||
message={controller.errorModal.message}
|
||||
image={SvgImage.PermissionDenied()}
|
||||
primaryButtonTestID={'retry'}
|
||||
primaryButtonText={
|
||||
controller.errorModal.showRetryButton &&
|
||||
controller.openID4VPRetryCount < 3
|
||||
? t('ScanScreen:status.retry')
|
||||
: undefined
|
||||
}
|
||||
primaryButtonEvent={controller.RETRY}
|
||||
textButtonTestID={'home'}
|
||||
textButtonText={t('ScanScreen:status.accepted.home')}
|
||||
textButtonEvent={handleTextButtonEvent}
|
||||
customImageStyles={{paddingBottom: 0, marginBottom: -6}}
|
||||
customStyles={{marginTop: '30%'}}
|
||||
testID={'vpShareError'}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
269
screens/Scan/SendVPScreenController.ts
Normal file
269
screens/Scan/SendVPScreenController.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useContext, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {selectIsCancelling} from '../../machines/bleShare/commonSelectors';
|
||||
import {ScanEvents} from '../../machines/bleShare/scan/scanMachine';
|
||||
import {
|
||||
selectFlowType,
|
||||
selectIsSendingVPError,
|
||||
} from '../../machines/bleShare/scan/scanSelectors';
|
||||
import {selectOpenID4VPRetryCount} from '../../machines/openID4VP/openID4VPSelectors';
|
||||
import {OpenID4VPEvents} from '../../machines/openID4VP/openID4VPMachine';
|
||||
import {
|
||||
selectAreAllVCsChecked,
|
||||
selectCredentials,
|
||||
selectIsError,
|
||||
selectIsFaceVerificationConsent,
|
||||
selectIsGetVCsSatisfyingAuthRequest,
|
||||
selectIsGetVPSharingConsent,
|
||||
selectIsInvalidIdentity,
|
||||
selectIsSelectingVcs,
|
||||
selectIsSharingVP,
|
||||
selectIsVerifyingIdentity,
|
||||
selectPurpose,
|
||||
selectShowConfirmationPopup,
|
||||
selectVCsMatchingAuthRequest,
|
||||
selectVerifiableCredentialsData,
|
||||
} from '../../machines/openID4VP/openID4VPSelectors';
|
||||
import {selectMyVcs} from '../../machines/QrLogin/QrLoginSelectors';
|
||||
import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine';
|
||||
import {selectShareableVcs} from '../../machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors';
|
||||
import {RootRouteProps} from '../../routes';
|
||||
import {BOTTOM_TAB_ROUTES} from '../../routes/routesConstants';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {isMosipVC} from '../../shared/Utils';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {VPShareOverlayProps} from './VPShareOverlay';
|
||||
|
||||
type MyVcsTabNavigation = NavigationProp<RootRouteProps>;
|
||||
|
||||
const changeTabBarVisible = (visible: string) => {
|
||||
Theme.BottomTabBarStyle.tabBarStyle.display = visible;
|
||||
};
|
||||
|
||||
export function getVcsForVPSharing(vcMetadatas: VCMetadata[]) {}
|
||||
|
||||
export function useSendVPScreen() {
|
||||
const {t} = useTranslation('SendVPScreen');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const scanService = appService.children.get('scan')!!;
|
||||
const vcMetaService = appService.children.get('vcMeta')!!;
|
||||
const navigation = useNavigation<MyVcsTabNavigation>();
|
||||
const openID4VPService = scanService.getSnapshot().context.OpenId4VPRef;
|
||||
const [selectedVCKeys, setSelectedVCKeys] = useState<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
const shareableVcs = useSelector(vcMetaService, selectShareableVcs);
|
||||
|
||||
const myVcs = useSelector(vcMetaService, selectMyVcs);
|
||||
|
||||
const isGetVCsSatisfyingAuthRequest = useSelector(
|
||||
openID4VPService,
|
||||
selectIsGetVCsSatisfyingAuthRequest,
|
||||
);
|
||||
|
||||
if (isGetVCsSatisfyingAuthRequest) {
|
||||
openID4VPService.send('DOWNLOADED_VCS', {vcs: shareableVcs});
|
||||
}
|
||||
|
||||
const areAllVCsChecked = useSelector(
|
||||
openID4VPService,
|
||||
selectAreAllVCsChecked,
|
||||
);
|
||||
const vcsMatchingAuthRequest = useSelector(
|
||||
openID4VPService,
|
||||
selectVCsMatchingAuthRequest,
|
||||
);
|
||||
const checkIfAnyMatchingVCHasImage = () => {
|
||||
const hasImage = Object.values(vcsMatchingAuthRequest)
|
||||
.flatMap(vc => vc)
|
||||
.some(vc => isMosipVC(vc.vcMetadata.issuer));
|
||||
return hasImage;
|
||||
};
|
||||
|
||||
const getSelectedVCs = () => {
|
||||
var selectedVcsData = {};
|
||||
Object.entries(selectedVCKeys).map(([vcKey, inputDescriptorId]) => {
|
||||
const vcData = myVcs[vcKey];
|
||||
selectedVcsData[inputDescriptorId] ??= [];
|
||||
selectedVcsData[inputDescriptorId].push(vcData);
|
||||
});
|
||||
return selectedVcsData;
|
||||
};
|
||||
|
||||
const isSendingVP = useSelector(openID4VPService, selectIsSharingVP);
|
||||
const showConfirmationPopup = useSelector(
|
||||
openID4VPService,
|
||||
selectShowConfirmationPopup,
|
||||
);
|
||||
const isSelectingVCs = useSelector(openID4VPService, selectIsSelectingVcs);
|
||||
const error = useSelector(openID4VPService, selectIsError);
|
||||
const isVPSharingConsent = useSelector(
|
||||
openID4VPService,
|
||||
selectIsGetVPSharingConsent,
|
||||
);
|
||||
const CONFIRM = () => openID4VPService.send(OpenID4VPEvents.CONFIRM());
|
||||
|
||||
const CANCEL = () => openID4VPService.send(OpenID4VPEvents.CANCEL());
|
||||
|
||||
const GO_BACK = () => openID4VPService.send(OpenID4VPEvents.GO_BACK());
|
||||
|
||||
const noCredentialsMatchingVPRequest =
|
||||
isSelectingVCs && Object.keys(vcsMatchingAuthRequest).length === 0;
|
||||
let errorModal = {
|
||||
show: error !== '' || noCredentialsMatchingVPRequest,
|
||||
title: '',
|
||||
message: '',
|
||||
showRetryButton: false,
|
||||
};
|
||||
|
||||
if (noCredentialsMatchingVPRequest) {
|
||||
errorModal.title = t('errors.noMatchingCredentials.title');
|
||||
errorModal.message = t('errors.noMatchingCredentials.message');
|
||||
} else if (
|
||||
error.includes('Verifier authentication was unsuccessful') ||
|
||||
error.startsWith('api error')
|
||||
) {
|
||||
errorModal.title = t('errors.invalidVerifier.title');
|
||||
errorModal.message = t('errors.invalidVerifier.message');
|
||||
} else if (error.includes('credential mismatch detected')) {
|
||||
errorModal.title = t('errors.credentialsMismatch.title');
|
||||
errorModal.message = t('errors.credentialsMismatch.message');
|
||||
} else if (error.includes('none of the selected VC has image')) {
|
||||
errorModal.title = t('errors.noImage.title');
|
||||
errorModal.message = t('errors.noImage.message');
|
||||
} else if (error.startsWith('vc validation')) {
|
||||
errorModal.title = t('errors.invalidQrCode.title');
|
||||
errorModal.message = t('errors.invalidQrCode.message');
|
||||
} else if (error !== '') {
|
||||
errorModal.title = t('errors.genericError.title');
|
||||
errorModal.message = t('errors.genericError.message');
|
||||
errorModal.showRetryButton = true;
|
||||
}
|
||||
|
||||
let overlayDetails: Omit<VPShareOverlayProps, 'isVisible'> | null = null;
|
||||
if (isVPSharingConsent) {
|
||||
overlayDetails = {
|
||||
primaryButtonTestID: 'confirm',
|
||||
primaryButtonText: t('consentDialog.confirmButton'),
|
||||
primaryButtonEvent: CONFIRM,
|
||||
secondaryButtonTestID: 'cancel',
|
||||
secondaryButtonText: t('consentDialog.cancelButton'),
|
||||
secondaryButtonEvent: CANCEL,
|
||||
title: t('consentDialog.title'),
|
||||
titleTestID: 'consentTitle',
|
||||
message: t('consentDialog.message'),
|
||||
messageTestID: 'consentMsg',
|
||||
};
|
||||
} else if (showConfirmationPopup) {
|
||||
overlayDetails = {
|
||||
primaryButtonTestID: 'yesProceed',
|
||||
primaryButtonText: t('confirmationDialog.confirmButton'),
|
||||
primaryButtonEvent: CONFIRM,
|
||||
secondaryButtonTestID: 'goBack',
|
||||
secondaryButtonText: t('confirmationDialog.cancelButton'),
|
||||
secondaryButtonEvent: GO_BACK,
|
||||
title: t('confirmationDialog.title'),
|
||||
titleTestID: 'confirmationTitle',
|
||||
message: t('confirmationDialog.message'),
|
||||
messageTestID: 'confirmationMsg',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isSendingVP,
|
||||
flowType: useSelector(openID4VPService, selectFlowType),
|
||||
showConfirmationPopup,
|
||||
isSelectingVCs,
|
||||
checkIfAnyMatchingVCHasImage,
|
||||
errorModal,
|
||||
overlayDetails,
|
||||
scanScreenError: useSelector(scanService, selectIsSendingVPError),
|
||||
vcsMatchingAuthRequest,
|
||||
areAllVCsChecked,
|
||||
selectedVCKeys,
|
||||
isVerifyingIdentity: useSelector(
|
||||
openID4VPService,
|
||||
selectIsVerifyingIdentity,
|
||||
),
|
||||
purpose: useSelector(openID4VPService, selectPurpose),
|
||||
isInvalidIdentity: useSelector(openID4VPService, selectIsInvalidIdentity),
|
||||
isCancelling: useSelector(scanService, selectIsCancelling),
|
||||
isFaceVerificationConsent: useSelector(
|
||||
openID4VPService,
|
||||
selectIsFaceVerificationConsent,
|
||||
),
|
||||
credentials: useSelector(openID4VPService, selectCredentials),
|
||||
verifiableCredentialsData: useSelector(
|
||||
openID4VPService,
|
||||
selectVerifiableCredentialsData,
|
||||
),
|
||||
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) =>
|
||||
openID4VPService.send(
|
||||
OpenID4VPEvents.FACE_VERIFICATION_CONSENT(isDoNotAskAgainChecked),
|
||||
),
|
||||
DISMISS: () => scanService.send(ScanEvents.DISMISS()),
|
||||
RETRY: () => openID4VPService.send(OpenID4VPEvents.RETRY()),
|
||||
FACE_VALID: () => openID4VPService.send(OpenID4VPEvents.FACE_VALID()),
|
||||
FACE_INVALID: () => openID4VPService.send(OpenID4VPEvents.FACE_INVALID()),
|
||||
RETRY_VERIFICATION: () =>
|
||||
openID4VPService.send(OpenID4VPEvents.RETRY_VERIFICATION()),
|
||||
GO_TO_HOME: () => {
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home, {screen: 'HomeScreen'});
|
||||
openID4VPService.send(OpenID4VPEvents.DISMISS());
|
||||
openID4VPService.send(OpenID4VPEvents.RESET_ERROR());
|
||||
changeTabBarVisible('flex');
|
||||
},
|
||||
SELECT_VC_ITEM:
|
||||
(vcKey: string, inputDescriptorId: string) =>
|
||||
(vcRef: ActorRefFrom<typeof VCItemMachine>) => {
|
||||
var selectedVcs = {...selectedVCKeys};
|
||||
var isVCSelected = !!!selectedVcs[vcKey];
|
||||
if (isVCSelected) {
|
||||
selectedVcs[vcKey] = inputDescriptorId;
|
||||
} else {
|
||||
delete selectedVcs[vcKey];
|
||||
}
|
||||
setSelectedVCKeys(selectedVcs);
|
||||
const {serviceRefs, wellknownResponse, ...vcData} =
|
||||
vcRef.getSnapshot().context;
|
||||
},
|
||||
|
||||
UNCHECK_ALL: () => {
|
||||
setSelectedVCKeys({});
|
||||
},
|
||||
|
||||
CHECK_ALL: () => {
|
||||
var updatedVCsList = {};
|
||||
Object.entries(vcsMatchingAuthRequest).map(([inputDescriptorId, vcs]) => {
|
||||
vcs.map(vcData => {
|
||||
const vcKey = VCMetadata.fromVcMetadataString(
|
||||
vcData.vcMetadata,
|
||||
).getVcKey();
|
||||
updatedVCsList[vcKey] = inputDescriptorId;
|
||||
});
|
||||
});
|
||||
setSelectedVCKeys({...updatedVCsList});
|
||||
},
|
||||
|
||||
ACCEPT_REQUEST: () => {
|
||||
openID4VPService.send(OpenID4VPEvents.ACCEPT_REQUEST(getSelectedVCs()));
|
||||
},
|
||||
|
||||
VERIFY_AND_ACCEPT_REQUEST: () => {
|
||||
openID4VPService.send(
|
||||
OpenID4VPEvents.VERIFY_AND_ACCEPT_REQUEST(getSelectedVCs()),
|
||||
);
|
||||
},
|
||||
CANCEL,
|
||||
openID4VPRetryCount: useSelector(
|
||||
openID4VPService,
|
||||
selectOpenID4VPRetryCount,
|
||||
),
|
||||
RESET_RETRY_COUNT: () =>
|
||||
openID4VPService.send(OpenID4VPEvents.RESET_RETRY_COUNT()),
|
||||
};
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export const SendVcScreen: React.FC = () => {
|
||||
<Column
|
||||
style={Theme.SendVcScreenStyles.shareOptionButtonsContainer}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
{isMosipVC(controller.verifiableCredentialData.issuer) && (
|
||||
{isMosipVC(controller.verifiableCredentialData[0].issuer) && (
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('acceptRequestAndVerify')}
|
||||
|
||||
74
screens/Scan/VPShareOverlay.tsx
Normal file
74
screens/Scan/VPShareOverlay.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Dimensions} from 'react-native';
|
||||
import {Overlay} from 'react-native-elements';
|
||||
import {Button, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
|
||||
export const VPShareOverlay: React.FC<VPShareOverlayProps> = props => {
|
||||
const {t} = useTranslation('SendVPScreen');
|
||||
|
||||
return (
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
// onBackdropPress={props.close}
|
||||
overlayStyle={Theme.BindingVcWarningOverlay.overlay}>
|
||||
<Column
|
||||
align="space-between"
|
||||
crossAlign="center"
|
||||
padding={'10'}
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
height={Dimensions.get('screen').height * 0}>
|
||||
<Column crossAlign="center" margin="10 0 15 0" padding="0">
|
||||
<Text
|
||||
testID={props.titleTestID}
|
||||
weight="bold"
|
||||
size="large"
|
||||
color="#000000"
|
||||
style={{padding: 3}}>
|
||||
{props.title}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
testID={props.messageTestID}
|
||||
align="center"
|
||||
size="mediumSmall"
|
||||
weight="regular"
|
||||
margin="10 0 0 0"
|
||||
color="#5D5D5D">
|
||||
{props.message}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Button
|
||||
testID={props.primaryButtonTestID}
|
||||
margin={'10 0 0 0'}
|
||||
type="gradient"
|
||||
title={props.primaryButtonText}
|
||||
onPress={() => props.primaryButtonEvent()}
|
||||
/>
|
||||
<Button
|
||||
testID={props.secondaryButtonTestID}
|
||||
margin={'10 0 0 0'}
|
||||
type="clear"
|
||||
title={props.secondaryButtonText}
|
||||
onPress={() => props.secondaryButtonEvent()}
|
||||
/>
|
||||
</Column>
|
||||
</Overlay>
|
||||
);
|
||||
};
|
||||
|
||||
export interface VPShareOverlayProps {
|
||||
isVisible: boolean;
|
||||
title: string;
|
||||
titleTestID: string;
|
||||
message: string;
|
||||
messageTestID: string;
|
||||
primaryButtonTestID: string;
|
||||
primaryButtonText: string;
|
||||
primaryButtonEvent: () => void;
|
||||
secondaryButtonTestID: string;
|
||||
secondaryButtonText: string;
|
||||
secondaryButtonEvent: () => void;
|
||||
}
|
||||
@@ -13,7 +13,6 @@ export const VerifyIdentityOverlay: React.FC<
|
||||
> = props => {
|
||||
const {t} = useTranslation('VerifyIdentityOverlay');
|
||||
const credential = props.credential;
|
||||
const vcImage = props.verifiableCredentialData.face;
|
||||
|
||||
const modalProps = {
|
||||
isVisible: props.isVerifyingIdentity,
|
||||
@@ -42,7 +41,9 @@ export const VerifyIdentityOverlay: React.FC<
|
||||
align="center">
|
||||
{credential != null && (
|
||||
<FaceScanner
|
||||
vcImage={vcImage}
|
||||
vcImages={props.verifiableCredentialData
|
||||
.map(data => data.face)
|
||||
.filter(face => face !== undefined)}
|
||||
onValid={props.onFaceValid}
|
||||
onInvalid={props.onFaceInvalid}
|
||||
isLiveness={props.isLivenessEnabled}
|
||||
@@ -75,7 +76,7 @@ export const VerifyIdentityOverlay: React.FC<
|
||||
};
|
||||
|
||||
export interface VerifyIdentityOverlayProps {
|
||||
credential?: VerifiableCredential | Credential;
|
||||
credential?: [VerifiableCredential | Credential];
|
||||
verifiableCredentialData: any;
|
||||
isVerifyingIdentity: boolean;
|
||||
onCancel: () => void;
|
||||
|
||||
@@ -15,11 +15,15 @@ export enum VCShareFlowType {
|
||||
MINI_VIEW_SHARE = 'mini view share',
|
||||
MINI_VIEW_SHARE_WITH_SELFIE = 'mini view share with selfie',
|
||||
MINI_VIEW_QR_LOGIN = 'mini view qr login',
|
||||
OPENID4VP = 'OpenID4VP',
|
||||
MINI_VIEW_SHARE_OPENID4VP = 'OpenID4VP share from mini view',
|
||||
MINI_VIEW_SHARE_WITH_SELFIE_OPENID4VP = 'OpenID4VP share with selfie from mini view',
|
||||
}
|
||||
|
||||
export enum VCItemContainerFlowType {
|
||||
QR_LOGIN = 'qr login',
|
||||
VC_SHARE = 'vc share',
|
||||
OPENID4VP = 'openID4VP',
|
||||
}
|
||||
|
||||
export interface CommunicationDetails {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import {CredentialIdForMsoMdoc, Protocols} from './openId4VCI/Utils';
|
||||
import {getMosipIdentifier} from './commonUtil';
|
||||
import { VCFormat } from './VCFormat';
|
||||
import {VCFormat} from './VCFormat';
|
||||
|
||||
const VC_KEY_PREFIX = 'VC';
|
||||
const VC_ITEM_STORE_KEY_REGEX = '^VC_[a-zA-Z0-9_-]+$';
|
||||
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
import {TelemetryConstants} from './telemetry/TelemetryConstants';
|
||||
|
||||
export const API_URLS: ApiUrls = {
|
||||
trustedVerifiersList: {
|
||||
method: 'GET',
|
||||
buildURL: (): `/${string}` => '/v1/mimoto/verifiers',
|
||||
},
|
||||
issuersList: {
|
||||
method: 'GET',
|
||||
buildURL: (): `/${string}` => '/v1/mimoto/issuers',
|
||||
@@ -90,6 +94,14 @@ export const API_URLS: ApiUrls = {
|
||||
};
|
||||
|
||||
export const API = {
|
||||
fetchTrustedVerifiersList: async () => {
|
||||
const response = await request(
|
||||
API_URLS.trustedVerifiersList.method,
|
||||
API_URLS.trustedVerifiersList.buildURL(),
|
||||
);
|
||||
return response;
|
||||
},
|
||||
|
||||
fetchIssuers: async () => {
|
||||
const response = await request(
|
||||
API_URLS.issuersList.method,
|
||||
@@ -129,6 +141,13 @@ export const API = {
|
||||
};
|
||||
|
||||
export const CACHED_API = {
|
||||
fetchTrustedVerifiersList: (isCachePreferred: boolean = true) =>
|
||||
generateCacheAPIFunction({
|
||||
isCachePreferred,
|
||||
cacheKey: API_CACHED_STORAGE_KEYS.fetchTrustedVerifiers,
|
||||
fetchCall: API.fetchTrustedVerifiersList,
|
||||
}),
|
||||
|
||||
fetchIssuers: () =>
|
||||
generateCacheAPIFunction({
|
||||
cacheKey: API_CACHED_STORAGE_KEYS.fetchIssuers,
|
||||
@@ -315,6 +334,7 @@ type Api_Params = {
|
||||
};
|
||||
|
||||
type ApiUrls = {
|
||||
trustedVerifiersList: Api_Params;
|
||||
issuersList: Api_Params;
|
||||
issuerConfig: Api_Params;
|
||||
issuerWellknownConfig: Api_Params;
|
||||
|
||||
@@ -69,6 +69,7 @@ export const API_CACHED_STORAGE_KEYS = {
|
||||
`CACHE_FETCH_ISSUER_CONFIG_${issuerId}`,
|
||||
fetchIssuerWellknownConfig: (issuerId: string) =>
|
||||
`CACHE_FETCH_ISSUER_WELLKNOWN_CONFIG_${issuerId}`,
|
||||
fetchTrustedVerifiers: 'CACHE_FETCH_TRUSTED_VERIFIERS',
|
||||
};
|
||||
|
||||
export function isIOS(): boolean {
|
||||
|
||||
87
shared/openID4VP/OpenID4VP.ts
Normal file
87
shared/openID4VP/OpenID4VP.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import {NativeModules} from 'react-native';
|
||||
import {__AppId} from '../GlobalVariables';
|
||||
import {VC} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import {getJWT} from '../cryptoutil/cryptoUtil';
|
||||
import {getJWK} from '../openId4VCI/Utils';
|
||||
|
||||
export const OpenID4VP_Key_Ref = 'OpenID4VP_KeyPair';
|
||||
export const OpenID4VP_Proof_Algo_Type = 'RsaSignature2018';
|
||||
export const OpenID4VP_Domain = 'OpenID4VP';
|
||||
|
||||
export class OpenID4VP {
|
||||
static InjiOpenID4VP = NativeModules.InjiOpenID4VP;
|
||||
|
||||
static initialize() {
|
||||
OpenID4VP.InjiOpenID4VP.init(__AppId.getValue());
|
||||
}
|
||||
|
||||
static async authenticateVerifier(
|
||||
encodedAuthorizationRequest: string,
|
||||
trustedVerifiersList: any,
|
||||
) {
|
||||
const authenticationResponse =
|
||||
await OpenID4VP.InjiOpenID4VP.authenticateVerifier(
|
||||
encodedAuthorizationRequest,
|
||||
trustedVerifiersList,
|
||||
);
|
||||
return JSON.parse(authenticationResponse);
|
||||
}
|
||||
|
||||
static async constructVerifiablePresentationToken(
|
||||
selectedVCs: Record<string, VC[]>,
|
||||
) {
|
||||
let updatedSelectedVCs = {};
|
||||
Object.keys(selectedVCs).forEach(inputDescriptorId => {
|
||||
updatedSelectedVCs[inputDescriptorId] = selectedVCs[
|
||||
inputDescriptorId
|
||||
].map(vc => JSON.stringify(vc));
|
||||
});
|
||||
|
||||
const vpToken =
|
||||
await OpenID4VP.InjiOpenID4VP.constructVerifiablePresentationToken(
|
||||
updatedSelectedVCs,
|
||||
);
|
||||
return vpToken;
|
||||
}
|
||||
|
||||
static async shareVerifiablePresentation(
|
||||
vpResponseMetadata: Record<string, string>,
|
||||
) {
|
||||
return await OpenID4VP.InjiOpenID4VP.shareVerifiablePresentation(
|
||||
vpResponseMetadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function constructProofJWT(
|
||||
publicKey: string,
|
||||
privateKey: string,
|
||||
vpToken: Object,
|
||||
keyType: string,
|
||||
): Promise<string> {
|
||||
const jwtHeader = {
|
||||
alg: keyType,
|
||||
jwk: await getJWK(publicKey, keyType),
|
||||
};
|
||||
|
||||
const jwtPayload = createJwtPayload(vpToken);
|
||||
|
||||
return await getJWT(
|
||||
jwtHeader,
|
||||
jwtPayload,
|
||||
OpenID4VP_Key_Ref,
|
||||
privateKey,
|
||||
keyType,
|
||||
);
|
||||
}
|
||||
|
||||
function createJwtPayload(vpToken: {[key: string]: any}) {
|
||||
const {'@context': context, type, verifiableCredential, id, holder} = vpToken;
|
||||
return {
|
||||
'@context': context,
|
||||
type,
|
||||
verifiableCredential,
|
||||
id,
|
||||
holder,
|
||||
};
|
||||
}
|
||||
@@ -159,7 +159,10 @@ export const getCredentialIssuersWellKnownConfig = async (
|
||||
wellknownResponse,
|
||||
credentialConfigurationId,
|
||||
);
|
||||
if (Object.keys(matchingWellknownDetails).includes('order')) {
|
||||
if (
|
||||
matchingWellknownDetails.order != null &&
|
||||
matchingWellknownDetails.order.length > 0
|
||||
) {
|
||||
fields = matchingWellknownDetails.order;
|
||||
} else {
|
||||
if (format === VCFormat.mso_mdoc) {
|
||||
|
||||
@@ -3,6 +3,7 @@ export const TelemetryConstants = {
|
||||
vcDownload: 'VC Download',
|
||||
faceModelInit: 'Face SDK initialize',
|
||||
qrLogin: 'QR Login',
|
||||
vpSharing: 'VP Sharing',
|
||||
senderVcShare: 'Sender VC Share',
|
||||
receiverVcShare: 'Receiver VC Share',
|
||||
vcActivation: 'VC Activation',
|
||||
|
||||
Reference in New Issue
Block a user