mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-06 20:23:52 -05:00
[INJIMOB-3647] refactor: update isRevoked data type (#2149)
* [INJIMOB-3647] refactor: modify data type of isRevoked to EvaluationStatus Type representing any possible value of EvaluationStatus. - "TRUE" → Condition was evaluated and is positively true - "FALSE" → Condition was evaluated and is definitively false - "UNDETERMINED" → Condition could not be evaluated due to an error Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3647] refactor: modify data type of isRevoked to EvaluationStatus Type representing any possible value of EvaluationStatus. - "TRUE" → Condition was evaluated and is positively true - "FALSE" → Condition was evaluated and is definitively false - "UNDETERMINED" → Condition could not be evaluated due to an error Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3647] refactor: change statuslistVC type to record from string Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> # Conflicts: # shared/vcjs/verifyCredential.ts * [INJIMOB-3647] refactor: update status revoke check to check for null status Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3647] refactor: VCMetadat constructor isRevoked param Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3647] refactor: rename EvaluationStatus to RevocationStatus Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> * [INJIMOB-3647] refactor: modify revocation status logs Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com> --------- Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
This commit is contained in:
committed by
GitHub
parent
0e667bd46d
commit
9457ad0d9f
10
.talismanrc
10
.talismanrc
@@ -64,7 +64,7 @@ fileignoreconfig:
|
||||
- filename: assets/Finger_Print_Icon.svg
|
||||
checksum: 776d4fe4fc4b54d185ccf97daf0511b9fe2c0e0f7c1a809047020e5e8a100db6
|
||||
- filename: screens/MainLayout.tsx
|
||||
checksum: dd31361997111c28461239e986112a30ee986e99432ac3016033508863b90ddd
|
||||
checksum: 8909ef957c866221e864c6edaf93081af7f5968857200284bf7047631f525322
|
||||
- filename: android/app/build.gradle
|
||||
checksum: 8d5715e179a398518e6acff82c75b27077c9f893dc90b2972c77f9a09f10be95
|
||||
- filename: .github/workflows/push-triggers.yml
|
||||
@@ -270,7 +270,7 @@ fileignoreconfig:
|
||||
- filename: machines/Issuers/IssuersService.ts
|
||||
checksum: e3832dff27687abc28609d2b281e570b4b0017995b7cfb56627a6b96949c469a
|
||||
- filename: screens/Home/ViewVcModal.tsx
|
||||
checksum: cfb25d562185488432b76287c4ef93359c1c64d8e29f5755d4c0a726c1485442
|
||||
checksum: 847d45d566b7fc86dd3ebbba74bf587399dd7754466e42ebdecd462a157705e9
|
||||
- filename: injitest/src/main/resources/TestData.json
|
||||
checksum: 1b5af14c96b456898259b4cb7a5607b006404cf0360274bdc204d7d065698e3c
|
||||
- filename: injitest/src/test/java/androidTestCases/ActivateVcTest.java
|
||||
@@ -385,7 +385,7 @@ fileignoreconfig:
|
||||
- filename: android/app/src/main/java/io/mosip/residentapp/InjiOpenId4VPModule.java
|
||||
checksum: 6b315164dca5de95c11e0dc8cbb480207b19c312b1c9135adc39ef74a1ff7e35
|
||||
- filename: screens/Scan/SendVPScreenController.ts
|
||||
checksum: f898ac7f1ecfa1df17e33b327d675f57debf2d5bd56052fc047dd03577354590
|
||||
checksum: aa228c43a01e653b9da6ee354a39c942bec25848aa9631650611d1b5f85623d7
|
||||
- filename: screens/Scan/SendVPScreen.tsx
|
||||
checksum: de80cb9a932ed99e224438a8c373d117807101a39a440e97977654ef6935af6c
|
||||
- filename: machines/openID4VP/openID4VPMachine.typegen.ts
|
||||
@@ -416,4 +416,8 @@ fileignoreconfig:
|
||||
checksum: 669e85d1c8ff526b97fa4ed4b8ed33a100eaba9f2f41bceccd75dc7a85a12103
|
||||
- filename: screens/Home/MyVcsTab.tsx
|
||||
checksum: 68ff83c5d9062fbc077d008956fa654540253c52ce68d7105c175c51562b3dc9
|
||||
- filename: components/VC/common/VcStatustooTip.tsx
|
||||
checksum: a48c88da719fadcb3d1aeb730a23735709c0c198351104203abd03445f6cc76f
|
||||
- filename: screens/Settings/KeyManagementScreen.tsx
|
||||
checksum: 6871fad16ecb5f019f502b2f7f715bc2a7b646ee08026b321aa1f8ce071dccc1
|
||||
version: "1.0"
|
||||
|
||||
3
App.tsx
3
App.tsx
@@ -59,7 +59,8 @@ const AppLayoutWrapper: React.FC = () => {
|
||||
const authService = appService.children.get('auth');
|
||||
const isAppSetupComplete = useSelector(authService, selectAppSetupComplete);
|
||||
|
||||
const [isDeepLinkOverlayVisible, setDeepLinkOverlayVisible] = useState(isDeepLinkFlow);
|
||||
const [isDeepLinkOverlayVisible, setDeepLinkOverlayVisible] =
|
||||
useState(isDeepLinkFlow);
|
||||
|
||||
useEffect(() => {
|
||||
if (AppState.currentState === 'active') {
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { Column } from "../../ui";
|
||||
import { Theme } from "../../ui/styleUtils";
|
||||
import { Text } from "../../ui";
|
||||
import { VC_STATUS_KEYS } from "./VCUtils";
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {View} from 'react-native';
|
||||
import {Column} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {Text} from '../../ui';
|
||||
import {VC_STATUS_KEYS} from './VCUtils';
|
||||
|
||||
export const StatusTooltipContent = () => {
|
||||
const { t } = useTranslation('ViewVcModal');
|
||||
|
||||
return (
|
||||
<Column align="center" style={{marginTop:20}}>
|
||||
{VC_STATUS_KEYS.map(key => (
|
||||
<View key={key} style={{ marginBottom: 20 }}>
|
||||
<Text weight="semibold">{t(`statusToolTipContent.${key}.title`)}</Text>
|
||||
<Text
|
||||
weight="regular"
|
||||
style={[
|
||||
Theme.Styles.tooltipContentDescription,
|
||||
{ marginTop: 3 },
|
||||
]}>
|
||||
{t(`statusToolTipContent.${key}.description`)}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
const {t} = useTranslation('ViewVcModal');
|
||||
|
||||
return (
|
||||
<Column align="center" style={{marginTop: 20}}>
|
||||
{VC_STATUS_KEYS.map(key => (
|
||||
<View key={key} style={{marginBottom: 20}}>
|
||||
<Text weight="semibold">
|
||||
{t(`statusToolTipContent.${key}.title`)}
|
||||
</Text>
|
||||
<Text
|
||||
weight="regular"
|
||||
style={[Theme.Styles.tooltipContentDescription, {marginTop: 3}]}>
|
||||
{t(`statusToolTipContent.${key}.description`)}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import {Row, Text} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {RevocationStatus} from '../shared/vcVerifier/VcVerifier';
|
||||
|
||||
export const VCVerification: React.FC<VCVerificationProps> = ({
|
||||
vcMetadata,
|
||||
@@ -20,12 +21,15 @@ export const VCVerification: React.FC<VCVerificationProps> = ({
|
||||
let statusIcon: JSX.Element;
|
||||
|
||||
if (vcMetadata.isVerified) {
|
||||
if (vcMetadata.isRevoked) {
|
||||
if (vcMetadata.isRevoked === RevocationStatus.TRUE) {
|
||||
statusText = t('revoked');
|
||||
statusIcon = <PendingIcon color="brown" />;
|
||||
} else if (vcMetadata.isExpired) {
|
||||
statusText = t('expired');
|
||||
statusIcon = <PendingIcon color="red" />;
|
||||
} else if (vcMetadata.isRevoked === RevocationStatus.UNDETERMINED) {
|
||||
statusText = t('pending');
|
||||
statusIcon = <PendingIcon color="orange" />;
|
||||
} else {
|
||||
statusText = t('valid');
|
||||
statusIcon = <VerifiedIcon />;
|
||||
|
||||
@@ -69,7 +69,10 @@ final class LdpStatusChecker {
|
||||
var results: [String: CredentialStatusResult] = [:]
|
||||
|
||||
for entry in filteredEntries {
|
||||
let purpose = (entry["statusPurpose"] as? String)?.lowercased() ?? ""
|
||||
guard let purpose = (entry["statusPurpose"] as? String)?.lowercased(), !purpose.isEmpty else {
|
||||
print("Warning: Skipping entry with missing statusPurpose")
|
||||
continue
|
||||
}
|
||||
do {
|
||||
let result = try await checkStatusEntry(entry: entry, purpose: purpose)
|
||||
results[purpose] = result
|
||||
@@ -175,7 +178,7 @@ final class LdpStatusChecker {
|
||||
throw StatusCheckException(message: "statusMessage count mismatch", errorCode: .statusVerificationError)
|
||||
}
|
||||
|
||||
print("Status message for purpose '\(purpose): $\(statusMessage)")
|
||||
print("Status message for purpose '\(purpose): \(statusMessage)")
|
||||
}
|
||||
|
||||
let bitSet = try decodeEncodedList(encodedList)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import {assign, send} from 'xstate';
|
||||
import {CommunicationDetails, UUID, VerificationStatus} from '../../../shared/Utils';
|
||||
import {
|
||||
CommunicationDetails,
|
||||
UUID,
|
||||
VerificationStatus,
|
||||
} from '../../../shared/Utils';
|
||||
import {StoreEvents} from '../../store';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {
|
||||
@@ -31,6 +35,7 @@ import {VcMetaEvents} from '../VCMetaMachine/VCMetaMachine';
|
||||
import {WalletBindingResponse} from '../VCMetaMachine/vc';
|
||||
import {BannerStatusType} from '../../../components/BannerNotification';
|
||||
import {VCActivityLog} from '../../../components/ActivityLogEvent';
|
||||
import {RevocationStatus} from '../../../shared/vcVerifier/VcVerifier';
|
||||
|
||||
export const VCItemActions = model => {
|
||||
return {
|
||||
@@ -58,7 +63,7 @@ export const VCItemActions = model => {
|
||||
resetIsVerified: assign((context: any) => {
|
||||
const previous = context.vcMetadata;
|
||||
const statusChanged = previous.isVerified;
|
||||
|
||||
|
||||
return {
|
||||
...context,
|
||||
vcMetadata: new VCMetadata({
|
||||
@@ -91,16 +96,21 @@ export const VCItemActions = model => {
|
||||
|
||||
sendReverificationSuccessToVcMeta: send(
|
||||
(context: any) => ({
|
||||
type: 'REVERIFY_VC_SUCCESS',
|
||||
statusValue: context.vcMetadata.isRevoked
|
||||
? VerificationStatus.REVOKED
|
||||
: context.vcMetadata.isExpired
|
||||
? VerificationStatus.EXPIRED
|
||||
: context.vcMetadata.isVerified
|
||||
? VerificationStatus.VALID
|
||||
: VerificationStatus.PENDING,
|
||||
type:
|
||||
context.vcMetadata.isRevoked === RevocationStatus.UNDETERMINED
|
||||
? 'REVERIFY_VC_FAILED'
|
||||
: 'REVERIFY_VC_SUCCESS',
|
||||
statusValue:
|
||||
context.vcMetadata.isRevoked === RevocationStatus.TRUE
|
||||
? VerificationStatus.REVOKED
|
||||
: context.vcMetadata.isExpired
|
||||
? VerificationStatus.EXPIRED
|
||||
: context.vcMetadata.isVerified &&
|
||||
context.vcMetadata.isRevoked === RevocationStatus.FALSE
|
||||
? VerificationStatus.VALID
|
||||
: VerificationStatus.PENDING,
|
||||
vcKey: context.vcMetadata.getVcKey(),
|
||||
vcType: context.vcMetadata.credentialType
|
||||
vcType: context.vcMetadata.credentialType,
|
||||
}),
|
||||
{
|
||||
to: (context: any) => context.serviceRefs.vcMeta,
|
||||
@@ -110,13 +120,13 @@ export const VCItemActions = model => {
|
||||
resetStatusChangedFlag: assign({
|
||||
statusChangedDuringVerification: () => false,
|
||||
}),
|
||||
|
||||
|
||||
sendReverificationFailureToVcMeta: send(
|
||||
(context:any) => ({
|
||||
(context: any) => ({
|
||||
type: 'REVERIFY_VC_FAILED',
|
||||
statusValue: VerificationStatus.PENDING,
|
||||
vcKey: context.vcMetadata.getVcKey(),
|
||||
vcType: context.vcMetadata.credentialType
|
||||
vcType: context.vcMetadata.credentialType,
|
||||
}),
|
||||
{
|
||||
to: (context: any) => context.serviceRefs.vcMeta,
|
||||
|
||||
@@ -30,8 +30,8 @@ export const baseRoutes: Screen[] = [
|
||||
component: KeyManagementScreen,
|
||||
},
|
||||
{
|
||||
name:'AuthView',
|
||||
component:AuthWebViewScreen
|
||||
name: 'AuthView',
|
||||
component: AuthWebViewScreen,
|
||||
},
|
||||
{
|
||||
name: 'Language',
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import {authRoutes, baseRoutes} from '../routes';
|
||||
import {useAppLayout} from './AppLayoutController';
|
||||
import {StatusBar} from 'react-native';
|
||||
import {GestureHandlerRootView} from "react-native-gesture-handler";
|
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler';
|
||||
|
||||
const {Navigator, Screen} = createNativeStackNavigator();
|
||||
export const AppLayout: React.FC = () => {
|
||||
@@ -25,17 +25,19 @@ export const AppLayout: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView>
|
||||
<NavigationContainer ref={navigationRef}>
|
||||
<StatusBar animated={true} barStyle="dark-content" />
|
||||
<Navigator initialRouteName={baseRoutes[0].name} screenOptions={options}>
|
||||
{baseRoutes.map(route => (
|
||||
<Screen key={route.name} {...route} />
|
||||
))}
|
||||
{controller.isAuthorized &&
|
||||
authRoutes.map(route => <Screen key={route.name} {...route} />)}
|
||||
</Navigator>
|
||||
</NavigationContainer>
|
||||
</GestureHandlerRootView>
|
||||
<GestureHandlerRootView>
|
||||
<NavigationContainer ref={navigationRef}>
|
||||
<StatusBar animated={true} barStyle="dark-content" />
|
||||
<Navigator
|
||||
initialRouteName={baseRoutes[0].name}
|
||||
screenOptions={options}>
|
||||
{baseRoutes.map(route => (
|
||||
<Screen key={route.name} {...route} />
|
||||
))}
|
||||
{controller.isAuthorized &&
|
||||
authRoutes.map(route => <Screen key={route.name} {...route} />)}
|
||||
</Navigator>
|
||||
</NavigationContainer>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -41,13 +41,13 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = props => {
|
||||
const renderItem = ({item}) => {
|
||||
return (
|
||||
<View style={Theme.OnboardingOverlayStyles.slide}>
|
||||
<ScrollView showsVerticalScrollIndicator={true}>
|
||||
<Text style={Theme.OnboardingOverlayStyles.sliderTitle}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text style={Theme.OnboardingOverlayStyles.text}>{item.text}</Text>
|
||||
{item.footer}
|
||||
</ScrollView>
|
||||
<ScrollView showsVerticalScrollIndicator={true}>
|
||||
<Text style={Theme.OnboardingOverlayStyles.sliderTitle}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text style={Theme.OnboardingOverlayStyles.text}>{item.text}</Text>
|
||||
{item.footer}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -40,7 +40,11 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
const controller = useViewVcModal(props);
|
||||
const profileImage = controller.verifiableCredentialData.face;
|
||||
const verificationStatus = controller.verificationStatus;
|
||||
const verificationStatusMessage = controller.verificationStatus?.isRevoked ? "revoked" : controller.verificationStatus?.isExpired ? "expired" : controller.verificationStatus?.statusType;
|
||||
const verificationStatusMessage = controller.verificationStatus?.isRevoked
|
||||
? 'revoked'
|
||||
: controller.verificationStatus?.isExpired
|
||||
? 'expired'
|
||||
: controller.verificationStatus?.statusType;
|
||||
const [verifiableCredential, setVerifiableCredential] = useState(null);
|
||||
const [svgTemplate, setSvgTemplate] = useState<string[] | null>(null);
|
||||
const [svgRendererError, setSvgRendererError] = useState<string[] | null>(
|
||||
@@ -69,7 +73,7 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
!controller.verifiableCredentialData.vcMetadata.isVerified &&
|
||||
!controller.isVerificationInProgress
|
||||
) {
|
||||
props.vcItemActor.send({ type: 'VERIFY' });
|
||||
props.vcItemActor.send({type: 'VERIFY'});
|
||||
}
|
||||
}, [controller.verifiableCredentialData.vcMetadata.isVerified]);
|
||||
|
||||
@@ -86,11 +90,12 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
setLoadingSvg(true);
|
||||
|
||||
const vcJsonString = JSON.stringify(controller.credential.credential);
|
||||
const result = await VcRenderer.getInstance().generateCredentialDisplayContent(
|
||||
controller.verifiableCredentialData.format,
|
||||
wellknown ?? null,
|
||||
vcJsonString,
|
||||
);
|
||||
const result =
|
||||
await VcRenderer.getInstance().generateCredentialDisplayContent(
|
||||
controller.verifiableCredentialData.format,
|
||||
wellknown ?? null,
|
||||
vcJsonString,
|
||||
);
|
||||
|
||||
setSvgTemplate(result);
|
||||
setSvgRendererError(null);
|
||||
@@ -181,7 +186,9 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
<BannerNotification
|
||||
type={verificationStatus?.statusType as BannerStatus}
|
||||
message={t(`VcVerificationBanner:${verificationStatusMessage}`, {
|
||||
vcDetails: `${verificationStatus?.vcType} ${verificationStatus?.vcNumber ?? ""}`,
|
||||
vcDetails: `${verificationStatus?.vcType} ${
|
||||
verificationStatus?.vcNumber ?? ''
|
||||
}`,
|
||||
})}
|
||||
onClosePress={controller.RESET_VERIFICATION_STATUS}
|
||||
key={'reVerificationInProgress'}
|
||||
@@ -239,7 +246,9 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isWalletBindingInProgress || controller.isReverifyingVc}
|
||||
isVisible={
|
||||
controller.isWalletBindingInProgress || controller.isReverifyingVc
|
||||
}
|
||||
title={t('inProgress')}
|
||||
progress
|
||||
/>
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FlatList, Pressable, View } from 'react-native';
|
||||
import { Issuer } from '../../components/openId4VCI/Issuer';
|
||||
import { Error } from '../../components/ui/Error';
|
||||
import { Header } from '../../components/ui/Header';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../../routes';
|
||||
import { HomeRouteProps } from '../../routes/routeTypes';
|
||||
import { useIssuerScreenController } from './IssuerScreenController';
|
||||
import { Loader } from '../../components/ui/Loader';
|
||||
import React, {useEffect, useLayoutEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {FlatList, Pressable, View} from 'react-native';
|
||||
import {Issuer} from '../../components/openId4VCI/Issuer';
|
||||
import {Error} from '../../components/ui/Error';
|
||||
import {Header} from '../../components/ui/Header';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {RootRouteProps} from '../../routes';
|
||||
import {HomeRouteProps} from '../../routes/routeTypes';
|
||||
import {useIssuerScreenController} from './IssuerScreenController';
|
||||
import {Loader} from '../../components/ui/Loader';
|
||||
import ScanIcon from '../../assets/scanIcon.svg';
|
||||
import {
|
||||
isTranslationKeyFound,
|
||||
removeWhiteSpace,
|
||||
} from '../../shared/commonUtil';
|
||||
import {isTranslationKeyFound, removeWhiteSpace} from '../../shared/commonUtil';
|
||||
import {
|
||||
ErrorMessage,
|
||||
getDisplayObjectForCurrentLanguage,
|
||||
@@ -26,31 +23,31 @@ import {
|
||||
sendInteractEvent,
|
||||
sendStartEvent,
|
||||
} from '../../shared/telemetry/TelemetryUtils';
|
||||
import { TelemetryConstants } from '../../shared/telemetry/TelemetryConstants';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { SearchBar } from '../../components/ui/SearchBar';
|
||||
import { SvgImage } from '../../components/ui/svg';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { BannerNotificationContainer } from '../../components/BannerNotificationContainer';
|
||||
import { CredentialTypeSelectionScreen } from './CredentialTypeSelectionScreen';
|
||||
import { QrScanner } from '../../components/QrScanner';
|
||||
import { IssuersModel } from '../../machines/Issuers/IssuersModel';
|
||||
import { AUTH_ROUTES } from '../../routes/routesConstants';
|
||||
import { TransactionCodeModal } from './TransactionCodeScreen';
|
||||
import { TrustModal } from '../../components/TrustModal';
|
||||
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {MessageOverlay} from '../../components/MessageOverlay';
|
||||
import {SearchBar} from '../../components/ui/SearchBar';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {BannerNotificationContainer} from '../../components/BannerNotificationContainer';
|
||||
import {CredentialTypeSelectionScreen} from './CredentialTypeSelectionScreen';
|
||||
import {QrScanner} from '../../components/QrScanner';
|
||||
import {IssuersModel} from '../../machines/Issuers/IssuersModel';
|
||||
import {AUTH_ROUTES} from '../../routes/routesConstants';
|
||||
import {TransactionCodeModal} from './TransactionCodeScreen';
|
||||
import {TrustModal} from '../../components/TrustModal';
|
||||
import i18next from 'i18next';
|
||||
export const IssuersScreen: React.FC<
|
||||
HomeRouteProps | RootRouteProps
|
||||
> = props => {
|
||||
const model = IssuersModel;
|
||||
const controller = useIssuerScreenController(props);
|
||||
const { i18n, t } = useTranslation('IssuersScreen');
|
||||
const {i18n, t} = useTranslation('IssuersScreen');
|
||||
const issuers = controller.issuers;
|
||||
let [filteredSearchData, setFilteredSearchData] = useState(issuers);
|
||||
const [search, setSearch] = useState('');
|
||||
const [tapToSearch, setTapToSearch] = useState(false);
|
||||
const [clearSearchIcon, setClearSearchIcon] = useState(false);
|
||||
const showFullScreenError = controller.isError
|
||||
const showFullScreenError = controller.isError;
|
||||
|
||||
const isVerificationFailed = controller.verificationErrorMessage !== '';
|
||||
|
||||
@@ -58,8 +55,7 @@ export const IssuersScreen: React.FC<
|
||||
|
||||
const verificationErrorMessage = isTranslationKeyFound(translationKey, t)
|
||||
? t(translationKey)
|
||||
: t('errors.verificationFailed.ERR_GENERIC');
|
||||
|
||||
: t('errors.verificationFailed.ERR_GENERIC');
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (controller.loadingReason || showFullScreenError) {
|
||||
@@ -72,13 +68,12 @@ export const IssuersScreen: React.FC<
|
||||
header: props => (
|
||||
<Header
|
||||
goBack={props.navigation.goBack}
|
||||
title={ controller.isQrScanning?t('download'):t('title')}
|
||||
title={controller.isQrScanning ? t('download') : t('title')}
|
||||
testID="issuersScreenHeader"
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
}, [
|
||||
controller.loadingReason,
|
||||
controller.errorMessageType,
|
||||
@@ -93,8 +88,10 @@ export const IssuersScreen: React.FC<
|
||||
if (controller.isAuthEndpointToOpen) {
|
||||
(props.navigation as any).navigate(AUTH_ROUTES.AuthView, {
|
||||
authorizationURL: controller.authEndpount,
|
||||
clientId: controller.selectedIssuer.client_id ?? "wallet",
|
||||
redirectUri: controller.selectedIssuer.redirect_uri ?? "io.mosip.residentapp.inji://oauthredirect",
|
||||
clientId: controller.selectedIssuer.client_id ?? 'wallet',
|
||||
redirectUri:
|
||||
controller.selectedIssuer.redirect_uri ??
|
||||
'io.mosip.residentapp.inji://oauthredirect',
|
||||
controller: controller,
|
||||
});
|
||||
}
|
||||
@@ -102,7 +99,7 @@ export const IssuersScreen: React.FC<
|
||||
|
||||
const onPressHandler = (id: string, protocol: string) => {
|
||||
sendStartEvent(
|
||||
getStartEventData(TelemetryConstants.FlowType.vcDownload, { id: id }),
|
||||
getStartEventData(TelemetryConstants.FlowType.vcDownload, {id: id}),
|
||||
);
|
||||
sendInteractEvent(
|
||||
getInteractEventData(
|
||||
@@ -124,9 +121,9 @@ export const IssuersScreen: React.FC<
|
||||
return (
|
||||
controller.errorMessageType === ErrorMessage.TECHNICAL_DIFFICULTIES ||
|
||||
controller.errorMessageType ===
|
||||
ErrorMessage.CREDENTIAL_TYPE_DOWNLOAD_FAILURE ||
|
||||
ErrorMessage.CREDENTIAL_TYPE_DOWNLOAD_FAILURE ||
|
||||
controller.errorMessageType ===
|
||||
ErrorMessage.AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED ||
|
||||
ErrorMessage.AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED ||
|
||||
controller.errorMessageType === ErrorMessage.NETWORK_REQUEST_FAILED
|
||||
);
|
||||
}
|
||||
@@ -195,7 +192,7 @@ export const IssuersScreen: React.FC<
|
||||
primaryButtonText="goBack"
|
||||
primaryButtonEvent={controller.RESET_VERIFY_ERROR}
|
||||
primaryButtonTestID="goBack"
|
||||
customStyles={{ marginTop: '30%' }}
|
||||
customStyles={{marginTop: '30%'}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -203,14 +200,16 @@ export const IssuersScreen: React.FC<
|
||||
return issuerTrustConsentComponent();
|
||||
}
|
||||
if (controller.isTxCodeRequested) {
|
||||
return <TransactionCodeModal
|
||||
visible={controller.isTxCodeRequested}
|
||||
onDismiss={controller.CANCEL}
|
||||
onVerify={controller.TX_CODE_RECEIVED}
|
||||
inputMode= {controller.txCodeDisplayDetails.inputMode}
|
||||
description={controller.txCodeDisplayDetails.description}
|
||||
length={controller.txCodeDisplayDetails.length}
|
||||
/>
|
||||
return (
|
||||
<TransactionCodeModal
|
||||
visible={controller.isTxCodeRequested}
|
||||
onDismiss={controller.CANCEL}
|
||||
onVerify={controller.TX_CODE_RECEIVED}
|
||||
inputMode={controller.txCodeDisplayDetails.inputMode}
|
||||
description={controller.txCodeDisplayDetails.description}
|
||||
length={controller.txCodeDisplayDetails.length}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (controller.isBiometricsCancelled) {
|
||||
@@ -253,7 +252,7 @@ export const IssuersScreen: React.FC<
|
||||
primaryButtonTestID="tryAgain"
|
||||
primaryButtonText={
|
||||
controller.errorMessageType != ErrorMessage.TECHNICAL_DIFFICULTIES &&
|
||||
controller.errorMessageType !=
|
||||
controller.errorMessageType !=
|
||||
ErrorMessage.AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED
|
||||
? 'tryAgain'
|
||||
: undefined
|
||||
@@ -272,7 +271,6 @@ export const IssuersScreen: React.FC<
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (controller.isQrScanning) {
|
||||
return qrScannerComponent();
|
||||
@@ -280,15 +278,21 @@ export const IssuersScreen: React.FC<
|
||||
function qrScannerComponent() {
|
||||
return (
|
||||
<Column crossAlign="center">
|
||||
<QrScanner
|
||||
onQrFound={controller.QR_CODE_SCANNED}
|
||||
/>
|
||||
<QrScanner onQrFound={controller.QR_CODE_SCANNED} />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function issuerTrustConsentComponent() {
|
||||
return <TrustModal isVisible={true} logo={controller.issuerLogo} name={controller.issuerName} onConfirm={controller.ON_CONSENT_GIVEN} onCancel={controller.CANCEL} />
|
||||
return (
|
||||
<TrustModal
|
||||
isVisible={true}
|
||||
logo={controller.issuerLogo}
|
||||
name={controller.issuerName}
|
||||
onConfirm={controller.ON_CONSENT_GIVEN}
|
||||
onCancel={controller.CANCEL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -333,19 +337,26 @@ export const IssuersScreen: React.FC<
|
||||
}}>
|
||||
{t('description')}
|
||||
</Text>
|
||||
{search === '' && <View style={{ height: 85 }}><Issuer defaultLogo={ScanIcon} displayDetails={{
|
||||
title: t('offerTitle'),
|
||||
locale: i18n.language,
|
||||
description: t('offerDescription'),
|
||||
}} onPress={
|
||||
controller.SCAN_CREDENTIAL_OFFER_QR_CODE
|
||||
} testID={'credentalOfferButton'} /></View>}
|
||||
{search === '' && (
|
||||
<View style={{height: 85}}>
|
||||
<Issuer
|
||||
defaultLogo={ScanIcon}
|
||||
displayDetails={{
|
||||
title: t('offerTitle'),
|
||||
locale: i18n.language,
|
||||
description: t('offerDescription'),
|
||||
}}
|
||||
onPress={controller.SCAN_CREDENTIAL_OFFER_QR_CODE}
|
||||
testID={'credentalOfferButton'}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Column scroll style={Theme.IssuersScreenStyles.issuersContainer}>
|
||||
{controller.issuers.length > 0 && (
|
||||
<FlatList
|
||||
data={filteredSearchData}
|
||||
renderItem={({ item }) => (
|
||||
renderItem={({item}) => (
|
||||
<Issuer
|
||||
testID={removeWhiteSpace(item.issuer_id)}
|
||||
key={item.issuer_id}
|
||||
@@ -368,5 +379,3 @@ export const IssuersScreen: React.FC<
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -53,11 +53,12 @@ export const ReceiveVcScreen: React.FC = () => {
|
||||
setLoadingSvg(true);
|
||||
|
||||
const vcJsonString = JSON.stringify(controller.credential.credential);
|
||||
const result = await VcRenderer.getInstance().generateCredentialDisplayContent(
|
||||
verifiableCredentialData.vcMetadata.format,
|
||||
wellknown ?? null,
|
||||
vcJsonString,
|
||||
);
|
||||
const result =
|
||||
await VcRenderer.getInstance().generateCredentialDisplayContent(
|
||||
verifiableCredentialData.vcMetadata.format,
|
||||
wellknown ?? null,
|
||||
vcJsonString,
|
||||
);
|
||||
|
||||
setSvgTemplate(result);
|
||||
setSvgRendererError(null);
|
||||
|
||||
@@ -48,7 +48,7 @@ import {VPShareOverlayProps} from './VPShareOverlay';
|
||||
import {ActivityLogEvents} from '../../machines/activityLog';
|
||||
import {VPShareActivityLog} from '../../components/VPShareActivityLogEvent';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
import { getFaceAttribute } from '../../components/VC/common/VCUtils';
|
||||
import {getFaceAttribute} from '../../components/VC/common/VCUtils';
|
||||
|
||||
type MyVcsTabNavigation = NavigationProp<RootRouteProps>;
|
||||
|
||||
@@ -65,9 +65,10 @@ export function useSendVPScreen() {
|
||||
const navigation = useNavigation<MyVcsTabNavigation>();
|
||||
const openID4VPService = scanService.getSnapshot().context.OpenId4VPRef;
|
||||
// input descriptor id to VCs mapping
|
||||
const [inputDescriptorIdToSelectedVcKeys, setInputDescriptorIdToSelectedVcKeys] = useState<Record<string, [string]>>(
|
||||
{},
|
||||
);
|
||||
const [
|
||||
inputDescriptorIdToSelectedVcKeys,
|
||||
setInputDescriptorIdToSelectedVcKeys,
|
||||
] = useState<Record<string, [string]>>({});
|
||||
|
||||
const hasLoggedErrorRef = useRef(false);
|
||||
|
||||
@@ -97,26 +98,31 @@ export function useSendVPScreen() {
|
||||
return Object.values(vcs)
|
||||
.flatMap(vc => vc)
|
||||
.some(vc => {
|
||||
return getFaceAttribute(vc.verifiableCredential,vc.format) != null;
|
||||
return getFaceAttribute(vc.verifiableCredential, vc.format) != null;
|
||||
});
|
||||
};
|
||||
|
||||
const checkIfAllVCsHasImage = vcs => {
|
||||
return Object.values(vcs)
|
||||
.flatMap(vc => vc)
|
||||
.every(vc => getFaceAttribute(vc.verifiableCredential,vc.format) != null);
|
||||
.every(
|
||||
vc => getFaceAttribute(vc.verifiableCredential, vc.format) != null,
|
||||
);
|
||||
};
|
||||
|
||||
const getSelectedVCs = (): Record<string, any[]> => {
|
||||
let selectedVcsData: Record<string, any[]> = {}; // input_descriptor_id to VC[]
|
||||
Object.entries(inputDescriptorIdToSelectedVcKeys).forEach(([inputDescriptorId, vcKeys]) => {
|
||||
vcKeys.forEach((vcKey : string) => {
|
||||
const vcData = myVcs[vcKey];
|
||||
selectedVcsData[inputDescriptorId] = selectedVcsData[inputDescriptorId] || [];
|
||||
selectedVcsData[inputDescriptorId].push(vcData);
|
||||
Object.entries(inputDescriptorIdToSelectedVcKeys).forEach(
|
||||
([inputDescriptorId, vcKeys]) => {
|
||||
vcKeys.forEach((vcKey: string) => {
|
||||
const vcData = myVcs[vcKey];
|
||||
selectedVcsData[inputDescriptorId] =
|
||||
selectedVcsData[inputDescriptorId] || [];
|
||||
selectedVcsData[inputDescriptorId].push(vcData);
|
||||
});
|
||||
});
|
||||
return selectedVcsData
|
||||
},
|
||||
);
|
||||
return selectedVcsData;
|
||||
};
|
||||
|
||||
const showConfirmationPopup = useSelector(
|
||||
@@ -222,9 +228,18 @@ export function useSendVPScreen() {
|
||||
showLoadingScreen: useSelector(openID4VPService, selectIsShowLoadingScreen),
|
||||
vpVerifierName,
|
||||
flowType: useSelector(openID4VPService, selectFlowType),
|
||||
showTrustConsentModal: useSelector(openID4VPService,selectshowTrustConsentModal),
|
||||
verifierNameInTrustModal: useSelector(openID4VPService, selectVerifierNameInTrustModal),
|
||||
verifierLogoInTrustModal: useSelector(openID4VPService, selectVerifierLogoInTrustModal),
|
||||
showTrustConsentModal: useSelector(
|
||||
openID4VPService,
|
||||
selectshowTrustConsentModal,
|
||||
),
|
||||
verifierNameInTrustModal: useSelector(
|
||||
openID4VPService,
|
||||
selectVerifierNameInTrustModal,
|
||||
),
|
||||
verifierLogoInTrustModal: useSelector(
|
||||
openID4VPService,
|
||||
selectVerifierLogoInTrustModal,
|
||||
),
|
||||
showConfirmationPopup,
|
||||
isSelectingVCs,
|
||||
checkIfAnyVCHasImage,
|
||||
@@ -282,60 +297,80 @@ export function useSendVPScreen() {
|
||||
(vcRef: ActorRefFrom<typeof VCItemMachine>) => {
|
||||
let descriptorMappingToVCs = {...inputDescriptorIdToSelectedVcKeys};
|
||||
|
||||
const isVCSelected = Object.keys(inputDescriptorIdToSelectedVcKeys)?.includes(inputDescriptorId) && inputDescriptorIdToSelectedVcKeys[inputDescriptorId]?.includes(vcKey) ? false : true;
|
||||
const isVCSelected =
|
||||
Object.keys(inputDescriptorIdToSelectedVcKeys)?.includes(
|
||||
inputDescriptorId,
|
||||
) &&
|
||||
inputDescriptorIdToSelectedVcKeys[inputDescriptorId]?.includes(vcKey)
|
||||
? false
|
||||
: true;
|
||||
if (isVCSelected) {
|
||||
if (descriptorMappingToVCs[inputDescriptorId]) {
|
||||
if (!descriptorMappingToVCs[inputDescriptorId].includes(vcKey)) {
|
||||
descriptorMappingToVCs[inputDescriptorId].push(vcKey);
|
||||
}
|
||||
} else {
|
||||
descriptorMappingToVCs[inputDescriptorId] = [vcKey];
|
||||
if (descriptorMappingToVCs[inputDescriptorId]) {
|
||||
if (!descriptorMappingToVCs[inputDescriptorId].includes(vcKey)) {
|
||||
descriptorMappingToVCs[inputDescriptorId].push(vcKey);
|
||||
}
|
||||
} else {
|
||||
descriptorMappingToVCs[inputDescriptorId] = [vcKey];
|
||||
}
|
||||
} else {
|
||||
// remove vc key from the input descriptor mapping
|
||||
if (descriptorMappingToVCs[inputDescriptorId]) {
|
||||
descriptorMappingToVCs[inputDescriptorId] = descriptorMappingToVCs[
|
||||
inputDescriptorId
|
||||
].filter(key => key !== vcKey); // remove the vcKey from the array
|
||||
if (descriptorMappingToVCs[inputDescriptorId].length === 0) { // if the array is empty, remove the input descriptor id
|
||||
delete descriptorMappingToVCs[inputDescriptorId];
|
||||
}
|
||||
if (descriptorMappingToVCs[inputDescriptorId]) {
|
||||
descriptorMappingToVCs[inputDescriptorId] = descriptorMappingToVCs[
|
||||
inputDescriptorId
|
||||
].filter(key => key !== vcKey); // remove the vcKey from the array
|
||||
if (descriptorMappingToVCs[inputDescriptorId].length === 0) {
|
||||
// if the array is empty, remove the input descriptor id
|
||||
delete descriptorMappingToVCs[inputDescriptorId];
|
||||
}
|
||||
}
|
||||
}
|
||||
setInputDescriptorIdToSelectedVcKeys(descriptorMappingToVCs)
|
||||
setInputDescriptorIdToSelectedVcKeys(descriptorMappingToVCs);
|
||||
const {serviceRefs, wellknownResponse, ...vcData} =
|
||||
vcRef.getSnapshot().context;
|
||||
},
|
||||
|
||||
UNCHECK_ALL: () => {
|
||||
setInputDescriptorIdToSelectedVcKeys({})
|
||||
setInputDescriptorIdToSelectedVcKeys({});
|
||||
},
|
||||
|
||||
CHECK_ALL: () => {
|
||||
const updatedInputDescriptorToCredentialsMapping: Record<string, any[]> = {};
|
||||
const updatedInputDescriptorToCredentialsMapping: Record<string, any[]> =
|
||||
{};
|
||||
Object.entries(vcsMatchingAuthRequest).map(([inputDescriptorId, vcs]) => {
|
||||
updatedInputDescriptorToCredentialsMapping[inputDescriptorId] = [];
|
||||
vcs.map(vcData => {
|
||||
const vcKey = VCMetadata.fromVcMetadataString(
|
||||
vcData.vcMetadata,
|
||||
).getVcKey();
|
||||
updatedInputDescriptorToCredentialsMapping[inputDescriptorId].push(vcKey);
|
||||
updatedInputDescriptorToCredentialsMapping[inputDescriptorId].push(
|
||||
vcKey,
|
||||
);
|
||||
});
|
||||
});
|
||||
setInputDescriptorIdToSelectedVcKeys({...updatedInputDescriptorToCredentialsMapping});
|
||||
setInputDescriptorIdToSelectedVcKeys({
|
||||
...updatedInputDescriptorToCredentialsMapping,
|
||||
});
|
||||
},
|
||||
|
||||
ACCEPT_REQUEST: (selectedDisclosuresByVc) => {
|
||||
openID4VPService.send(OpenID4VPEvents.ACCEPT_REQUEST(getSelectedVCs(), selectedDisclosuresByVc));
|
||||
ACCEPT_REQUEST: selectedDisclosuresByVc => {
|
||||
openID4VPService.send(
|
||||
OpenID4VPEvents.ACCEPT_REQUEST(
|
||||
getSelectedVCs(),
|
||||
selectedDisclosuresByVc,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
VERIFIER_TRUST_CONSENT_GIVEN: () =>{
|
||||
VERIFIER_TRUST_CONSENT_GIVEN: () => {
|
||||
openID4VPService.send(OpenID4VPEvents.VERIFIER_TRUST_CONSENT_GIVEN());
|
||||
},
|
||||
|
||||
VERIFY_AND_ACCEPT_REQUEST: (selectedDisclosuresByVc) => {
|
||||
VERIFY_AND_ACCEPT_REQUEST: selectedDisclosuresByVc => {
|
||||
openID4VPService.send(
|
||||
OpenID4VPEvents.VERIFY_AND_ACCEPT_REQUEST(getSelectedVCs(), selectedDisclosuresByVc),
|
||||
OpenID4VPEvents.VERIFY_AND_ACCEPT_REQUEST(
|
||||
getSelectedVCs(),
|
||||
selectedDisclosuresByVc,
|
||||
),
|
||||
);
|
||||
},
|
||||
CANCEL,
|
||||
|
||||
@@ -21,7 +21,7 @@ import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {SUPPORTED_KEY_TYPES} from '../../shared/constants';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { HelpIcon } from '../../components/ui/HelpIcon';
|
||||
import {HelpIcon} from '../../components/ui/HelpIcon';
|
||||
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
|
||||
@@ -129,10 +129,7 @@ export const KeyManagementScreen: React.FC<KeyManagementScreenProps> = () => {
|
||||
style={Theme.KeyManagementScreenStyle.heading}>
|
||||
{t('header')}
|
||||
</Text>
|
||||
<HelpScreen
|
||||
source={'keyManagement'}
|
||||
triggerComponent={ HelpIcon() }
|
||||
/>
|
||||
<HelpScreen source={'keyManagement'} triggerComponent={HelpIcon()} />
|
||||
</View>
|
||||
<BannerNotificationContainer />
|
||||
<View style={Theme.KeyManagementScreenStyle.copilotViewStyle}>
|
||||
|
||||
@@ -13,10 +13,7 @@ import {ProfileInfo} from '../../shared/CloudBackupAndRestoreUtils';
|
||||
import {useBackupScreen} from './BackupController';
|
||||
import {BannerNotificationContainer} from '../../components/BannerNotificationContainer';
|
||||
import {useBackupRestoreScreen} from '../Settings/BackupRestoreController';
|
||||
import {
|
||||
getAccountType,
|
||||
getDriveName,
|
||||
} from '../../shared/commonUtil';
|
||||
import {getAccountType, getDriveName} from '../../shared/commonUtil';
|
||||
import {HelpScreen} from '../../components/HelpScreen';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
import {HelpIcon} from '../../components/ui/HelpIcon';
|
||||
@@ -222,11 +219,11 @@ const BackupAndRestoreScreen: React.FC<BackupAndRestoreProps> = props => {
|
||||
<LoaderAnimation testID="backupAndRestoreScreen" />
|
||||
</Column>
|
||||
) : (
|
||||
<ScrollView>
|
||||
{LastBackupSection}
|
||||
{AccountSection}
|
||||
{RestoreSection}
|
||||
</ScrollView>
|
||||
<ScrollView>
|
||||
{LastBackupSection}
|
||||
{AccountSection}
|
||||
{RestoreSection}
|
||||
</ScrollView>
|
||||
)}
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {getMosipIdentifier} from './commonUtil';
|
||||
import {VCFormat} from './VCFormat';
|
||||
import {isMosipVC, UUID} from './Utils';
|
||||
import {getCredentialType} from '../components/VC/common/VCUtils';
|
||||
import {RevocationStatus, RevocationStatusType} from './vcVerifier/VcVerifier';
|
||||
|
||||
const VC_KEY_PREFIX = 'VC';
|
||||
const VC_ITEM_STORE_KEY_REGEX = '^VC_[a-zA-Z0-9_-]+$';
|
||||
@@ -31,7 +32,7 @@ export class VCMetadata {
|
||||
mosipIndividualId: string = '';
|
||||
format: string = '';
|
||||
isExpired: boolean = false;
|
||||
isRevoked: boolean = false;
|
||||
isRevoked: RevocationStatusType = RevocationStatus.FALSE;
|
||||
|
||||
downloadKeyType: string = '';
|
||||
credentialType: string = '';
|
||||
@@ -51,7 +52,7 @@ export class VCMetadata {
|
||||
format = '',
|
||||
downloadKeyType = '',
|
||||
isExpired = false,
|
||||
isRevoked = false,
|
||||
isRevoked = RevocationStatus.FALSE,
|
||||
credentialType = '',
|
||||
issuerHost = '',
|
||||
lastKnownStatusTimestamp = '',
|
||||
@@ -150,7 +151,7 @@ export const getVCMetadata = (context: object, keyType: string) => {
|
||||
try {
|
||||
const url = new URL(issuerHost);
|
||||
return url.hostname.split('.')[0];
|
||||
}catch (error) {
|
||||
} catch (error) {
|
||||
// Fallback to issuerHost if URL parsing fails
|
||||
return issuerHost;
|
||||
}
|
||||
@@ -165,7 +166,7 @@ export const getVCMetadata = (context: object, keyType: string) => {
|
||||
isVerified: context.vcMetadata.isVerified ?? false,
|
||||
isExpired: context.vcMetadata.isExpired ?? false,
|
||||
isRevoked: context.vcMetadata.isRevoked ?? false,
|
||||
lastKnownStatusTimestamp:context.vcMetadata.lastKnownStatusTimestamp ?? '',
|
||||
lastKnownStatusTimestamp: context.vcMetadata.lastKnownStatusTimestamp ?? '',
|
||||
mosipIndividualId: getMosipIndividualId(
|
||||
context['verifiableCredential'] as VerifiableCredential,
|
||||
issuer,
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
import {NativeModules} from 'react-native';
|
||||
|
||||
export const RevocationStatus = Object.freeze({
|
||||
TRUE: 'TRUE',
|
||||
FALSE: 'FALSE',
|
||||
UNDETERMINED: 'UNDETERMINED',
|
||||
} as const);
|
||||
|
||||
/**
|
||||
* Type representing any possible value of RevocationStatus.
|
||||
*
|
||||
* - "TRUE" → Condition was evaluated and is positively true
|
||||
* - "FALSE" → Condition was evaluated and is definitively false
|
||||
* - "UNDETERMINED" → Condition could not be evaluated due to an error
|
||||
*/
|
||||
export type RevocationStatusType =
|
||||
(typeof RevocationStatus)[keyof typeof RevocationStatus];
|
||||
|
||||
export type CredentialStatusResult = {
|
||||
isValid: boolean;
|
||||
error?: ErrorResult;
|
||||
statusListVC?: string; // Available only in iOS
|
||||
statusListVC?: Record<string, any>; // Available only in iOS
|
||||
};
|
||||
|
||||
export type ErrorResult = {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type VerificationSummaryResult = {
|
||||
verificationStatus: boolean;
|
||||
@@ -39,8 +55,8 @@ class VCVerifier {
|
||||
): Promise<Record<string, CredentialStatusResult>> {
|
||||
try {
|
||||
return await this.vcVerifier.getCredentialStatus(
|
||||
JSON.stringify(credential),
|
||||
format,
|
||||
JSON.stringify(credential),
|
||||
format,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get credential status: ${error}`);
|
||||
@@ -53,9 +69,9 @@ class VCVerifier {
|
||||
): Promise<VerificationSummaryResult> {
|
||||
try {
|
||||
return await this.vcVerifier.getVerificationSummary(
|
||||
credentialString,
|
||||
credentialFormat,
|
||||
[],
|
||||
credentialString,
|
||||
credentialFormat,
|
||||
[],
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get verification summary: ${error}`);
|
||||
|
||||
@@ -4,14 +4,22 @@ import {RsaSignature2018} from '../../lib/jsonld-signatures/suites/rsa2018/RsaSi
|
||||
import {Ed25519Signature2018} from '../../lib/jsonld-signatures/suites/ed255192018/Ed25519Signature2018';
|
||||
import {AssertionProofPurpose} from '../../lib/jsonld-signatures/purposes/AssertionProofPurpose';
|
||||
import {PublicKeyProofPurpose} from '../../lib/jsonld-signatures/purposes/PublicKeyProofPurpose';
|
||||
import {Credential, VerifiableCredential,} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import {
|
||||
Credential,
|
||||
VerifiableCredential,
|
||||
} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import {getErrorEventData, sendErrorEvent} from '../telemetry/TelemetryUtils';
|
||||
import {TelemetryConstants} from '../telemetry/TelemetryConstants';
|
||||
import {getMosipIdentifier} from '../commonUtil';
|
||||
import {NativeModules} from 'react-native';
|
||||
import {isAndroid, isIOS} from '../constants';
|
||||
import {VCFormat} from '../VCFormat';
|
||||
import VCVerifier, {CredentialStatusResult, VerificationSummaryResult} from '../vcVerifier/VcVerifier';
|
||||
import VCVerifier, {
|
||||
CredentialStatusResult,
|
||||
RevocationStatus,
|
||||
RevocationStatusType,
|
||||
VerificationSummaryResult,
|
||||
} from '../vcVerifier/VcVerifier';
|
||||
|
||||
// FIXME: Ed25519Signature2018 not fully supported yet.
|
||||
// Ed25519Signature2018 proof type check is not tested with its real credential
|
||||
@@ -59,10 +67,11 @@ async function verifyCredentialForAndroid(
|
||||
typeof verifiableCredential === 'string'
|
||||
? verifiableCredential
|
||||
: JSON.stringify(verifiableCredential);
|
||||
const vcVerifierResult = await VCVerifier.getInstance().getVerificationSummary(
|
||||
credentialString,
|
||||
credentialFormat,
|
||||
);
|
||||
const vcVerifierResult =
|
||||
await VCVerifier.getInstance().getVerificationSummary(
|
||||
credentialString,
|
||||
credentialFormat,
|
||||
);
|
||||
return handleVcVerifierResponse(vcVerifierResult, verifiableCredential);
|
||||
}
|
||||
|
||||
@@ -81,12 +90,13 @@ async function verifyCredentialForIos(
|
||||
Since Digital Bazaar library is not able to verify ProofType: "Ed25519Signature2020",
|
||||
defaulting it to return true until VcVerifier is implemented for iOS.
|
||||
*/
|
||||
let verificationResponse: VerificationResult;
|
||||
let verificationResponse: VerificationResult;
|
||||
if (verifiableCredential.proof.type === ProofType.ED25519_2020) {
|
||||
verificationResponse = createSuccessfulVerificationResult();
|
||||
}
|
||||
else{
|
||||
const purpose = getPurposeFromProof(verifiableCredential.proof.proofPurpose);
|
||||
} else {
|
||||
const purpose = getPurposeFromProof(
|
||||
verifiableCredential.proof.proofPurpose,
|
||||
);
|
||||
const suite = selectVerificationSuite(verifiableCredential.proof);
|
||||
const vcjsOptions = {
|
||||
purpose,
|
||||
@@ -94,12 +104,11 @@ async function verifyCredentialForIos(
|
||||
credential: verifiableCredential,
|
||||
documentLoader: jsonld.documentLoaders.xhr(),
|
||||
};
|
||||
|
||||
|
||||
const result = await vcjs.verifyCredential(vcjsOptions);
|
||||
verificationResponse = handleResponse(result, verifiableCredential);
|
||||
}
|
||||
|
||||
|
||||
if (verificationResponse.isVerified) {
|
||||
const statusArray = await VCVerifier.getInstance().getCredentialStatus(
|
||||
verifiableCredential,
|
||||
@@ -188,7 +197,9 @@ async function handleVcVerifierResponse(
|
||||
verifiableCredential,
|
||||
);
|
||||
}
|
||||
const isRevoked = await checkIsStatusRevoked(verificationResult.credentialStatus)
|
||||
const isRevoked = await checkIsStatusRevoked(
|
||||
verificationResult.credentialStatus,
|
||||
);
|
||||
return {
|
||||
isVerified: verificationResult.verificationStatus,
|
||||
verificationMessage: verificationResult.verificationMessage,
|
||||
@@ -209,45 +220,51 @@ async function handleVcVerifierResponse(
|
||||
}
|
||||
}
|
||||
|
||||
const handleStatusListVCVerification = (status: CredentialStatusResult, type: "revoked" | "valid") => {
|
||||
const handleStatusListVCVerification = (
|
||||
status: CredentialStatusResult,
|
||||
type: 'revoked' | 'valid',
|
||||
) => {
|
||||
const isValid = verifyStatusListVC(status.statusListVC);
|
||||
if (!isValid) {
|
||||
throw new Error(
|
||||
`StatusListVC verification failed for ${type} entry ${status.error}`,
|
||||
`StatusListVC verification failed for ${type} entry ${status.error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function checkIsStatusRevoked(
|
||||
vcStatus: Record<string, CredentialStatusResult>,
|
||||
): Promise<boolean> {
|
||||
if (!Object.keys(vcStatus).length) return false;
|
||||
vcStatus: Record<string, CredentialStatusResult>,
|
||||
): Promise<RevocationStatusType> {
|
||||
if (!vcStatus || !Object.keys(vcStatus).length) return RevocationStatus.FALSE;
|
||||
|
||||
const revocationStatus = vcStatus["revocation"] as CredentialStatusResult;
|
||||
if (!revocationStatus) return false;
|
||||
const revocationStatus = vcStatus['revocation'] as CredentialStatusResult;
|
||||
if (!revocationStatus) return RevocationStatus.FALSE;
|
||||
|
||||
const {isValid, error} = revocationStatus;
|
||||
|
||||
if (isValid) {
|
||||
// Validate the valid statuses statusList VC for iOS
|
||||
if (isIOS()) {
|
||||
handleStatusListVCVerification(revocationStatus, "valid")
|
||||
handleStatusListVCVerification(revocationStatus, 'valid');
|
||||
}
|
||||
return false
|
||||
return RevocationStatus.FALSE;
|
||||
}
|
||||
|
||||
console.error(`Credential is revoked. Error: ${error?.code}, Message: ${error?.message}`);
|
||||
// if there is an error fetching revocation status itself, throw error (isValid = true, error = Error)
|
||||
if (error) {
|
||||
throw new Error(`Error fetching revocation status. Error: ${error.code}, Message: ${error.message}`);
|
||||
console.error(
|
||||
`Error fetching revocation status. Error: ${error.code}, Message: ${error.message}`,
|
||||
);
|
||||
return RevocationStatus.UNDETERMINED;
|
||||
}
|
||||
// There is no error fetching revocation status, but the status is invalid (isValid = false, error = undefined) - VC is revoked
|
||||
// Validate the valid statuses statusList VC for iOS
|
||||
if (isIOS()) {
|
||||
handleStatusListVCVerification(revocationStatus, "revoked");
|
||||
handleStatusListVCVerification(revocationStatus, 'revoked');
|
||||
}
|
||||
console.error(`Credential is revoked`);
|
||||
// If revocation status is invalid, the credential is revoked
|
||||
return true
|
||||
return RevocationStatus.TRUE;
|
||||
}
|
||||
|
||||
function createSuccessfulVerificationResult(): VerificationResult {
|
||||
@@ -298,12 +315,12 @@ export interface VerificationResult {
|
||||
isVerified: boolean;
|
||||
verificationMessage: string;
|
||||
verificationErrorCode: string;
|
||||
isRevoked?: boolean;
|
||||
isRevoked?: RevocationStatusType;
|
||||
}
|
||||
|
||||
//TODO: Implement status list VC verification for iOS.
|
||||
//Currently Digital Bazaar library does not support VC 2.0 status list VC verification.
|
||||
function verifyStatusListVC(statusListVC: string | undefined) {
|
||||
function verifyStatusListVC(statusListVC: Record<string, any> | undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user