From 003cc156c24d9fb0944440ebf93a29d3c461b1ee Mon Sep 17 00:00:00 2001 From: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:37:54 +0530 Subject: [PATCH] [INJIMOB-1192] onboarding of new issuer is affecting the existing issuers (#1476) * [INJIMOB-1192] : use wellknown response instead of mimoto issuer config. -- Remove hardcoding for sunbird issuer in vc activation and verification flow. -- Render idType from wellknown response -- Remove UIN/VID from default add-on fields Signed-off-by: Swati Goel * [INJIMOB-1192] : fix propType and some refactoring Signed-off-by: Swati Goel * [INJIMOB-1192] : add credentialType in VcMetadata Signed-off-by: Swati Goel * [INJIMOB-1192] fix vc download via issuer flow due to credentialType mismatch Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] rename supported list of credential type in issuers model Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] display id type in history based on wellknown for issuers VC Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] fix id type not shown for VC activation Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] remove unused credentialType field from VCMetaData Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] set default idType for logging activity Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] move vc item machine events into model Events should not be exported to other packages for direct use so that Xstate's createModel() can decorate the function appropriately Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] show verify banner id type from wellknown Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] refactor duplication and unused code Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] remove unused displayId in metadata Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] revert the dimensions of camera scanner to old values to support face liveness verification Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1192] remove unused code & debug logs Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-1192] fix failing test cases Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-1192] remove unused translations Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> --------- Signed-off-by: Swati Goel Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: Swati Goel Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> --- .talismanrc | 36 +++- __mocks__/jest-init.js | 9 +- __mocks__/react-native.mock.js | 6 + components/ActivityLogEvent.test.ts | 11 +- components/ActivityLogEvent.ts | 16 +- components/ActivityLogText.tsx | 8 +- components/BannerNotificationContainer.tsx | 4 +- components/FaceScanner/FaceCompare.tsx | 39 +++-- components/FaceScanner/FaceScannerHelper.ts | 6 +- components/FaceScanner/LivenessDetection.tsx | 68 +++++--- components/VC/Views/VCCardViewContent.tsx | 14 +- components/VC/Views/VCDetailView.tsx | 1 + components/VC/common/VCUtils.tsx | 142 ++++++++-------- components/ui/Modal.tsx | 111 ++++++------- components/ui/svg.tsx | 2 +- components/ui/themes/DefaultTheme.ts | 9 +- components/ui/themes/PurpleTheme.ts | 9 +- machines/Issuers/IssuersActions.ts | 55 ++++--- machines/Issuers/IssuersGuards.ts | 7 +- machines/Issuers/IssuersMachine.ts | 20 +-- machines/Issuers/IssuersMachine.typegen.ts | 6 +- machines/Issuers/IssuersModel.ts | 2 +- machines/Issuers/IssuersSelectors.ts | 4 +- machines/Issuers/IssuersService.ts | 59 +++---- machines/QrLogin/QrLoginSelectors.ts | 41 +++-- .../VCItemMachine/VCItemActions.ts | 80 +++++---- .../VCItemMachine/VCItemEvents.ts | 32 ---- .../VCItemMachine/VCItemGaurds.ts | 9 +- .../VCItemMachine/VCItemMachine.ts | 23 ++- .../VCItemMachine/VCItemMachine.typegen.ts | 17 +- .../VCItemMachine/VCItemModel.ts | 42 ++++- .../VCItemMachine/VCItemSelectors.ts | 53 +++--- .../VCItemMachine/VCItemServices.ts | 25 ++- .../VCMetaMachine/VCMetaSelectors.ts | 1 - .../VCMetaMachine/vc.d.ts | 40 ++++- machines/activityLog.ts | 59 ++++++- machines/activityLog.typegen.ts | 70 ++++---- machines/bleShare/request/requestMachine.ts | 7 +- machines/bleShare/request/selectors.ts | 41 +++-- machines/bleShare/scan/scanActions.ts | 53 +++--- machines/bleShare/scan/scanSelectors.ts | 38 ++--- machines/faceScanner.typegen.ts | 116 +++++++------ machines/settings.ts | 2 +- machines/settings.typegen.ts | 121 ++++++++------ machines/store.ts | 15 ++ machines/store.typegen.ts | 1 + screens/History/HistoryScreenController.ts | 19 ++- screens/Home/ViewVcModal.tsx | 4 +- .../Issuers/CredentialTypeSelectionScreen.tsx | 2 +- screens/Issuers/IssuerScreenController.tsx | 7 +- screens/QrLogin/QrLogin.tsx | 2 +- screens/Scan/ScanLayout.tsx | 2 +- screens/Scan/SendVcScreen.tsx | 8 +- screens/VerifyIdentityOverlay.tsx | 9 +- shared/Utils.ts | 5 + shared/VCMetadata.ts | 28 +--- shared/api.ts | 8 +- shared/commonUtil.ts | 2 +- shared/constants.ts | 7 +- shared/openId4VCI/Utils.ts | 154 +++++++++--------- shared/storage.ts | 23 ++- shared/telemetry/TelemetryConstants.js | 3 + 62 files changed, 1040 insertions(+), 773 deletions(-) delete mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts diff --git a/.talismanrc b/.talismanrc index 18ac4bf0..6e650aee 100644 --- a/.talismanrc +++ b/.talismanrc @@ -2,7 +2,7 @@ fileignoreconfig: - filename: package.json checksum: 5b4fcb5ddc7cc96cc2d1733b544d56ea66e88cdab995a1052fbf9ac0e9c2dc21 - filename: package-lock.json - checksum: 50254c7e3e84d59dceb77cde95ce71cb5d0625cb5ec84971a28b6fc4f95db7f1 + checksum: 98f4ef19f06521bac3ea3033d82810203214bf55b0469790a1d8acc20933c581 - filename: lib/jsonld-signatures/suites/ed255192018/ed25519.ts checksum: 493b6e31144116cb612c24d98b97d8adcad5609c0a52c865a6847ced0a0ddc3a - filename: components/PasscodeVerify.tsx @@ -38,7 +38,7 @@ fileignoreconfig: - filename: screens/Home/MyVcs/GetIdInputModal.tsx checksum: 5c736ed79a372d0ffa7c02eb33d0dc06edbbb08d120978ff287f5f06cd6c7746 - filename: shared/openId4VCI/Utils.ts - checksum: f8b53de9c07074dc3a73f9443ba140c7e86300b4a2beac1a84bfccafb55ecd6a + checksum: ee4db1768be8d51fac0eb876a7b16fd2ab1806abcc711f01056f672003d11f31 - filename: shared/cryptoutil/cryptoUtil.ts checksum: 2efef1baca1eee0da60420c8d966a6d58589bc3ac74169ab1cdc19423b630dba - filename: shared/telemetry/TelemetryConstants.js @@ -63,8 +63,10 @@ fileignoreconfig: checksum: 736b5a7ddb86bd4376229ce198dbf8a663e7ac89fc3311bd4f19afd4a2b36ffd - filename: assets/Finger_Print_Icon.svg checksum: 776d4fe4fc4b54d185ccf97daf0511b9fe2c0e0f7c1a809047020e5e8a100db6 + - filename: screens/MainLayout.tsx + checksum: 53ead79279c9609e42a8993db1a66bdb4649e1ae3b909d462b45b00c507c416e - filename: android/app/build.gradle - checksum: 9f9cdcd2ffb37338760d741694486cf5418a38834c3ca1bd9c573098ee10d997 + checksum: 46a4054440361b25d13ecd75811bf239a6abb4830ce7f79b2b15ccd878758760 - filename: .github/workflows/push-triggers.yml checksum: abc19ea38c8d7b79f15695d015709cc88a34a995181aaf12bc8344f940f3cbc4 - filename: android/fastlane/Fastfile @@ -113,6 +115,16 @@ fileignoreconfig: checksum: a4e3772dc67a07ecbcfc58be0d6d4f7fa799cec7ac25bd269ac29459c8669ca4 - filename: injitest/src/test/java/iosTestCases/CredentialRegistryTest.java checksum: b0808e0c511412cde21fd169a9bbeaf3b77cb48f25418e12d341cc3ce1df5898 + - filename: injitest/src/main/resources/Vids.json + checksum: 8bcffed7a6dd565ae695e1b29de0655e10bd5c5420af2718defd593a687b8817 + - filename: injitest/src/main/java/inji/utils/UpdateNetworkSettings.java + checksum: e249ce3e6b7f47abc183fe5a3637bb39ccb06900ef75b9b2f08426d1535e22aa + - filename: injitest/src/test/java/androidTestCases/ShareVcTest.java + checksum: a7e3e579b6ac05f95932638b61272142774d0690c13717c890e87374782ea509 + - filename: injitest/src/test/java/iosTestCases/ShareVcTest.java + checksum: 1cf9b61d3fcea9b63b2b9f7dffe9b5a1848e196c39f77790b6c9d83f201c6197 + - filename: ios/RNPixelpassModule.swift + checksum: 822a2421798d5c0669f4ab1b983194eb770cbef2aa30bf212d06bd959738c4ca - filename: components/BackupAndRestoreAllScreenBanner.tsx checksum: 2c98e7e83959c9dac4dd12da32f81483c3d334bd05e279637cf465475fbf54b8 - filename: locales/en.json @@ -144,7 +156,7 @@ fileignoreconfig: - filename: shared/VCMetadata.ts checksum: e93f988415bf91064e2cf5fbc09ff6c7226798baa5da721fa0715d5d0d6afddf - filename: ios/Podfile.lock - checksum: 0b4ed806e0fbecf60fb1135072d0a629666723fedcd960b6470bac0bb42a3e68 + checksum: b8c97d58a88207bae811db83074388cff249a83055a1f92ea7dee2f59b7a32c9 - filename: components/BackupAndRestoreBannerNotification.tsx checksum: e465a9947727687d784d0cb9d8db1e28f765b0659bf4a3aa6d75643aa7b14102 - filename: components/ActivityLogEvent.ts @@ -186,11 +198,11 @@ fileignoreconfig: - filename: screens/Home/IntroSlidersScreen.tsx checksum: 9880724461b194db7651737576ad2fd2db9cf3b4e732747f59be422a7ff4e4a1 - filename: .env - checksum: e4254ca79a1269161ac3e9d4870680a8650ac7dbdf61c39c084722a9e8925669 + checksum: ac76b852842c44ff5dac96c1fa5061e569bea4f54b3080d869a9dc25abd17991 - filename: machines/VCItemMachine/VCItemMachine.typegen.ts checksum: 850b5d02636bef9e286fc0fbc4ffffbd38068f332c319302a906496f4bc1c8a1 - - filename: machines/VCItemMachine/VCItemEvents.ts - checksum: 04e5758d4fa8bc37e8b66f7f51627a9e71ccbca7a046aa64e914f5cf855aa48b + - filename: machines/VerifiableCredential/VCItemMachine/VCItemModel.ts + checksum: 2a22331fe5f20d44c210b8970be0b934bfdf39bc999009305a9ba8f18e4d5469 - filename: machines/VCItemMachine/VCItemGaurds.ts checksum: 4f32814fc26a0edaa54a42dbc9f9e1d899144eb059ac8da211d1738887871829 - filename: machines/VCItemMachine/VCItemServices.ts @@ -317,8 +329,14 @@ fileignoreconfig: - filename: machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts checksum: fdc0c23a7107eb713d20f60fda675f9e9fe8ef29c981d798d90e581dfee340c8 checksum: 8ac74d2e5c6de179e460b86899eb048ad4c5bd67abc3d28c015e92335b8afe24 + - filename: machines/VerifiableCredential/VCItemMachine/VCItemServices.ts + checksum: 1ce38602f148388940eec172a5c9be83de7a600adcae0ba9e8ac27e5ebc44641 + - 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 - filename: injitest/src/main/resources/Vids.json checksum: 8bcffed7a6dd565ae695e1b29de0655e10bd5c5420af2718defd593a687b8817 - filename: injitest/src/main/java/inji/utils/UpdateNetworkSettings.java @@ -347,4 +365,8 @@ fileignoreconfig: checksum: b168940c6b487dc96fd22f564f2e187dae46f4fa5e4a64cf81c4d810b1c1ae78 - filename: ios/Inji.xcodeproj/project.pbxproj checksum: 4359976ed4d1ac3206d76b87d3458d070027199c8569ba123436c4b5343aba74 + - filename: components/FaceScanner/FaceCompare.tsx + checksum: 947b6d75543e2bf959ca2d95dd7224051e0b4ec2c28f7515f923701e22a932f0 + - filename: components/FaceScanner/LivenessDetection.tsx + checksum: d4140a42ee9ca0f7c90e490f762d181a723fd9dd20db891cbbe53bfbd8f81632 version: "" diff --git a/__mocks__/jest-init.js b/__mocks__/jest-init.js index 2268d229..c6952559 100644 --- a/__mocks__/jest-init.js +++ b/__mocks__/jest-init.js @@ -145,12 +145,9 @@ jest.mock('react-native-gesture-handler', () => { }; }); -jest.mock('@mosip/secure-keystore', () => ({ - sign: jest.fn(), - encryptData: input => (input ? String(input) : 'mockedString'), - decryptData: input => (input ? String(input) : 'mockedString'), - deviceSupportsHardware: () => true, -})); +jest.mock('@invertase/react-native-apple-authentication', () => {}); + +jest.mock('react-native-share', () => {}); jest.mock('../machines/store', () => ({ getItem: jest.fn(), diff --git a/__mocks__/react-native.mock.js b/__mocks__/react-native.mock.js index 33464336..b4934147 100644 --- a/__mocks__/react-native.mock.js +++ b/__mocks__/react-native.mock.js @@ -17,6 +17,12 @@ jest.mock('react-native', () => { SecureKeystore: { deviceSupportsHardware: jest.fn(), }, + RNSecureKeystoreModule: { + sign: jest.fn(), + encryptData: input => (input ? String(input) : 'mockedString'), + decryptData: input => (input ? String(input) : 'mockedString'), + deviceSupportsHardware: () => true, + }, }, }); diff --git a/components/ActivityLogEvent.test.ts b/components/ActivityLogEvent.test.ts index a5eb0d8d..85e9698f 100644 --- a/components/ActivityLogEvent.test.ts +++ b/components/ActivityLogEvent.test.ts @@ -23,7 +23,7 @@ describe('getActionText', () => { mockIl18nfn = jest.fn(); activityLog = new ActivityLog({ id: 'mockId', - idType: 'mockIDtype', + idType: ['mockIDtype'] as string[], _vcKey: 'mock_vc_key', type: 'mockType', timestamp: 1234, @@ -38,18 +38,17 @@ describe('getActionText', () => { return 'National ID'; } }); - getActionText(activityLog, mockIl18nfn); - expect(mockIl18nfn).toHaveBeenCalledWith(`VcDetails:mockIDtype`); + getActionText(activityLog, mockIl18nfn, {}); expect(mockIl18nfn).toHaveBeenCalledWith('mockType', { - idType: 'National ID', + idType: 'nationalCard', id: 'mockId', }); - expect(mockIl18nfn).toHaveBeenCalledTimes(2); + expect(mockIl18nfn).toHaveBeenCalledTimes(1); // TODO: assert the returned string }); it('should not fetch id type from translation file mock', () => { activityLog.idType = undefined; - getActionText(activityLog, mockIl18nfn); + getActionText(activityLog, mockIl18nfn, {}); expect(mockIl18nfn).toHaveBeenCalledWith('mockType', { idType: '', id: 'mockId', diff --git a/components/ActivityLogEvent.ts b/components/ActivityLogEvent.ts index eb789e53..a0b8e9a4 100644 --- a/components/ActivityLogEvent.ts +++ b/components/ActivityLogEvent.ts @@ -1,3 +1,5 @@ +import {getIdType} from './VC/common/VCUtils'; + export type ActivityLogType = | '' // replacement for undefined | 'VC_SHARED' @@ -18,21 +20,23 @@ export type ActivityLogType = export class ActivityLog { id: string; - idType: string; + idType: string[]; _vcKey: string; timestamp: number; deviceName: string; vcLabel: string; type: ActivityLogType; + issuer: string; constructor({ id = '', - idType = '', + idType = [], _vcKey = '', type = '', timestamp = Date.now(), deviceName = '', vcLabel = '', + issuer = '', } = {}) { this.id = id; this.idType = idType; @@ -41,6 +45,7 @@ export class ActivityLog { this.timestamp = timestamp; this.deviceName = deviceName; this.vcLabel = vcLabel; + this.issuer = issuer; } static logTamperedVCs() { @@ -50,13 +55,14 @@ export class ActivityLog { timestamp: Date.now(), deviceName: '', vcLabel: '', + issuer: '', }; } } -export function getActionText(activity: ActivityLog, t) { - if (activity.idType && activity.idType !== '') { - let cardType = t(`VcDetails:${activity.idType}`); +export function getActionText(activity: ActivityLog, t, wellknown: Object) { + if (activity.idType && activity.idType.length !== 0) { + const cardType = getIdType(wellknown, activity.idType); return `${t(activity.type, {idType: cardType, id: activity.id})}`; } return `${t(activity.type, {idType: '', id: activity.id})}`; diff --git a/components/ActivityLogText.tsx b/components/ActivityLogText.tsx index 44c192b2..dc885e31 100644 --- a/components/ActivityLogText.tsx +++ b/components/ActivityLogText.tsx @@ -5,15 +5,21 @@ import {useTranslation} from 'react-i18next'; import * as DateFnsLocale from 'date-fns/locale'; import {TextItem} from './ui/TextItem'; import {ActivityLog, getActionText} from './ActivityLogEvent'; +import {useHistoryTab} from '../screens/History/HistoryScreenController'; export const ActivityLogText: React.FC<{activity: ActivityLog}> = props => { const {t, i18n} = useTranslation('ActivityLogText'); + const historyController = useHistoryTab(); const {activity} = props; return ( ); diff --git a/components/BannerNotificationContainer.tsx b/components/BannerNotificationContainer.tsx index ecbd24f6..4d795788 100644 --- a/components/BannerNotificationContainer.tsx +++ b/components/BannerNotificationContainer.tsx @@ -78,9 +78,7 @@ export const BannerNotificationContainer: React.FC< = ({ @@ -13,11 +13,11 @@ const FaceCompare: React.FC = ({ isCapturing, isVerifying, service, - t + t, }) => { return ( - - + + = ({ align="center" weight="semibold" style={Theme.TextStyles.base} - margin="80 57" - > + margin="80 57"> {t('imageCaptureGuide')} @@ -47,7 +46,9 @@ const FaceCompare: React.FC = ({ service.send('CAPTURE')}> {SvgImage.CameraCaptureIcon()} - + {t('capture')} @@ -55,7 +56,9 @@ const FaceCompare: React.FC = ({ service.send('FLIP_CAMERA')}> {SvgImage.FlipCameraIcon()} - + {t('flipCamera')} @@ -69,10 +72,10 @@ const FaceCompare: React.FC = ({ export default FaceCompare; interface FaceCompareProps { - whichCamera: CameraType; - setCameraRef:(node: Camera) => void; - isCapturing: boolean; - isVerifying: boolean; - service: any; - t: (key: string) => string; - } \ No newline at end of file + whichCamera: CameraType; + setCameraRef: (node: Camera) => void; + isCapturing: boolean; + isVerifying: boolean; + service: any; + t: (key: string) => string; +} diff --git a/components/FaceScanner/FaceScannerHelper.ts b/components/FaceScanner/FaceScannerHelper.ts index 7306b47e..1fb035bf 100644 --- a/components/FaceScanner/FaceScannerHelper.ts +++ b/components/FaceScanner/FaceScannerHelper.ts @@ -45,7 +45,7 @@ export const imageCaptureConfig = { imageType: ImageType.jpg, }; -export const faceDetectorConfig : FaceDetectorConfig= { +export const faceDetectorConfig: FaceDetectorConfig = { mode: FaceDetector.FaceDetectorMode.accurate, detectLandmarks: FaceDetector.FaceDetectorLandmarks.all, runClassifications: FaceDetector.FaceDetectorClassifications.all, @@ -258,7 +258,7 @@ export interface FaceDetectorConfig { mode: FaceDetector.FaceDetectorMode; detectLandmarks: FaceDetector.FaceDetectorLandmarks; runClassifications: FaceDetector.FaceDetectorClassifications; - contourMode: FaceDetector.FaceDetectorClassifications; + contourMode: FaceDetector.FaceDetectorClassifications; minDetectionInterval: number; tracking: boolean; -}; \ No newline at end of file +} diff --git a/components/FaceScanner/LivenessDetection.tsx b/components/FaceScanner/LivenessDetection.tsx index f0237fff..d7df5d66 100644 --- a/components/FaceScanner/LivenessDetection.tsx +++ b/components/FaceScanner/LivenessDetection.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { Camera, CameraType } from 'expo-camera'; -import { View, TouchableOpacity } from 'react-native'; +import {Camera, CameraType} from 'expo-camera'; +import {View, TouchableOpacity} from 'react-native'; import Spinner from 'react-native-spinkit'; -import { Column, Text } from '.././ui'; -import { Theme } from '.././ui/styleUtils'; -import Svg, { Defs, Mask, Rect, Ellipse } from 'react-native-svg'; +import {Column, Text} from '.././ui'; +import {Theme} from '.././ui/styleUtils'; +import Svg, {Defs, Mask, Rect, Ellipse} from 'react-native-svg'; import testIDProps from '../../shared/commonUtil'; -import { FaceDetectorConfig } from './FaceScannerHelper'; +import {FaceDetectorConfig} from './FaceScannerHelper'; const LivenessDetection: React.FC = ({ screenColor, @@ -18,19 +18,24 @@ const LivenessDetection: React.FC = ({ handleOnCancel, opacity, setOpacity, - t + t, }) => { return ( - + - + {infoText} - + = ({ onFacesDetected={handleFacesDetected} faceDetectorSettings={faceDetectorConfig} /> - + - + @@ -56,12 +66,16 @@ const LivenessDetection: React.FC = ({ setOpacity(0.5)} onPressOut={() => setOpacity(1)} - onPress={handleOnCancel} - > - + onPress={handleOnCancel}> + {t('cancel')} @@ -73,14 +87,14 @@ const LivenessDetection: React.FC = ({ export default LivenessDetection; interface LivenessDetectionProps { - screenColor: string; - infoText: string; - whichCamera: CameraType; - setCameraRef: (node: Camera) => void; - handleFacesDetected: (faces: any) => Promise; - faceDetectorConfig: FaceDetectorConfig; - handleOnCancel: () => void; - opacity: number; - setOpacity: (opacity: number) => void; - t: (key: string) => string; - } \ No newline at end of file + screenColor: string; + infoText: string; + whichCamera: CameraType; + setCameraRef: (node: Camera) => void; + handleFacesDetected: (faces: any) => Promise; + faceDetectorConfig: FaceDetectorConfig; + handleOnCancel: () => void; + opacity: number; + setOpacity: (opacity: number) => void; + t: (key: string) => string; +} diff --git a/components/VC/Views/VCCardViewContent.tsx b/components/VC/Views/VCCardViewContent.tsx index 84ab3d49..0651933f 100644 --- a/components/VC/Views/VCCardViewContent.tsx +++ b/components/VC/Views/VCCardViewContent.tsx @@ -3,7 +3,7 @@ import {ImageBackground, Pressable, Image, View} from 'react-native'; import {getLocalizedField} from '../../../i18n'; import {VCMetadata} from '../../../shared/VCMetadata'; import {KebabPopUp} from '../../KebabPopUp'; -import {VerifiableCredential} from '../../../machines/VerifiableCredential/VCMetaMachine/vc'; +import {Credential} from '../../../machines/VerifiableCredential/VCMetaMachine/vc'; import {Column, Row} from '../../ui'; import {Theme} from '../../ui/styleUtils'; import {CheckBox, Icon} from 'react-native-elements'; @@ -13,7 +13,7 @@ import {isVCLoaded, getBackgroundColour} from '../common/VCUtils'; import {VCItemFieldValue} from '../common/VCItemField'; import {WalletBinding} from '../../../screens/Home/MyVcs/WalletBinding'; import {VCVerification} from '../../VCVerification'; -import {Issuers} from '../../../shared/openId4VCI/Utils'; +import {isActivationNeeded} from '../../../shared/openId4VCI/Utils'; import {VCItemContainerFlowType} from '../../../shared/Utils'; import {RemoveVcWarningOverlay} from '../../../screens/Home/MyVcs/RemoveVcWarningOverlay'; import {HistoryTab} from '../../../screens/Home/MyVcs/HistoryTab'; @@ -87,10 +87,10 @@ export const VCCardViewContent: React.FC = props => { {!Object.values(VCItemContainerFlowType).includes(props.flow) && ( <> - {props.vcMetadata.issuer === Issuers.Sunbird || - props.walletBindingResponse - ? SvgImage.walletActivatedIcon() - : SvgImage.walletUnActivatedIcon()} + {!props.walletBindingResponse && + isActivationNeeded(props.verifiableCredentialData?.issuer) + ? SvgImage.walletUnActivatedIcon() + : SvgImage.walletActivatedIcon()} = props => { export interface VCItemContentProps { context: any; - credential: VerifiableCredential; + credential: Credential; verifiableCredentialData: any; fields: []; wellknown: {}; diff --git a/components/VC/Views/VCDetailView.tsx b/components/VC/Views/VCDetailView.tsx index 363b8128..e2f2530f 100644 --- a/components/VC/Views/VCDetailView.tsx +++ b/components/VC/Views/VCDetailView.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {useTranslation} from 'react-i18next'; import {Image, ImageBackground, View} from 'react-native'; import { + Credential, VerifiableCredential, WalletBindingResponse, } from '../../../machines/VerifiableCredential/VCMetaMachine/vc'; diff --git a/components/VC/common/VCUtils.tsx b/components/VC/common/VCUtils.tsx index 4c6b5909..d9c7f60c 100644 --- a/components/VC/common/VCUtils.tsx +++ b/components/VC/common/VCUtils.tsx @@ -1,5 +1,8 @@ import { + Credential, CredentialSubject, + CredentialTypes, + IssuerWellknownResponse, VerifiableCredential, } from '../../../machines/VerifiableCredential/VCMetaMachine/vc'; import i18n, {getLocalizedField} from '../../../i18n'; @@ -8,10 +11,10 @@ import {VCItemField} from './VCItemField'; import React from 'react'; import {Theme} from '../../ui/styleUtils'; import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv'; -import {getIDType} from '../../../shared/openId4VCI/Utils'; import {VCVerification} from '../../VCVerification'; import {MIMOTO_BASE_URL} from '../../../shared/constants'; -import {useTranslation} from 'react-i18next'; +import {VCItemDetailsProps} from '../Views/VCDetailView'; +import {getSelectedCredentialTypeDetails} from '../../../shared/openId4VCI/Utils'; export const CARD_VIEW_DEFAULT_FIELDS = ['fullName']; export const DETAIL_VIEW_DEFAULT_FIELDS = [ @@ -26,8 +29,6 @@ export const DETAIL_VIEW_DEFAULT_FIELDS = [ //todo UIN & VID to be removed once we get the fields in the wellknown endpoint export const CARD_VIEW_ADD_ON_FIELDS = ['UIN', 'VID']; export const DETAIL_VIEW_ADD_ON_FIELDS = [ - 'UIN', - 'VID', 'status', 'credentialRegistry', 'idType', @@ -46,18 +47,11 @@ export const BOTTOM_SECTION_FIELDS_WITH_DETAILED_ADDRESS_FIELDS = [ ]; export const getFieldValue = ( - verifiableCredential: VerifiableCredential, + verifiableCredential: Credential, field: string, wellknown: any, props: any, ) => { - const {t} = useTranslation(); - const date = new Date( - getLocalizedField(verifiableCredential?.credentialSubject[field]), - ).toString(); - if (date !== 'Invalid Date') { - return formattedDateTime(date); - } switch (field) { case 'status': return ( @@ -67,7 +61,7 @@ export const getFieldValue = ( /> ); case 'idType': - return t(`VcDetails:${getIDType(verifiableCredential)}`); + return getIdType(wellknown); case 'credentialRegistry': return props?.vc?.credentialRegistry; case 'address': @@ -77,51 +71,22 @@ export const getFieldValue = ( default: { const fieldValue = verifiableCredential?.credentialSubject[field]; if (Array.isArray(fieldValue) && typeof fieldValue[0] !== 'object') { - return fieldValue; + return fieldValue.join(', '); } return getLocalizedField(fieldValue); } } }; -export const getCredentialDefinition = ( - wellknown: any, - vcCredentialTypes: Object[], -) => { - if (Array.isArray(wellknown.credentials_supported)) { - return wellknown.credentials_supported[0].credential_definition; - } else { - for (const supportedCredential in wellknown.credentials_supported) { - const credentialDefinition = - wellknown.credentials_supported[supportedCredential] - .credential_definition; - if ( - JSON.stringify(credentialDefinition.type) === - JSON.stringify(vcCredentialTypes) - ) { - return credentialDefinition; - } - } - return null; - } -}; - -export const getFieldName = ( - field: string, - wellknown: any, - vcCredentialTypes: Object[], -) => { - if (wellknown && wellknown.credentials_supported) { - const credentialDefinition = getCredentialDefinition( - wellknown, - vcCredentialTypes, - ); +export const getFieldName = (field: string, wellknown: any) => { + if (wellknown) { + const credentialDefinition = wellknown.credential_definition; if (!credentialDefinition) { console.error( 'Credential definition is not available for the selected credential type', ); } - let fieldObj = credentialDefinition.credentialSubject[field]; + let fieldObj = credentialDefinition?.credentialSubject[field]; if (fieldObj) { const newFieldObj = fieldObj.display.map(obj => { return {language: obj.locale, value: obj.name}; @@ -133,20 +98,11 @@ export const getFieldName = ( }; export const getBackgroundColour = (wellknown: any) => { - if (wellknown && wellknown.credentials_supported[0]?.display) { - return { - backgroundColor: wellknown.credentials_supported[0].display[0] - ?.background_color - ? wellknown.credentials_supported[0].display[0].background_color - : Theme.Colors.textValue, - }; - } + return wellknown?.display[0]?.background_color ?? Theme.Colors.textValue; }; export const getTextColor = (wellknown: any, defaultColor: string) => { - return ( - wellknown?.credentials_supported[0]?.display[0]?.text_color ?? defaultColor - ); + return wellknown?.display[0]?.text_color ?? defaultColor; }; export function getAddressFields() { @@ -174,26 +130,14 @@ function getFullAddress(credential: CredentialSubject) { .join(', '); } -function formattedDateTime(timeStamp: any) { - if (timeStamp) { - const options = {year: 'numeric', month: '2-digit', day: '2-digit'}; - return new Date(timeStamp).toLocaleDateString('en-US', options); - } - return timeStamp; -} - export const fieldItemIterator = ( fields: any[], - verifiableCredential: any, + verifiableCredential: VerifiableCredential | Credential, wellknown: any, - props: any, + props: VCItemDetailsProps, ) => { return fields.map(field => { - const fieldName = getFieldName( - field, - wellknown, - props.verifiableCredentialData.vcCredentialTypes, - ); + const fieldName = getFieldName(field, wellknown); const fieldValue = getFieldValue( verifiableCredential, field, @@ -225,7 +169,10 @@ export const fieldItemIterator = ( }); }; -export const isVCLoaded = (verifiableCredential: any, fields: string[]) => { +export const isVCLoaded = ( + verifiableCredential: Credential, + fields: string[], +) => { return verifiableCredential != null && fields.length > 0; }; @@ -235,3 +182,50 @@ export const getMosipLogo = () => { alt_text: 'a square logo of mosip', }; }; + +/** + * + * @param wellknown (either supportedCredential's wellknown or whole well known response of issuer) + * @param idType + * @returns id Type translations (Eg - National ID) + * + * supportedCredential's wellknown is passed from getActivityText after fresh download + * & all other consumers pass whole well known response of issuer + */ +export const getIdType = ( + wellknown: CredentialTypes | IssuerWellknownResponse, + idType?: string[], +) => { + if (wellknown && wellknown?.display) { + const idTypeObj = wellknown.display.map((displayProps: any) => { + return {language: displayProps.locale, value: displayProps.name}; + }); + return getLocalizedField(idTypeObj); + } else if ( + wellknown && + Object.keys(wellknown).length > 0 && + idType !== undefined + ) { + let supportedCredentialsWellknown; + wellknown = JSON.parse(wellknown) as Object[]; + if (!!!wellknown['credentials_supported']) { + return i18n.t('VcDetails:nationalCard'); + } + supportedCredentialsWellknown = getSelectedCredentialTypeDetails( + wellknown, + idType, + ); + if (Object.keys(supportedCredentialsWellknown).length === 0) { + return i18n.t('VcDetails:nationalCard'); + } + return getIdType(supportedCredentialsWellknown); + } else { + return i18n.t('VcDetails:nationalCard'); + } +}; + +export const getCredentialTypes = ( + credential: Credential | VerifiableCredential, +): string[] => { + return (credential?.credentialTypes as string[]) ?? ['VerifiableCredential']; +}; diff --git a/components/ui/Modal.tsx b/components/ui/Modal.tsx index 6ddd3265..ddc39478 100644 --- a/components/ui/Modal.tsx +++ b/components/ui/Modal.tsx @@ -19,64 +19,65 @@ export const Modal: React.FC = props => { visible={props.isVisible} onShow={props.onShow} onRequestClose={props.onDismiss}> - - { props.showHeader ? ( - - - {props.headerRight && !props.arrowLeft ? ( - - ) : null} - {props.arrowLeft && props.onDismiss ? ( - - ) : null} - - - - {props.headerTitle || props.headerLeft} - - {!props.requester ? ( - - {props.headerLabel} - - ) : ( - - - - )} - - - {props.headerRight != null || - props.arrowLeft || - (props.showClose && ( + + {props.showHeader ? ( + + + {props.headerRight && !props.arrowLeft ? ( - ))} - {props.headerRight && props.headerRight} - - ) : null} + ) : null} + {props.arrowLeft && props.onDismiss ? ( + + ) : null} + + + + {props.headerTitle || props.headerLeft} + + {!props.requester ? ( + + {props.headerLabel} + + ) : ( + + + + )} + + + {props.headerRight != null || + props.arrowLeft || + (props.showClose && ( + + ))} + {props.headerRight && props.headerRight} + + + ) : null} {props.children} diff --git a/components/ui/svg.tsx b/components/ui/svg.tsx index 67beba4d..e17f8b38 100644 --- a/components/ui/svg.tsx +++ b/components/ui/svg.tsx @@ -21,7 +21,7 @@ import ReceiveCard from '../../assets/Receive_Card.svg'; import ReceivedCards from '../../assets/Received_Cards.svg'; import ProgressIcon from '../../assets/Progress_Icon1.svg'; import testIDProps from '../../shared/commonUtil'; -import Logo from '../../assets/Inji_Logo'; +import Logo from '../../assets/Inji_Logo.svg'; import WarningLogo from '../../assets/Warning_Icon.svg'; import OtpVerificationIcon from '../../assets/Otp_Verification_Icon.svg'; import FlipCameraIcon from '../../assets/Flip_Camera_Icon.svg'; diff --git a/components/ui/themes/DefaultTheme.ts b/components/ui/themes/DefaultTheme.ts index bb45bb74..5e0ade88 100644 --- a/components/ui/themes/DefaultTheme.ts +++ b/components/ui/themes/DefaultTheme.ts @@ -1549,8 +1549,8 @@ export const DefaultTheme = { }, scannerContainer: { borderRadius: 24, - height: 320, - width: 300, + height: 350, + width: 320, marginTop: 40, backgroundColor: Colors.White, borderWidth: 1, @@ -1559,12 +1559,11 @@ export const DefaultTheme = { }), CameraEnabledStyles: StyleSheet.create({ - container: {marginTop: 20, marginBottom: 20}, scannerContainer: { borderRadius: 24, alignSelf: 'center', - height: 320, - width: 300, + height: 350, + width: 320, overflow: 'hidden', }, scanner: { diff --git a/components/ui/themes/PurpleTheme.ts b/components/ui/themes/PurpleTheme.ts index 3e8201b0..7ce5e01e 100644 --- a/components/ui/themes/PurpleTheme.ts +++ b/components/ui/themes/PurpleTheme.ts @@ -1549,8 +1549,8 @@ export const PurpleTheme = { }, scannerContainer: { borderRadius: 24, - height: 320, - width: 300, + height: 350, + width: 320, marginTop: 40, backgroundColor: Colors.White, borderWidth: 1, @@ -1558,12 +1558,11 @@ export const PurpleTheme = { }, }), CameraEnabledStyles: StyleSheet.create({ - container: {marginTop: 20, marginBottom: 20}, scannerContainer: { borderRadius: 24, alignSelf: 'center', - height: 320, - width: 300, + height: 350, + width: 320, overflow: 'hidden', }, scanner: { diff --git a/machines/Issuers/IssuersActions.ts b/machines/Issuers/IssuersActions.ts index 4c1fcf25..77fd52a2 100644 --- a/machines/Issuers/IssuersActions.ts +++ b/machines/Issuers/IssuersActions.ts @@ -1,9 +1,4 @@ -import { - ErrorMessage, - getIdType, - Issuers_Key_Ref, - updateVCmetadataOfCredentialWrapper, -} from '../../shared/openId4VCI/Utils'; +import {ErrorMessage, Issuers_Key_Ref} from '../../shared/openId4VCI/Utils'; import { MY_VCS_STORE_KEY, NETWORK_REQUEST_FAILED, @@ -61,12 +56,12 @@ export const IssuersActions = (model: any) => { setSelectedCredentialType: model.assign({ selectedCredentialType: (_: any, event: any) => event.credType, }), + setSupportedCredentialTypes: model.assign({ + supportedCredentialTypes: (_: any, event: any) => event.data, + }), resetSelectedCredentialType: model.assign({ selectedCredentialType: {}, }), - setCredentialTypes: model.assign({ - credentialTypes: (_: any, event: any) => event.data.supportedCredentials, - }), setError: model.assign({ errorMessage: (_: any, event: any) => { console.error('Error occurred ', event.data.message); @@ -119,10 +114,10 @@ export const IssuersActions = (model: any) => { ), setMetadataInCredentialData: (context: any) => { - return updateVCmetadataOfCredentialWrapper( - context, - context.credentialWrapper, - ); + context.credentialWrapper = { + ...context.credentialWrapper, + vcMetadata: context.vcMetadata, + }; }, setVCMetadata: assign({ @@ -132,11 +127,13 @@ export const IssuersActions = (model: any) => { }), storeVerifiableCredentialData: send( - (context: any) => - StoreEvents.SET(getVCMetadata(context).getVcKey(), { + (context: any) => { + const vcMeatadata = getVCMetadata(context); + return StoreEvents.SET(vcMeatadata.getVcKey(), { ...context.credentialWrapper, - vcMetadata: getVCMetadata(context), - }), + vcMetadata: vcMeatadata, + }); + }, { to: (context: any) => context.serviceRefs.store, }, @@ -201,15 +198,21 @@ export const IssuersActions = (model: any) => { logDownloaded: send( context => { - return ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: getVCMetadata(context).getVcKey(), - type: 'VC_DOWNLOADED', - id: getVCMetadata(context).id, - idType: getIdType(getVCMetadata(context).issuer), - timestamp: Date.now(), - deviceName: '', - vcLabel: getVCMetadata(context).id, - }); + const vcMetadata = getVCMetadata(context); + return ActivityLogEvents.LOG_ACTIVITY( + { + _vcKey: vcMetadata.getVcKey(), + type: 'VC_DOWNLOADED', + id: vcMetadata.id, + idType: + context.credentialWrapper.verifiableCredential.credentialTypes, + timestamp: Date.now(), + deviceName: '', + vcLabel: vcMetadata.id, + issuer: context.selectedIssuerId, + }, + context.selectedCredentialType, + ); }, { to: (context: any) => context.serviceRefs.activityLog, diff --git a/machines/Issuers/IssuersGuards.ts b/machines/Issuers/IssuersGuards.ts index 37c388b9..707bb358 100644 --- a/machines/Issuers/IssuersGuards.ts +++ b/machines/Issuers/IssuersGuards.ts @@ -1,8 +1,8 @@ import {isSignedInResult} from '../../shared/CloudBackupAndRestoreUtils'; -import {ErrorMessage, Issuers, OIDCErrors} from '../../shared/openId4VCI/Utils'; +import {ErrorMessage, OIDCErrors} from '../../shared/openId4VCI/Utils'; import {isHardwareKeystoreExists} from '../../shared/cryptoutil/cryptoUtil'; import {BiometricCancellationError} from '../../shared/error/BiometricCancellationError'; -import {NETWORK_REQUEST_FAILED, REQUEST_TIMEOUT} from '../../shared/constants'; +import {NETWORK_REQUEST_FAILED} from '../../shared/constants'; import {VerificationErrorType} from '../../shared/vcjs/verifyCredential'; export const IssuersGuards = () => { @@ -12,9 +12,6 @@ export const IssuersGuards = () => { isSignedIn: (_: any, event: any) => (event.data as isSignedInResult).isSignedIn, hasKeyPair: (context: any) => !!context.publicKey, - isMultipleCredentialsSupported: (context: any, event: any) => - event.data.supportedCredentials.length > 1 && - context.selectedIssuer.credential_issuer === Issuers.Sunbird, isInternetConnected: (_: any, event: any) => !!event.data.isConnected, isOIDCflowCancelled: (_: any, event: any) => { // iOS & Android have different error strings for user cancelled flow diff --git a/machines/Issuers/IssuersMachine.ts b/machines/Issuers/IssuersMachine.ts index d3e30562..a6861be2 100644 --- a/machines/Issuers/IssuersMachine.ts +++ b/machines/Issuers/IssuersMachine.ts @@ -1,9 +1,9 @@ import {EventFrom, send, sendParent} from 'xstate'; -import {log} from 'xstate/lib/actions'; import {IssuersModel} from './IssuersModel'; import {IssuersActions} from './IssuersActions'; import {IssuersService} from './IssuersService'; import {IssuersGuards} from './IssuersGuards'; +import {CredentialTypes} from '../VerifiableCredential/VCMetaMachine/vc'; const model = IssuersModel; @@ -106,8 +106,7 @@ export const IssuersMachine = model.createMachine( src: 'downloadCredentialTypes', onDone: [ { - actions: 'setCredentialTypes', - cond: 'isMultipleCredentialsSupported', + actions: 'setSupportedCredentialTypes', target: 'selectingCredentialType', }, { @@ -126,10 +125,7 @@ export const IssuersMachine = model.createMachine( target: 'displayIssuers', }, SELECTED_CREDENTIAL_TYPE: { - actions: [ - (_, event) => console.log('>>>>> event', event), - 'setSelectedCredentialType', - ], + actions: 'setSelectedCredentialType', target: 'checkInternet', }, }, @@ -413,9 +409,11 @@ export interface logoType { export interface displayType { name: string; - logo: logoType; - language: string; locale: string; + language: string; + logo: logoType; + background_color: string; + text_color: string; title: string; description: string; } @@ -426,14 +424,12 @@ export interface issuerType { client_id: string; '.well-known': string; redirect_uri: string; - scopes_supported: [string]; additional_headers: object; authorization_endpoint: string; - authorization_alias: string; token_endpoint: string; proxy_token_endpoint: string; credential_endpoint: string; - credential_type: [string]; credential_audience: string; display: [displayType]; + credentialTypes: [CredentialTypes]; } diff --git a/machines/Issuers/IssuersMachine.typegen.ts b/machines/Issuers/IssuersMachine.typegen.ts index 4a0a5e44..78a7eb28 100644 --- a/machines/Issuers/IssuersMachine.typegen.ts +++ b/machines/Issuers/IssuersMachine.typegen.ts @@ -104,7 +104,6 @@ export interface Typegen0 { | 'sendErrorEndEvent' | 'sendImpressionEvent' | 'sendSuccessEndEvent' - | 'setCredentialTypes' | 'setCredentialWrapper' | 'setError' | 'setIsVerified' @@ -120,6 +119,7 @@ export interface Typegen0 { | 'setSelectedCredentialType' | 'setSelectedIssuerId' | 'setSelectedIssuers' + | 'setSupportedCredentialTypes' | 'setTokenResponse' | 'setVCMetadata' | 'setVerifiableCredential' @@ -137,7 +137,6 @@ export interface Typegen0 { | 'isCustomSecureKeystore' | 'isGenericError' | 'isInternetConnected' - | 'isMultipleCredentialsSupported' | 'isOIDCConfigError' | 'isOIDCflowCancelled' | 'isSignedIn' @@ -186,7 +185,6 @@ export interface Typegen0 { sendErrorEndEvent: 'error.platform.issuersMachine.verifyingCredential:invocation[0]'; sendImpressionEvent: 'done.invoke.issuersMachine.displayIssuers:invocation[0]'; sendSuccessEndEvent: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'; - setCredentialTypes: 'done.invoke.issuersMachine.downloadCredentialTypes:invocation[0]'; setCredentialWrapper: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]'; setError: | 'error.platform.issuersMachine.displayIssuers:invocation[0]' @@ -216,6 +214,7 @@ export interface Typegen0 { setSelectedCredentialType: 'SELECTED_CREDENTIAL_TYPE'; setSelectedIssuerId: 'SELECTED_ISSUER'; setSelectedIssuers: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]'; + setSupportedCredentialTypes: 'done.invoke.issuersMachine.downloadCredentialTypes:invocation[0]'; setTokenResponse: 'done.invoke.issuersMachine.performAuthorization:invocation[0]'; setVCMetadata: | 'done.invoke.issuersMachine.verifyingCredential:invocation[0]' @@ -244,7 +243,6 @@ export interface Typegen0 { isCustomSecureKeystore: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]'; isGenericError: 'error.platform.issuersMachine.downloadCredentials:invocation[0]'; isInternetConnected: 'done.invoke.checkInternet'; - isMultipleCredentialsSupported: 'done.invoke.issuersMachine.downloadCredentialTypes:invocation[0]'; isOIDCConfigError: 'error.platform.issuersMachine.performAuthorization:invocation[0]'; isOIDCflowCancelled: 'error.platform.issuersMachine.performAuthorization:invocation[0]'; isSignedIn: 'done.invoke.issuersMachine.storing:invocation[0]'; diff --git a/machines/Issuers/IssuersModel.ts b/machines/Issuers/IssuersModel.ts index a799e617..5d6df08c 100644 --- a/machines/Issuers/IssuersModel.ts +++ b/machines/Issuers/IssuersModel.ts @@ -20,7 +20,7 @@ export const IssuersModel = createModel( loadingReason: 'displayIssuers' as string, verifiableCredential: null as VerifiableCredential | null, selectedCredentialType: {} as CredentialTypes, - credentialTypes: [] as CredentialTypes[], + supportedCredentialTypes: [] as CredentialTypes[], credentialWrapper: {} as CredentialWrapper, serviceRefs: {} as AppServices, verificationErrorMessage: '', diff --git a/machines/Issuers/IssuersSelectors.ts b/machines/Issuers/IssuersSelectors.ts index 1a661b79..ec193b75 100644 --- a/machines/Issuers/IssuersSelectors.ts +++ b/machines/Issuers/IssuersSelectors.ts @@ -54,6 +54,6 @@ export function selectSelectingCredentialType(state: State) { return state.matches('selectingCredentialType'); } -export function selectCredentialTypes(state: State) { - return state.context.credentialTypes; +export function selectSupportedCredentialTypes(state: State) { + return state.context.supportedCredentialTypes; } diff --git a/machines/Issuers/IssuersService.ts b/machines/Issuers/IssuersService.ts index b8ed6a44..7cb7c484 100644 --- a/machines/Issuers/IssuersService.ts +++ b/machines/Issuers/IssuersService.ts @@ -1,12 +1,9 @@ import Cloud from '../../shared/CloudBackupAndRestoreUtils'; -import {API_URLS, CACHED_API} from '../../shared/api'; +import {CACHED_API} from '../../shared/api'; import NetInfo from '@react-native-community/netinfo'; -import {request} from '../../shared/request'; import { constructAuthorizationConfiguration, constructProofJWT, - getCredentialType, - Issuers, Issuers_Key_Ref, updateCredentialInformation, vcDownloadTimeout, @@ -17,7 +14,6 @@ import { isHardwareKeystoreExists, } from '../../shared/cryptoutil/cryptoUtil'; import {NativeModules} from 'react-native'; -import {getVCMetadata, VCMetadata} from '../../shared/VCMetadata'; import { VerificationErrorType, verifyCredential, @@ -27,6 +23,7 @@ import { sendImpressionEvent, } from '../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants'; +import {isMosipVC} from '../../shared/Utils'; import {VciClient} from '../../shared/vciClient/VciClient'; const {RNSecureKeystoreModule} = NativeModules; @@ -43,20 +40,18 @@ export const IssuersService = () => { let issuersConfig = await CACHED_API.fetchIssuerConfig( context.selectedIssuerId, ); - if (context.selectedIssuer['.well-known']) { - await CACHED_API.fetchIssuerWellknownConfig( - context.selectedIssuerId, - context.selectedIssuer['.well-known'], - ); - } + const wellknownResponse = await CACHED_API.fetchIssuerWellknownConfig( + context.selectedIssuerId, + issuersConfig['.well-known'], + ); + issuersConfig.credential_endpoint = + wellknownResponse?.credential_endpoint; + issuersConfig.credential_audience = wellknownResponse?.credential_issuer; + issuersConfig.credentialTypes = wellknownResponse?.credentials_supported; return issuersConfig; }, downloadCredentialTypes: async (context: any) => { - const response = await request( - API_URLS.credentialTypes.method, - API_URLS.credentialTypes.buildURL(context.selectedIssuerId), - ); - return response?.response; + return context.selectedIssuer.credentialTypes; }, downloadCredential: async (context: any) => { const downloadTimeout = await vcDownloadTimeout(); @@ -65,7 +60,8 @@ export const IssuersService = () => { credentialAudience: context.selectedIssuer.credential_audience, credentialEndpoint: context.selectedIssuer.credential_endpoint, downloadTimeoutInMilliSeconds: downloadTimeout, - credentialType: getCredentialType(context), + credentialType: context.selectedCredentialType?.credential_definition + .type ?? ['VerifiableCredential'], credentialFormat: 'ldp_vc', }; const proofJWT = await constructProofJWT( @@ -81,8 +77,7 @@ export const IssuersService = () => { ); console.info(`VC download via ${context.selectedIssuerId} is successful`); - credential = updateCredentialInformation(context, credential); - return credential; + return updateCredentialInformation(context, credential); }, invokeAuthorization: async (context: any) => { sendImpressionEvent( @@ -92,16 +87,10 @@ export const IssuersService = () => { TelemetryConstants.Screens.webViewPage, ), ); - let supportedScopes: [string]; - if (Object.keys(context.selectedCredentialType).length === 0) { - supportedScopes = context.selectedIssuer.scopes_supported; - } else { - supportedScopes = [context.selectedCredentialType['scope']]; - } return await authorize( constructAuthorizationConfiguration( context.selectedIssuer, - supportedScopes, + context.selectedCredentialType.scope, ), ); }, @@ -118,21 +107,19 @@ export const IssuersService = () => { }, verifyCredential: async (context: any) => { //this issuer specific check has to be removed once vc validation is done. - if ( - VCMetadata.fromVcMetadataString(getVCMetadata(context)).issuer === - Issuers.Sunbird - ) { + if (isMosipVC(context.vcMetadata.issuer)) { + const verificationResult = await verifyCredential( + context.verifiableCredential?.credential, + ); + if (!verificationResult.isVerified) { + throw new Error(verificationResult.errorMessage); + } + } else { return { isVerified: true, errorMessage: VerificationErrorType.NO_ERROR, }; } - const verificationResult = await verifyCredential( - context.verifiableCredential?.credential, - ); - if (!verificationResult.isVerified) { - throw new Error(verificationResult.errorMessage); - } }, }; }; diff --git a/machines/QrLogin/QrLoginSelectors.ts b/machines/QrLogin/QrLoginSelectors.ts index 2d65d57b..bc6e7056 100644 --- a/machines/QrLogin/QrLoginSelectors.ts +++ b/machines/QrLogin/QrLoginSelectors.ts @@ -2,6 +2,7 @@ import {StateFrom} from 'xstate'; import {getMosipLogo} from '../../components/VC/common/VCUtils'; import {VCMetadata} from '../../shared/VCMetadata'; import {qrLoginMachine} from './QrLoginMachine'; +import {Credential} from '../VerifiableCredential/VCMetaMachine/vc'; type State = StateFrom; @@ -57,31 +58,29 @@ export function selectIsVerifyingSuccesful(state: State) { return state.matches('success'); } -export function selectCredential(state: State) { - return new VCMetadata(state.context.selectedVc?.vcMetadata).isFromOpenId4VCI() - ? state.context.selectedVc?.verifiableCredential?.credential - : state.context.selectedVc?.credential; +export function selectCredential(state: State): Credential { + return ( + state.context.selectedVc?.verifiableCredential?.credential || + state.context.selectedVc?.credential + ); } export function selectVerifiableCredentialData(state: State) { const vcMetadata = new VCMetadata(state.context.selectedVc?.vcMetadata); - return vcMetadata.isFromOpenId4VCI() - ? { - vcMetadata: vcMetadata, - face: state.context.selectedVc?.verifiableCredential?.credential - ?.credentialSubject?.face, - issuerLogo: state.context.selectedVc?.verifiableCredential?.issuerLogo, - wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown, - credentialTypes: - state.context.selectedVc?.verifiableCredential?.credentialTypes, - issuer: vcMetadata.issuer, - } - : { - vcMetadata: vcMetadata, - issuer: vcMetadata.issuer, - face: state.context.selectedVc?.credential?.biometrics?.face, - issuerLogo: getMosipLogo(), - }; + 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, + credentialTypes: + state.context.selectedVc?.verifiableCredential?.credentialTypes, + }; } export function selectLinkTransactionResponse(state: State) { diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts b/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts index 6e473378..d82a0d8f 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts @@ -2,17 +2,18 @@ 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 { + API_CACHED_STORAGE_KEYS, + MIMOTO_BASE_URL, + MY_VCS_STORE_KEY, +} from '../../../shared/constants'; import {KeyPair} from 'react-native-rsa-native'; import i18n from '../../../i18n'; import {getHomeMachineService} from '../../../screens/Home/HomeScreenController'; import {DownloadProps} from '../../../shared/api'; import {isHardwareKeystoreExists} from '../../../shared/cryptoutil/cryptoUtil'; import {getBindingCertificateConstant} from '../../../shared/keystore/SecureKeystore'; -import { - getIdType, - getVcVerificationDetails, -} from '../../../shared/openId4VCI/Utils'; +import {getVcVerificationDetails} from '../../../shared/openId4VCI/Utils'; import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; import { sendStartEvent, @@ -30,6 +31,7 @@ import {BackupEvents} from '../../backupAndRestore/backup'; import {VcMetaEvents} from '../VCMetaMachine/VCMetaMachine'; import {WalletBindingResponse} from '../VCMetaMachine/vc'; import {BannerStatusType} from '../../../components/BannerNotification'; +import {getCredentialTypes} from '../../../components/VC/common/VCUtils'; export const VCItemActions = model => { return { @@ -61,6 +63,7 @@ export const VCItemActions = model => { statusType, context.vcMetadata, context.verifiableCredential, + context.wellknownResponse, ); }, }), @@ -124,10 +127,11 @@ export const VCItemActions = model => { ), setContext: model.assign((context, event) => { + const vcMetadata = context.vcMetadata; return { ...context, ...event.response, - vcMetadata: context.vcMetadata, + vcMetadata: VCMetadata.fromVC(vcMetadata), }; }), storeContext: send( @@ -149,16 +153,22 @@ export const VCItemActions = model => { to: context => context.serviceRefs.store, }, ), + setVcMetadata: assign({ vcMetadata: (_, event) => event.vcMetadata, }), + + updateWellknownResponse: assign({ + wellknownResponse: (_, event) => event.data, + }), + storeVcInContext: send( //todo : separate handling done for openid4vci , handle commonly from vc machine (context: any) => { - const {serviceRefs, ...verifiableCredential} = context; + const {serviceRefs, wellknownResponse, ...data} = context; return { type: 'VC_DOWNLOADED', - vc: verifiableCredential, + vc: data, vcMetadata: context.vcMetadata, }; }, @@ -442,7 +452,8 @@ export const VCItemActions = model => { _vcKey: context.vcMetadata.getVcKey(), type: 'VC_DOWNLOADED', id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), + issuer: context.vcMetadata.issuer!!, + idType: getCredentialTypes(context.verifiableCredential), timestamp: Date.now(), deviceName: '', vcLabel: data.id, @@ -453,47 +464,56 @@ export const VCItemActions = model => { }, ), logRemovedVc: send( - (context: any, _) => - ActivityLogEvents.LOG_ACTIVITY({ - idType: getIdType(context.vcMetadata.issuer), - id: context.vcMetadata.id, - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + (context: any, _) => { + const vcMetadata = VCMetadata.fromVC(context.vcMetadata); + return ActivityLogEvents.LOG_ACTIVITY({ + idType: getCredentialTypes(context.verifiableCredential), + issuer: vcMetadata.issuer!!, + id: vcMetadata.id, + _vcKey: vcMetadata.getVcKey(), type: 'VC_REMOVED', timestamp: Date.now(), deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), + vcLabel: vcMetadata.id, + }); + }, { to: context => context.serviceRefs.activityLog, }, ), logWalletBindingSuccess: send( - (context: any) => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + (context: any) => { + const vcMetadata = VCMetadata.fromVC(context.vcMetadata); + return ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: vcMetadata.getVcKey(), type: 'WALLET_BINDING_SUCCESSFULL', - idType: getIdType(context.vcMetadata.issuer), - id: context.vcMetadata.id, + idType: getCredentialTypes(context.verifiableCredential), + issuer: vcMetadata.issuer!!, + id: vcMetadata.id, timestamp: Date.now(), deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), + vcLabel: vcMetadata.id, + }); + }, { to: context => context.serviceRefs.activityLog, }, ), logWalletBindingFailure: send( - (context: any) => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + (context: any) => { + const vcMetadata = VCMetadata.fromVC(context.vcMetadata); + return ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: vcMetadata.getVcKey(), type: 'WALLET_BINDING_FAILURE', - id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), + id: vcMetadata.id, + idType: getCredentialTypes(context.verifiableCredential), + issuer: vcMetadata.issuer!!, timestamp: Date.now(), deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), + vcLabel: vcMetadata.id, + }); + }, { to: context => context.serviceRefs.activityLog, }, diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts b/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts deleted file mode 100644 index d14585f6..00000000 --- a/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {VCMetadata} from '../../../shared/VCMetadata'; -import {VC} from '../VCMetaMachine/vc'; - -export const VCItemEvents = { - DISMISS: () => ({}), - CREDENTIAL_DOWNLOADED: (response: VC) => ({response}), - STORE_RESPONSE: (response: VC) => ({response}), - STORE_ERROR: (error: Error) => ({error}), - POLL: () => ({}), - DOWNLOAD_READY: () => ({}), - FAILED: () => ({}), - GET_VC_RESPONSE: (response: VC) => ({response}), - INPUT_OTP: (OTP: string) => ({OTP}), - RESEND_OTP: () => ({}), - REFRESH: () => ({}), - ADD_WALLET_BINDING_ID: () => ({}), - CANCEL: () => ({}), - CONFIRM: () => ({}), - PIN_CARD: () => ({}), - KEBAB_POPUP: () => ({}), - SHOW_ACTIVITY: () => ({}), - CLOSE_VC_MODAL: () => ({}), - REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}), - UPDATE_VC_METADATA: (vcMetadata: VCMetadata) => ({vcMetadata}), - TAMPERED_VC: (key: string) => ({key}), - SHOW_BINDING_STATUS: () => ({}), - VERIFY: () => ({}), - SET_VERIFICATION_STATUS: (response: unknown) => ({response}), - RESET_VERIFICATION_STATUS: () => ({}), - REMOVE_VERIFICATION_STATUS_BANNER: () => ({}), - SHOW_VERIFICATION_STATUS_BANNER: (response: unknown) => ({response}), -}; diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts b/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts index 544de8cb..d8bb64c0 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts @@ -4,9 +4,16 @@ import {VerificationErrorType} from '../../../shared/vcjs/verifyCredential'; export const VCItemGaurds = () => { return { + hasCredentialAndWellknown: (context, event) => { + const vc = event.response; + return ( + vc?.verifiableCredential != null && + !!context.verifiableCredential?.wellKnown + ); + }, hasCredential: (_, event) => { const vc = event.response; - return vc?.credential != null || vc?.verifiableCredential != null; + return vc?.verifiableCredential != null; }, isSignedIn: (_context, event) => (event.data as isSignedInResult).isSignedIn, diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts index 24dc2b8a..74568f5c 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts @@ -44,6 +44,11 @@ export const VCItemMachine = model.createMachine( description: 'Fetch the VC data from the Memory.', on: { GET_VC_RESPONSE: [ + { + actions: ['setContext'], + cond: 'hasCredentialAndWellknown', + target: '.fetchWellknown', + }, { actions: ['setContext'], cond: 'hasCredential', @@ -58,6 +63,20 @@ export const VCItemMachine = model.createMachine( target: '#vc-item-machine.vcUtilitiesState.idle', }, }, + initial: 'idle', + states: { + idle: {}, + fetchWellknown: { + invoke: { + src: 'fetchIssuerWellknown', + onDone: { + actions: 'updateWellknownResponse', + + target: `#vc-item-machine.vcUtilitiesState.idle`, + }, + }, + }, + }, }, loadVcFromServer: { description: @@ -492,10 +511,10 @@ export const VCItemMachine = model.createMachine( { cond: 'isSignedIn', actions: ['sendBackupEvent'], - target: '#vc-item-machine.vcUtilitiesState.idle', + target: `#vc-item-machine.vcUtilitiesState.idle`, }, { - target: '#vc-item-machine.vcUtilitiesState.idle', + target: `#vc-item-machine.vcUtilitiesState.idle`, }, ], }, diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts index 08d33f69..e0e4de88 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts @@ -19,6 +19,11 @@ export interface Typegen0 { data: unknown; __tip: 'See the XState TS docs to learn how to strongly type this.'; }; + 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]': { + type: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]': { type: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; data: unknown; @@ -120,6 +125,7 @@ export interface Typegen0 { checkDownloadExpiryLimit: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; checkStatus: 'done.invoke.checkStatus'; downloadCredential: 'done.invoke.downloadCredential'; + fetchIssuerWellknown: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]'; generateKeyPair: 'done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]'; isUserSignedAlready: | 'done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]' @@ -186,10 +192,12 @@ export interface Typegen0 { | 'unSetBindingTransactionId' | 'unSetError' | 'unSetOTP' - | 'updateVcMetadata'; + | 'updateVcMetadata' + | 'updateWellknownResponse'; delays: never; guards: | 'hasCredential' + | 'hasCredentialAndWellknown' | 'isCustomSecureKeystore' | 'isDownloadAllowed' | 'isSignedIn' @@ -199,6 +207,7 @@ export interface Typegen0 { | 'checkDownloadExpiryLimit' | 'checkStatus' | 'downloadCredential' + | 'fetchIssuerWellknown' | 'generateKeyPair' | 'isUserSignedAlready' | 'loadDownloadLimitConfig' @@ -334,10 +343,12 @@ export interface Typegen0 { | 'DISMISS' | 'done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]'; updateVcMetadata: 'PIN_CARD' | 'STORE_RESPONSE'; + updateWellknownResponse: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]'; }; eventsCausingDelays: {}; eventsCausingGuards: { hasCredential: 'GET_VC_RESPONSE'; + hasCredentialAndWellknown: 'GET_VC_RESPONSE'; isCustomSecureKeystore: | 'done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]' | 'done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]'; @@ -356,6 +367,7 @@ export interface Typegen0 { | 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; checkStatus: 'done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; downloadCredential: 'DOWNLOAD_READY'; + fetchIssuerWellknown: 'GET_VC_RESPONSE'; generateKeyPair: 'INPUT_OTP'; isUserSignedAlready: 'STORE_RESPONSE'; loadDownloadLimitConfig: 'GET_VC_RESPONSE' | 'STORE_ERROR'; @@ -375,6 +387,8 @@ export interface Typegen0 { | 'vcUtilitiesState.kebabPopUp.triggerAutoBackup' | 'vcUtilitiesState.loadVc' | 'vcUtilitiesState.loadVc.loadVcFromContext' + | 'vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown' + | 'vcUtilitiesState.loadVc.loadVcFromContext.idle' | 'vcUtilitiesState.loadVc.loadVcFromServer' | 'vcUtilitiesState.loadVc.loadVcFromServer.checkingStatus' | 'vcUtilitiesState.loadVc.loadVcFromServer.downloadingCredential' @@ -421,6 +435,7 @@ export interface Typegen0 { | 'loadVcFromContext' | 'loadVcFromServer' | { + loadVcFromContext?: 'fetchWellknown' | 'idle'; loadVcFromServer?: | 'checkingStatus' | 'downloadingCredential' diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts b/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts index b1c62cc1..74e3015a 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts @@ -1,17 +1,52 @@ import {createModel} from 'xstate/lib/model'; import {AppServices} from '../../../shared/GlobalContext'; import {VCMetadata} from '../../../shared/VCMetadata'; -import {VerifiableCredential, WalletBindingResponse} from '../VCMetaMachine/vc'; +import { + Credential, + DecodedCredential, + VC, + WalletBindingResponse, +} from '../VCMetaMachine/vc'; import {CommunicationDetails} from '../../../shared/Utils'; -import {VCItemEvents} from './VCItemEvents'; import {vcVerificationBannerDetails} from '../../../components/BannerNotificationContainer'; +const VCItemEvents = { + DISMISS: () => ({}), + CREDENTIAL_DOWNLOADED: (response: VC) => ({response}), + STORE_RESPONSE: (response: VC) => ({response}), + STORE_ERROR: (error: Error) => ({error}), + POLL: () => ({}), + DOWNLOAD_READY: () => ({}), + FAILED: () => ({}), + GET_VC_RESPONSE: (response: VC) => ({response}), + INPUT_OTP: (OTP: string) => ({OTP}), + RESEND_OTP: () => ({}), + REFRESH: () => ({}), + ADD_WALLET_BINDING_ID: () => ({}), + CANCEL: () => ({}), + CONFIRM: () => ({}), + PIN_CARD: () => ({}), + KEBAB_POPUP: () => ({}), + SHOW_ACTIVITY: () => ({}), + CLOSE_VC_MODAL: () => ({}), + REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}), + UPDATE_VC_METADATA: (vcMetadata: VCMetadata) => ({vcMetadata}), + TAMPERED_VC: (key: string) => ({key}), + SHOW_BINDING_STATUS: () => ({}), + VERIFY: () => ({}), + SET_VERIFICATION_STATUS: (response: unknown) => ({response}), + RESET_VERIFICATION_STATUS: () => ({}), + REMOVE_VERIFICATION_STATUS_BANNER: () => ({}), + SHOW_VERIFICATION_STATUS_BANNER: (response: unknown) => ({response}), +}; + export const VCItemModel = createModel( { serviceRefs: {} as AppServices, vcMetadata: {} as VCMetadata, generatedOn: new Date() as Date, - verifiableCredential: null as unknown as VerifiableCredential, + credential: null as unknown as DecodedCredential, + verifiableCredential: null as unknown as Credential, hashedId: '', publicKey: '', privateKey: '', @@ -27,6 +62,7 @@ export const VCItemModel = createModel( communicationDetails: null as unknown as CommunicationDetails, verificationStatus: null as vcVerificationBannerDetails | null, showVerificationStatusBanner: false as boolean, + wellknownResponse: {} as Object, }, { events: VCItemEvents, diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts b/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts index bcb254ca..d52091cb 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts @@ -2,6 +2,11 @@ import {StateFrom} from 'xstate'; import {VCMetadata} from '../../../shared/VCMetadata'; import {VCItemMachine} from './VCItemMachine'; import {getMosipLogo} from '../../../components/VC/common/VCUtils'; +import { + Credential, + VerifiableCredential, + VerifiableCredentialData, +} from '../VCMetaMachine/vc'; type State = StateFrom; @@ -26,39 +31,31 @@ export function selectVerifiableCredential(state: State) { } export function getVerifiableCredential( - vcMetadata: VCMetadata, - verifiableCredential, -) { - return VCMetadata.fromVC(vcMetadata).isFromOpenId4VCI() - ? verifiableCredential?.credential - : verifiableCredential; + verifiableCredential: VerifiableCredential | Credential, +): Credential { + return verifiableCredential?.credential || verifiableCredential; } -export function selectCredential(state: State) { - return getVerifiableCredential( - state.context.vcMetadata, - state.context.verifiableCredential, - ); +export function selectCredential(state: State): Credential { + return getVerifiableCredential(state.context.verifiableCredential); } -export function selectVerifiableCredentialData(state: State) { +export function selectVerifiableCredentialData( + state: State, +): VerifiableCredentialData { const vcMetadata = new VCMetadata(state.context.vcMetadata); - return vcMetadata.isFromOpenId4VCI() - ? { - vcMetadata: vcMetadata, - face: state.context.verifiableCredential?.credential?.credentialSubject - .face, - issuerLogo: state.context.verifiableCredential?.issuerLogo, - wellKnown: state.context.verifiableCredential?.wellKnown, - credentialTypes: state.context.verifiableCredential?.credentialTypes, - issuer: vcMetadata.issuer, - } - : { - vcMetadata: vcMetadata, - issuer: vcMetadata.issuer, - face: state.context.credential?.biometrics?.face, - issuerLogo: getMosipLogo(), - }; + + return { + vcMetadata: vcMetadata, + face: + state.context.verifiableCredential?.credential?.credentialSubject?.face ?? + state.context.credential?.biometrics?.face, + issuerLogo: + state.context.verifiableCredential?.issuerLogo ?? getMosipLogo(), + wellKnown: state.context.verifiableCredential?.wellKnown, + credentialTypes: state.context.verifiableCredential?.credentialTypes, + issuer: vcMetadata.issuer, + }; } export function selectKebabPopUp(state: State) { diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts b/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts index ac1872b3..aaab00d9 100644 --- a/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts +++ b/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts @@ -3,6 +3,7 @@ import Cloud from '../../../shared/CloudBackupAndRestoreUtils'; import {VCMetadata} from '../../../shared/VCMetadata'; import getAllConfigurations, { API_URLS, + CACHED_API, DownloadProps, } from '../../../shared/api'; import { @@ -16,8 +17,9 @@ import { import {CredentialDownloadResponse, request} from '../../../shared/request'; import {WalletBindingResponse} from '../VCMetaMachine/vc'; import {verifyCredential} from '../../../shared/vcjs/verifyCredential'; -import {getMosipIdentifier} from '../../../shared/commonUtil'; import {getVerifiableCredential} from './VCItemSelectors'; +import {getSelectedCredentialTypeDetails} from '../../../shared/openId4VCI/Utils'; +import {getCredentialTypes} from '../../../components/VC/common/VCUtils'; const {RNSecureKeystoreModule} = NativeModules; export const VCItemServices = model => { @@ -105,9 +107,7 @@ export const VCItemServices = model => { ); }, requestBindingOTP: async context => { - const vc = VCMetadata.fromVC(context.vcMetadata).isFromOpenId4VCI() - ? context.verifiableCredential.credential - : context.verifiableCredential; + const vc = getVerifiableCredential(context.verifiableCredential); const response = await request( API_URLS.bindingOtp.method, API_URLS.bindingOtp.buildURL(), @@ -124,6 +124,18 @@ export const VCItemServices = model => { } return response; }, + fetchIssuerWellknown: async context => { + const wellknownResponse = await CACHED_API.fetchIssuerWellknownConfig( + context.vcMetadata.issuer, + context.verifiableCredential.wellKnown, + true, + ); + const wellknownOfCredential = getSelectedCredentialTypeDetails( + wellknownResponse, + getCredentialTypes(context.verifiableCredential), + ); + return wellknownOfCredential; + }, checkStatus: context => (callback, onReceive) => { const pollInterval = setInterval( () => callback(model.events.POLL()), @@ -199,10 +211,7 @@ export const VCItemServices = model => { verifyCredential: async context => { if (context.verifiableCredential) { const verificationResult = await verifyCredential( - getVerifiableCredential( - context.vcMetadata, - context.verifiableCredential, - ), + getVerifiableCredential(context.verifiableCredential), ); if (!verificationResult.isVerified) { throw new Error(verificationResult.errorMessage); diff --git a/machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors.ts b/machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors.ts index 945c3079..7896a3ab 100644 --- a/machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors.ts +++ b/machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors.ts @@ -15,7 +15,6 @@ export function selectMyVcsMetadata(state: State): VCMetadata[] { export function selectShareableVcsMetadata(state: State): VCMetadata[] { return state.context.myVcsMetadata.filter( vcMetadata => - state.context.myVcs[vcMetadata.getVcKey()]?.credential != null || state.context.myVcs[vcMetadata.getVcKey()]?.verifiableCredential != null, ); } diff --git a/machines/VerifiableCredential/VCMetaMachine/vc.d.ts b/machines/VerifiableCredential/VCMetaMachine/vc.d.ts index 572aed39..de01a52f 100644 --- a/machines/VerifiableCredential/VCMetaMachine/vc.d.ts +++ b/machines/VerifiableCredential/VCMetaMachine/vc.d.ts @@ -1,15 +1,17 @@ import {displayType, logoType} from '../../Issuers/IssuersMachine'; +import {VCMetadata} from '../../../shared/VCMetadata'; export interface VC { id?: string; idType?: VcIdType; credential?: DecodedCredential; - verifiableCredential: VerifiableCredential; + verifiableCredential: VerifiableCredential | Credential; requestId?: string; isVerified?: boolean; lastVerifiedOn: number; walletBindingResponse?: WalletBindingResponse; hashedId?: string; + vcMetadata: VCMetadata; } export type VcIdType = 'UIN' | 'VID'; @@ -54,7 +56,8 @@ export interface Credential { type: 'RsaSignature2018' | string; verificationMethod: string; }; - type: VerifiableCredentialType[]; + type: string[]; + credentialTypes: string[]; } export interface VerifiableCredential { @@ -65,11 +68,20 @@ export interface VerifiableCredential { credentialTypes: Object[]; } +export interface VerifiableCredentialData { + vcMetadata: VCMetadata; + face: string; + issuerLogo: logoType; + wellKnown?: string; + credentialTypes?: Object[]; + issuer?: string; +} + export interface CredentialWrapper { verifiableCredential: VerifiableCredential; identifier: string; generatedOn: Date; - issuerLogo: string; + vcMetadata: VCMetadata; } export interface CredentialTypes { @@ -84,10 +96,11 @@ export interface CredentialTypes { }; } -export type VerifiableCredentialType = - | 'VerifiableCredential' - | 'MOSIPVerfiableCredential' - | string; +export interface IssuerWellknownResponse { + credential_issuer: string; + credential_endpoint: string; + credentials_supported: Object[]; +} export interface VCLabel { singular: string; @@ -116,3 +129,16 @@ export interface WalletBindingResponse { thumbprint: string; expireDateTime: string; } + +//TODO: Check if Type word is needed in the naming +export interface VCMetadataType { + //TODO: requestId is not null at any point as its used for file names and all + isPinned: boolean; + requestId: string | null; + issuer: string; + protocol: string; + id: string; + timestamp: string; + isVerified: boolean; + credentialType: string; +} diff --git a/machines/activityLog.ts b/machines/activityLog.ts index 0ea74c2e..bad9ddad 100644 --- a/machines/activityLog.ts +++ b/machines/activityLog.ts @@ -9,11 +9,15 @@ const model = createModel( { serviceRefs: {} as AppServices, activities: [] as ActivityLog[], + wellKnownIssuerMap: {} as Record, }, { events: { STORE_RESPONSE: (response: unknown) => ({response}), - LOG_ACTIVITY: (log: ActivityLog | ActivityLog[]) => ({log}), + LOG_ACTIVITY: (log: ActivityLog | ActivityLog[], wellknown?: any) => ({ + log, + wellknown, + }), REFRESH: () => ({}), }, }, @@ -40,6 +44,15 @@ export const activityLogMachine = on: { STORE_RESPONSE: { actions: ['setActivities', sendParent('READY')], + target: 'loadWellknownConfig', + }, + }, + }, + loadWellknownConfig: { + entry: 'fetchAllWellKnownConfigResponse', + on: { + STORE_RESPONSE: { + actions: ['setAllWellknownConfigResponse'], target: 'ready', }, }, @@ -58,7 +71,7 @@ export const activityLogMachine = }, }, logging: { - entry: 'storeActivity', + entry: ['loadWellknownIntoContext', 'storeActivity'], on: { STORE_RESPONSE: { actions: 'prependActivity', @@ -94,11 +107,41 @@ export const activityLogMachine = {to: context => context.serviceRefs.store}, ), + loadWellknownIntoContext: model.assign({ + wellKnownIssuerMap: (context, event) => { + if (!!event.wellknown) { + const updatedWellKnownIssuerMap = { + ...context.wellKnownIssuerMap, + }; + updatedWellKnownIssuerMap[event.log.issuer] = event.wellknown; + return updatedWellKnownIssuerMap as unknown as Record< + string, + Object + >; + } + return context.wellKnownIssuerMap; + }, + }), + prependActivity: model.assign({ - activities: (context, event) => - (Array.isArray(event.response) - ? [...event.response, ...context.activities] - : [event.response, ...context.activities]) as ActivityLog[], + activities: (context, event) => { + return ( + Array.isArray(event.response) + ? [...event.response, ...context.activities] + : [event.response, ...context.activities] + ) as ActivityLog[]; + }, + }), + + fetchAllWellKnownConfigResponse: send( + () => StoreEvents.FETCH_ALL_WELLKNOWN_CONFIG(), + {to: context => context.serviceRefs.store}, + ), + + setAllWellknownConfigResponse: model.assign({ + wellKnownIssuerMap: (_, event) => { + return event.response as Record; + }, }), }, }, @@ -134,6 +177,10 @@ export function selectActivities(state: State) { return state.context.activities; } +export function selectWellknownIssuerMap(state: State) { + return state.context.wellKnownIssuerMap; +} + export function selectIsRefreshing(state: State) { return state.matches('ready.refreshing'); } diff --git a/machines/activityLog.typegen.ts b/machines/activityLog.typegen.ts index ff117107..24cedabf 100644 --- a/machines/activityLog.typegen.ts +++ b/machines/activityLog.typegen.ts @@ -1,36 +1,36 @@ +// This file was automatically generated. Edits will be overwritten - // This file was automatically generated. Edits will be overwritten - - export interface Typegen0 { - '@@xstate/typegen': true; - internalEvents: { - "xstate.init": { type: "xstate.init" }; - }; - invokeSrcNameMap: { - - }; - missingImplementations: { - actions: never; - delays: never; - guards: never; - services: never; - }; - eventsCausingActions: { - "loadActivities": "REFRESH" | "xstate.init"; -"prependActivity": "STORE_RESPONSE"; -"setActivities": "STORE_RESPONSE"; -"storeActivity": "LOG_ACTIVITY"; - }; - eventsCausingDelays: { - - }; - eventsCausingGuards: { - - }; - eventsCausingServices: { - - }; - matchesStates: "init" | "ready" | "ready.idle" | "ready.logging" | "ready.refreshing" | { "ready"?: "idle" | "logging" | "refreshing"; }; - tags: never; - } - \ No newline at end of file +export interface Typegen0 { + '@@xstate/typegen': true; + internalEvents: { + 'xstate.init': {type: 'xstate.init'}; + }; + invokeSrcNameMap: {}; + missingImplementations: { + actions: never; + delays: never; + guards: never; + services: never; + }; + eventsCausingActions: { + fetchAllWellKnownConfigResponse: 'STORE_RESPONSE'; + loadActivities: 'REFRESH' | 'xstate.init'; + loadWellknownIntoContext: 'LOG_ACTIVITY'; + prependActivity: 'STORE_RESPONSE'; + setActivities: 'STORE_RESPONSE'; + setAllWellknownConfigResponse: 'STORE_RESPONSE'; + storeActivity: 'LOG_ACTIVITY'; + }; + eventsCausingDelays: {}; + eventsCausingGuards: {}; + eventsCausingServices: {}; + matchesStates: + | 'init' + | 'loadWellknownConfig' + | 'ready' + | 'ready.idle' + | 'ready.logging' + | 'ready.refreshing' + | {ready?: 'idle' | 'logging' | 'refreshing'}; + tags: never; +} diff --git a/machines/bleShare/request/requestMachine.ts b/machines/bleShare/request/requestMachine.ts index 8d64dfa7..5385a74c 100644 --- a/machines/bleShare/request/requestMachine.ts +++ b/machines/bleShare/request/requestMachine.ts @@ -37,7 +37,7 @@ import { sendStartEvent, } from '../../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; -import {getIdType} from '../../../shared/openId4VCI/Utils'; +import {getCredentialTypes} from '../../../components/VC/common/VCUtils'; const {verifier, EventTypes, VerificationStatus} = tuvali; @@ -620,7 +620,10 @@ export const requestMachine = _vcKey: vcMetadata.getVcKey(), type: context.receiveLogType, id: vcMetadata.id, - idType: getIdType(vcMetadata.issuer), + idType: getCredentialTypes( + context.incomingVc.verifiableCredential, + ), + issuer: vcMetadata.issuer!!, timestamp: Date.now(), deviceName: context.senderInfo.name || context.senderInfo.deviceName, diff --git a/machines/bleShare/request/selectors.ts b/machines/bleShare/request/selectors.ts index 7b670ac1..308e2283 100644 --- a/machines/bleShare/request/selectors.ts +++ b/machines/bleShare/request/selectors.ts @@ -2,6 +2,7 @@ import {StateFrom} from 'xstate'; import {requestMachine} from './requestMachine'; import {VCMetadata} from '../../../shared/VCMetadata'; import {getMosipLogo} from '../../../components/VC/common/VCUtils'; +import {Credential} from '../../VerifiableCredential/VCMetaMachine/vc'; type State = StateFrom; @@ -9,31 +10,29 @@ export function selectSenderInfo(state: State) { return state.context.senderInfo; } -export function selectCredential(state: State) { - return new VCMetadata(state.context.incomingVc?.vcMetadata).isFromOpenId4VCI() - ? state.context.incomingVc?.verifiableCredential?.credential - : state.context.incomingVc?.verifiableCredential; +export function selectCredential(state: State): Credential { + return ( + state.context.incomingVc?.verifiableCredential?.credential || + state.context.incomingVc?.verifiableCredential + ); } export function selectVerifiableCredentialData(state: State) { const vcMetadata = new VCMetadata(state.context.incomingVc?.vcMetadata); - return vcMetadata.isFromOpenId4VCI() - ? { - vcMetadata: vcMetadata, - face: state.context.incomingVc?.verifiableCredential.credential - .credentialSubject.face, - issuerLogo: state.context.incomingVc?.verifiableCredential?.issuerLogo, - wellKnown: state.context.incomingVc?.verifiableCredential?.wellKnown, - credentialTypes: - state.context.incomingVc?.verifiableCredential?.credentialTypes, - issuer: vcMetadata.issuer, - } - : { - vcMetadata: vcMetadata, - issuer: vcMetadata.issuer, - face: state.context.incomingVc?.credential?.biometrics?.face, - issuerLogo: getMosipLogo(), - }; + return { + vcMetadata: vcMetadata, + face: + state.context.incomingVc?.verifiableCredential.credential + ?.credentialSubject?.face || + state.context.incomingVc?.credential?.biometrics?.face, + issuerLogo: + state.context.incomingVc?.verifiableCredential?.issuerLogo || + getMosipLogo(), + issuer: vcMetadata.issuer, + wellKnown: state.context.incomingVc?.verifiableCredential?.wellKnown, + credentialTypes: + state.context.incomingVc?.verifiableCredential?.credentialTypes, + }; } export function selectIsReviewingInIdle(state: State) { diff --git a/machines/bleShare/scan/scanActions.ts b/machines/bleShare/scan/scanActions.ts index 6aab8030..d08ee7f3 100644 --- a/machines/bleShare/scan/scanActions.ts +++ b/machines/bleShare/scan/scanActions.ts @@ -11,7 +11,6 @@ import { MY_VCS_STORE_KEY, MY_LOGIN_STORE_KEY, } from '../../../shared/constants'; -import {getIdType} from '../../../shared/openId4VCI/Utils'; import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; import { sendImpressionEvent, @@ -30,6 +29,7 @@ import {StoreEvents} from '../../store'; import tuvali from '@mosip/tuvali'; import BluetoothStateManager from 'react-native-bluetooth-state-manager'; import {NativeModules} from 'react-native'; +import {getCredentialTypes} from '../../../components/VC/common/VCUtils'; const {wallet, EventTypes, VerificationStatus} = tuvali; export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => { @@ -191,14 +191,16 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => { logShared: send( (context: any) => { - const vcMetadata = context.selectedVc?.vcMetadata; + const vcMetadata = VCMetadata.fromVC(context.selectedVc?.vcMetadata); + const selectedVc = context.QrLoginRef.getSnapshot().context.selectedVc; return ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(vcMetadata).getVcKey(), + _vcKey: vcMetadata.getVcKey(), type: context.shareLogType ? context.shareLogType : 'VC_SHARED_WITH_VERIFICATION_CONSENT', id: vcMetadata.id, - idType: getIdType(vcMetadata.issuer), + idType: getCredentialTypes(selectedVc.verifiableCredential), + issuer: vcMetadata.issuer!!, timestamp: Date.now(), deviceName: context.receiverInfo.name || context.receiverInfo.deviceName, @@ -209,17 +211,21 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => { ), logFailedVerification: send( - context => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(context.selectedVc).getVcKey(), + context => { + const vcMetadata = VCMetadata.fromVC(context.selectedVc); + const selectedVc = context.QrLoginRef.getSnapshot().context.selectedVc; + return ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: vcMetadata.getVcKey(), type: 'PRESENCE_VERIFICATION_FAILED', timestamp: Date.now(), - idType: getIdType(context.selectedVc.vcMetadata.issuer), - id: context.selectedVc.vcMetadata.id, + idType: getCredentialTypes(selectedVc.verifiableCredential), + id: vcMetadata.id, + issuer: vcMetadata.issuer!!, deviceName: context.receiverInfo.name || context.receiverInfo.deviceName, - vcLabel: context.selectedVc.vcMetadata.id, - }), + vcLabel: vcMetadata.id, + }); + }, {to: context => context.serviceRefs.activityLog}, ), @@ -242,11 +248,8 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => { ), loadVCDataToMemory: send( (context: any) => { - let metadata = VCMetadata.fromVC(context.quickShareData?.meta); - - let verifiableCredential = metadata.isFromOpenId4VCI() - ? {credential: context.quickShareData?.verifiableCredential} - : context.quickShareData?.verifiableCredential; + const verifiableCredential = + context.quickShareData?.verifiableCredential; return StoreEvents.SET(metadata.getVcKey(), { verifiableCredential: verifiableCredential, @@ -268,16 +271,22 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => { ), storingActivityLog: send( - (_, event) => - ActivityLogEvents.LOG_ACTIVITY({ + (context, event) => { + const vcMetadata = event.response.selectedVc.vcMetadata; + + const selectedVc = context.QrLoginRef.getSnapshot().context.selectedVc; + + return ActivityLogEvents.LOG_ACTIVITY({ _vcKey: '', - id: event.response.selectedVc.vcMetadata.id, - idType: getIdType(event.response.selectedVc.vcMetadata.issuer), + id: vcMetadata.id, + issuer: vcMetadata.issuer!!, + idType: getCredentialTypes(selectedVc.verifiableCredential), type: 'QRLOGIN_SUCCESFULL', timestamp: Date.now(), deviceName: '', - vcLabel: String(event.response.selectedVc.vcMetadata.id), - }), + vcLabel: String(vcMetadata.id), + }); + }, { to: (context: any) => context.serviceRefs.activityLog, }, diff --git a/machines/bleShare/scan/scanSelectors.ts b/machines/bleShare/scan/scanSelectors.ts index a5159c41..64c77a00 100644 --- a/machines/bleShare/scan/scanSelectors.ts +++ b/machines/bleShare/scan/scanSelectors.ts @@ -18,30 +18,28 @@ export function selectVcName(state: State) { } export function selectCredential(state: State) { - return new VCMetadata(state.context.selectedVc?.vcMetadata).isFromOpenId4VCI() - ? state.context.selectedVc?.verifiableCredential?.credential - : state.context.selectedVc?.verifiableCredential; + return ( + state.context.selectedVc?.verifiableCredential?.credential || + state.context.selectedVc?.verifiableCredential + ); } export function selectVerifiableCredentialData(state: State) { const vcMetadata = new VCMetadata(state.context.selectedVc?.vcMetadata); - return vcMetadata.isFromOpenId4VCI() - ? { - vcMetadata: vcMetadata, - issuer: vcMetadata.issuer, - issuerLogo: state.context.selectedVc?.verifiableCredential?.issuerLogo, - wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown, - face: state.context.selectedVc?.verifiableCredential?.credential - .credentialSubject?.face, - credentialTypes: - state.context.selectedVc?.verifiableCredential?.credentialTypes, - } - : { - vcMetadata: vcMetadata, - issuer: vcMetadata.issuer, - face: state.context.selectedVc?.credential?.biometrics?.face, - issuerLogo: getMosipLogo(), - }; + 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, + credentialTypes: + state.context.selectedVc?.verifiableCredential?.credentialTypes, + }; } export function selectQrLoginRef(state: State) { diff --git a/machines/faceScanner.typegen.ts b/machines/faceScanner.typegen.ts index 17757e15..b7e60e89 100644 --- a/machines/faceScanner.typegen.ts +++ b/machines/faceScanner.typegen.ts @@ -1,47 +1,71 @@ +// This file was automatically generated. Edits will be overwritten - // This file was automatically generated. Edits will be overwritten - - export interface Typegen0 { - '@@xstate/typegen': true; - internalEvents: { - "done.invoke.faceScanner.capturing:invocation[0]": { type: "done.invoke.faceScanner.capturing:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." }; -"done.invoke.faceScanner.verifying:invocation[0]": { type: "done.invoke.faceScanner.verifying:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." }; -"error.platform.faceScanner.capturing:invocation[0]": { type: "error.platform.faceScanner.capturing:invocation[0]"; data: unknown }; -"xstate.init": { type: "xstate.init" }; - }; - invokeSrcNameMap: { - "captureImage": "done.invoke.faceScanner.capturing:invocation[0]"; -"checkPermission": "done.invoke.faceScanner.init.checkingPermission:invocation[0]"; -"requestPermission": "done.invoke.faceScanner.init.requestingPermission:invocation[0]"; -"verifyImage": "done.invoke.faceScanner.verifying:invocation[0]"; - }; - missingImplementations: { - actions: never; - delays: never; - guards: never; - services: never; - }; - eventsCausingActions: { - "flipWhichCamera": "FLIP_CAMERA"; -"openSettings": "OPEN_SETTINGS"; -"setCameraRef": "READY"; -"setCaptureError": "error.platform.faceScanner.capturing:invocation[0]"; -"setCapturedImage": "done.invoke.faceScanner.capturing:invocation[0]"; - }; - eventsCausingDelays: { - - }; - eventsCausingGuards: { - "canRequestPermission": "DENIED"; -"doesFaceMatch": "done.invoke.faceScanner.verifying:invocation[0]"; - }; - eventsCausingServices: { - "captureImage": "CAPTURE"; -"checkPermission": "APP_FOCUSED" | "xstate.init"; -"requestPermission": "DENIED"; -"verifyImage": "done.invoke.faceScanner.capturing:invocation[0]"; - }; - matchesStates: "capturing" | "init" | "init.checkingPermission" | "init.permissionDenied" | "init.permissionGranted" | "init.requestingPermission" | "invalid" | "scanning" | "valid" | "verifying" | { "init"?: "checkingPermission" | "permissionDenied" | "permissionGranted" | "requestingPermission"; }; - tags: never; - } - \ No newline at end of file +export interface Typegen0 { + '@@xstate/typegen': true; + internalEvents: { + 'done.invoke.faceScanner.capturing:invocation[0]': { + type: 'done.invoke.faceScanner.capturing:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.faceScanner.verifying:invocation[0]': { + type: 'done.invoke.faceScanner.verifying:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'error.platform.faceScanner.capturing:invocation[0]': { + type: 'error.platform.faceScanner.capturing:invocation[0]'; + data: unknown; + }; + 'xstate.init': {type: 'xstate.init'}; + }; + invokeSrcNameMap: { + captureImage: 'done.invoke.faceScanner.capturing:invocation[0]'; + checkPermission: 'done.invoke.faceScanner.init.checkingPermission:invocation[0]'; + requestPermission: 'done.invoke.faceScanner.init.requestingPermission:invocation[0]'; + verifyImage: 'done.invoke.faceScanner.verifying:invocation[0]'; + }; + missingImplementations: { + actions: never; + delays: never; + guards: never; + services: never; + }; + eventsCausingActions: { + flipWhichCamera: 'FLIP_CAMERA'; + openSettings: 'OPEN_SETTINGS'; + setCameraRef: 'READY'; + setCaptureError: 'error.platform.faceScanner.capturing:invocation[0]'; + setCapturedImage: 'done.invoke.faceScanner.capturing:invocation[0]'; + }; + eventsCausingDelays: {}; + eventsCausingGuards: { + canRequestPermission: 'DENIED'; + doesFaceMatch: 'done.invoke.faceScanner.verifying:invocation[0]'; + }; + eventsCausingServices: { + captureImage: 'CAPTURE'; + checkPermission: 'APP_FOCUSED' | 'xstate.init'; + requestPermission: 'DENIED'; + verifyImage: 'done.invoke.faceScanner.capturing:invocation[0]'; + }; + matchesStates: + | 'capturing' + | 'init' + | 'init.checkingPermission' + | 'init.permissionDenied' + | 'init.permissionGranted' + | 'init.requestingPermission' + | 'invalid' + | 'scanning' + | 'valid' + | 'verifying' + | { + init?: + | 'checkingPermission' + | 'permissionDenied' + | 'permissionGranted' + | 'requestingPermission'; + }; + tags: never; +} diff --git a/machines/settings.ts b/machines/settings.ts index 1ffc9261..69fca0ee 100644 --- a/machines/settings.ts +++ b/machines/settings.ts @@ -390,4 +390,4 @@ export function selectIsPasscodeUnlock(state: State) { return ( state.context.isBiometricToggled && !state.context.isBiometricUnlockEnabled ); -} \ No newline at end of file +} diff --git a/machines/settings.typegen.ts b/machines/settings.typegen.ts index 0be916ed..37a729a1 100644 --- a/machines/settings.typegen.ts +++ b/machines/settings.typegen.ts @@ -1,53 +1,70 @@ +// This file was automatically generated. Edits will be overwritten - // This file was automatically generated. Edits will be overwritten - - export interface Typegen0 { - '@@xstate/typegen': true; - internalEvents: { - "done.invoke.settings.resetInjiProps:invocation[0]": { type: "done.invoke.settings.resetInjiProps:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." }; -"error.platform.settings.resetInjiProps:invocation[0]": { type: "error.platform.settings.resetInjiProps:invocation[0]"; data: unknown }; -"xstate.init": { type: "xstate.init" }; - }; - invokeSrcNameMap: { - "resetInjiProps": "done.invoke.settings.resetInjiProps:invocation[0]"; - }; - missingImplementations: { - actions: never; - delays: never; - guards: never; - services: never; - }; - eventsCausingActions: { - "requestStoredContext": "xstate.init"; -"resetCredentialRegistryResponse": "CANCEL" | "UPDATE_HOST"; -"resetIsBiometricToggled": "DISMISS"; -"setBackupAndRestoreOptionExplored": "SET_IS_BACKUP_AND_RESTORE_EXPLORED"; -"setContext": "STORE_RESPONSE"; -"setIsBiometricToggled": "TOGGLE_BIOMETRIC_UNLOCK"; -"storeContext": "ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS" | "SET_IS_BACKUP_AND_RESTORE_EXPLORED" | "SHOWN_ACCOUNT_SELECTION_CONFIRMATION" | "STORE_RESPONSE" | "TOGGLE_BIOMETRIC_UNLOCK" | "UPDATE_HOST" | "UPDATE_NAME" | "UPDATE_VC_LABEL" | "done.invoke.settings.resetInjiProps:invocation[0]"; -"toggleBiometricUnlock": "TOGGLE_BIOMETRIC_UNLOCK"; -"updateCredentialRegistry": "done.invoke.settings.resetInjiProps:invocation[0]"; -"updateCredentialRegistryResponse": "error.platform.settings.resetInjiProps:invocation[0]"; -"updateCredentialRegistrySuccess": "done.invoke.settings.resetInjiProps:invocation[0]"; -"updateDefaults": "STORE_RESPONSE"; -"updateEsignetHostUrl": "UPDATE_HOST"; -"updateIsAccountSelectionConfirmationShown": "SHOWN_ACCOUNT_SELECTION_CONFIRMATION"; -"updateName": "UPDATE_NAME"; -"updatePartialDefaults": "STORE_RESPONSE"; -"updateUserShownWithHardwareKeystoreNotExists": "ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS"; -"updateVcLabel": "UPDATE_VC_LABEL"; - }; - eventsCausingDelays: { - - }; - eventsCausingGuards: { - "hasData": "STORE_RESPONSE"; -"hasPartialData": "STORE_RESPONSE"; - }; - eventsCausingServices: { - "resetInjiProps": "UPDATE_HOST"; - }; - matchesStates: "idle" | "init" | "resetInjiProps" | "showInjiTourGuide" | "storingDefaults"; - tags: never; - } - \ No newline at end of file +export interface Typegen0 { + '@@xstate/typegen': true; + internalEvents: { + 'done.invoke.settings.resetInjiProps:invocation[0]': { + type: 'done.invoke.settings.resetInjiProps:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'error.platform.settings.resetInjiProps:invocation[0]': { + type: 'error.platform.settings.resetInjiProps:invocation[0]'; + data: unknown; + }; + 'xstate.init': {type: 'xstate.init'}; + }; + invokeSrcNameMap: { + resetInjiProps: 'done.invoke.settings.resetInjiProps:invocation[0]'; + }; + missingImplementations: { + actions: never; + delays: never; + guards: never; + services: never; + }; + eventsCausingActions: { + requestStoredContext: 'xstate.init'; + resetCredentialRegistryResponse: 'CANCEL' | 'UPDATE_HOST'; + resetIsBiometricToggled: 'DISMISS'; + setBackupAndRestoreOptionExplored: 'SET_IS_BACKUP_AND_RESTORE_EXPLORED'; + setContext: 'STORE_RESPONSE'; + setIsBiometricToggled: 'TOGGLE_BIOMETRIC_UNLOCK'; + storeContext: + | 'ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS' + | 'SET_IS_BACKUP_AND_RESTORE_EXPLORED' + | 'SHOWN_ACCOUNT_SELECTION_CONFIRMATION' + | 'STORE_RESPONSE' + | 'TOGGLE_BIOMETRIC_UNLOCK' + | 'UPDATE_HOST' + | 'UPDATE_NAME' + | 'UPDATE_VC_LABEL' + | 'done.invoke.settings.resetInjiProps:invocation[0]'; + toggleBiometricUnlock: 'TOGGLE_BIOMETRIC_UNLOCK'; + updateCredentialRegistry: 'done.invoke.settings.resetInjiProps:invocation[0]'; + updateCredentialRegistryResponse: 'error.platform.settings.resetInjiProps:invocation[0]'; + updateCredentialRegistrySuccess: 'done.invoke.settings.resetInjiProps:invocation[0]'; + updateDefaults: 'STORE_RESPONSE'; + updateEsignetHostUrl: 'UPDATE_HOST'; + updateIsAccountSelectionConfirmationShown: 'SHOWN_ACCOUNT_SELECTION_CONFIRMATION'; + updateName: 'UPDATE_NAME'; + updatePartialDefaults: 'STORE_RESPONSE'; + updateUserShownWithHardwareKeystoreNotExists: 'ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS'; + updateVcLabel: 'UPDATE_VC_LABEL'; + }; + eventsCausingDelays: {}; + eventsCausingGuards: { + hasData: 'STORE_RESPONSE'; + hasPartialData: 'STORE_RESPONSE'; + }; + eventsCausingServices: { + resetInjiProps: 'UPDATE_HOST'; + }; + matchesStates: + | 'idle' + | 'init' + | 'resetInjiProps' + | 'showInjiTourGuide' + | 'storingDefaults'; + tags: never; +} diff --git a/machines/store.ts b/machines/store.ts index f0134be5..915eb46a 100644 --- a/machines/store.ts +++ b/machines/store.ts @@ -77,6 +77,7 @@ const model = createModel( requester, }), STORE_ERROR: (error: Error, requester?: string) => ({error, requester}), + FETCH_ALL_WELLKNOWN_CONFIG: () => ({}), }, }, ); @@ -233,6 +234,9 @@ export const storeMachine = CLEAR: { actions: 'forwardStoreRequest', }, + FETCH_ALL_WELLKNOWN_CONFIG: { + actions: 'forwardStoreRequest', + }, STORE_RESPONSE: { actions: [ send( @@ -442,6 +446,13 @@ export const storeMachine = await clear(); break; } + case 'FETCH_ALL_WELLKNOWN_CONFIG': { + response = await fetchAllWellknownConfig( + context.encryptionKey, + ); + break; + } + default: return; } @@ -586,6 +597,10 @@ export async function loadBackupData(data, encryptionKey) { await Storage.loadBackupData(data, encryptionKey); } +export async function fetchAllWellknownConfig(encryptionKey: string) { + return await Storage.fetchAllWellknownConfig(encryptionKey); +} + export async function getVCsData(key: string, encryptionKey: string) { try { let vcsData: Record = {}; diff --git a/machines/store.typegen.ts b/machines/store.typegen.ts index dac36268..bbcd79c7 100644 --- a/machines/store.typegen.ts +++ b/machines/store.typegen.ts @@ -35,6 +35,7 @@ export interface Typegen0 { | 'APPEND' | 'CLEAR' | 'EXPORT' + | 'FETCH_ALL_WELLKNOWN_CONFIG' | 'GET' | 'GET_VCS_DATA' | 'PREPEND' diff --git a/screens/History/HistoryScreenController.ts b/screens/History/HistoryScreenController.ts index a7a5b303..a7a7eeb7 100644 --- a/screens/History/HistoryScreenController.ts +++ b/screens/History/HistoryScreenController.ts @@ -1,21 +1,30 @@ -import { useSelector } from '@xstate/react'; -import { useContext } from 'react'; +import {useSelector} from '@xstate/react'; +import {useContext} from 'react'; import { ActivityLogEvents, selectActivities, selectIsRefreshing, + selectWellknownIssuerMap, } from '../../machines/activityLog'; -import { GlobalContext } from '../../shared/GlobalContext'; +import {GlobalContext} from '../../shared/GlobalContext'; export function useHistoryTab() { - const { appService } = useContext(GlobalContext); - const activityLogService = appService.children.get('activityLog'); + const {appService} = useContext(GlobalContext); + const activityLogService = appService.children.get('activityLog')!!; + const wellknownIssuerMap = useSelector( + activityLogService, + selectWellknownIssuerMap, + ); return { activities: useSelector(activityLogService, selectActivities), isRefreshing: useSelector(activityLogService, selectIsRefreshing), + getWellKnownIssuerMap: (issuerName: string) => { + return wellknownIssuerMap[issuerName] ?? null; + }, + REFRESH: () => activityLogService.send(ActivityLogEvents.REFRESH()), }; } diff --git a/screens/Home/ViewVcModal.tsx b/screens/Home/ViewVcModal.tsx index 4bd289f5..efbb8edf 100644 --- a/screens/Home/ViewVcModal.tsx +++ b/screens/Home/ViewVcModal.tsx @@ -134,9 +134,7 @@ export const ViewVcModal: React.FC = props => { item.id} renderItem={({item}) => { diff --git a/screens/Issuers/IssuerScreenController.tsx b/screens/Issuers/IssuerScreenController.tsx index 8231aeff..29e3c804 100644 --- a/screens/Issuers/IssuerScreenController.tsx +++ b/screens/Issuers/IssuerScreenController.tsx @@ -1,6 +1,6 @@ import {useSelector} from '@xstate/react'; import { - selectCredentialTypes, + selectSupportedCredentialTypes, selectErrorMessageType, selectIsBiometricCancelled, selectIsDone, @@ -41,7 +41,10 @@ export function useIssuerScreenController({route, navigation}) { service, selectSelectingCredentialType, ), - credentialTypes: useSelector(service, selectCredentialTypes), + supportedCredentialTypes: useSelector( + service, + selectSupportedCredentialTypes, + ), verificationErrorMessage: useSelector( service, selectVerificationErrorMessage, diff --git a/screens/QrLogin/QrLogin.tsx b/screens/QrLogin/QrLogin.tsx index 4efebd23..9e821efd 100644 --- a/screens/QrLogin/QrLogin.tsx +++ b/screens/QrLogin/QrLogin.tsx @@ -12,7 +12,7 @@ import {Icon} from 'react-native-elements'; import {View} from 'react-native'; import {FaceVerificationAlertOverlay} from '../Scan/FaceVerificationAlertOverlay'; import {SvgImage} from '../../components/ui/svg'; -import { LIVENESS_CHECK } from '../../shared/constants'; +import {LIVENESS_CHECK} from '../../shared/constants'; export const QrLogin: React.FC = props => { const controller = useQrLogin(props); diff --git a/screens/Scan/ScanLayout.tsx b/screens/Scan/ScanLayout.tsx index 142aaf40..b54e6dfc 100644 --- a/screens/Scan/ScanLayout.tsx +++ b/screens/Scan/ScanLayout.tsx @@ -15,7 +15,7 @@ import {SvgImage} from '../../components/ui/svg'; import {View, I18nManager} from 'react-native'; import {Text} from './../../components/ui'; import {BannerStatusType} from '../../components/BannerNotification'; -import { LIVENESS_CHECK } from '../../shared/constants'; +import {LIVENESS_CHECK} from '../../shared/constants'; const ScanStack = createNativeStackNavigator(); diff --git a/screens/Scan/SendVcScreen.tsx b/screens/Scan/SendVcScreen.tsx index d90dbb21..63e5a403 100644 --- a/screens/Scan/SendVcScreen.tsx +++ b/screens/Scan/SendVcScreen.tsx @@ -17,13 +17,13 @@ import { import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants'; import { getVCsOrderedByPinStatus, + isMosipVC, VCItemContainerFlowType, } from '../../shared/Utils'; -import {Issuers} from '../../shared/openId4VCI/Utils'; import {FaceVerificationAlertOverlay} from './FaceVerificationAlertOverlay'; import {Error} from '../../components/ui/Error'; import {SvgImage} from '../../components/ui/svg'; -import { LIVENESS_CHECK } from '../../shared/constants'; +import {LIVENESS_CHECK} from '../../shared/constants'; export const SendVcScreen: React.FC = () => { const {t} = useTranslation('SendVcScreen'); @@ -102,9 +102,7 @@ export const SendVcScreen: React.FC = () => { - {[Issuers.MosipOtp, Issuers.Mosip].indexOf( - controller.verifiableCredentialData.issuer, - ) !== -1 && ( + {isMosipVC(controller.verifiableCredentialData.issuer) && (