[INJIMOB-1240] : Fix face auth consent for minified view share (#1397)

* [INJIMOB-1240] Resolve conflicts

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240] Refactor scan machine and QrLogin machine

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240] Refactor scan machine and QrLogin machine

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240] Refactor scan machine and QrLogin machine

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240]: Fix.Share with selfie pop up bug

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240]: Refactor. scanMachine and qrLoginMachine

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

* [INJIMOB-1240]: Refactor. scanMachine and qrLoginMachine

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>

---------

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>
This commit is contained in:
abhip2565
2024-05-02 18:36:52 +05:30
committed by GitHub
parent 8feff67f3f
commit 5351a909b8
35 changed files with 1972 additions and 2389 deletions

View File

@@ -21,7 +21,7 @@ import {ScanEvents} from '../machines/bleShare/scan/scanMachine';
import {BOTTOM_TAB_ROUTES, ScanStackParamList} from '../routes/routesConstants';
import {NavigationProp, useNavigation} from '@react-navigation/native';
import {MainBottomTabParamList} from '../routes/main';
import {selectIsScanning} from '../machines/bleShare/scan/selectors';
import {selectIsScanning} from '../machines/bleShare/scan/scanSelectors';
import {
VCItemEvents,
VCItemMachine,

View File

@@ -34,10 +34,10 @@
"verifyCredential": "done.invoke.issuersMachine.verifyingCredential:invocation[0]";
};
missingImplementations: {
actions: never;
actions: "getKeyPairFromStore" | "loadKeyPair" | "logDownloaded" | "resetError" | "resetLoadingReason" | "resetVerificationErrorMessage" | "sendBackupEvent" | "sendErrorEndEvent" | "sendImpressionEvent" | "sendSuccessEndEvent" | "setCredentialTypes" | "setCredentialWrapper" | "setError" | "setIssuers" | "setLoadingReasonAsDisplayIssuers" | "setLoadingReasonAsDownloadingCredentials" | "setLoadingReasonAsSettingUp" | "setMetadataInCredentialData" | "setNoInternet" | "setOIDCConfigError" | "setPrivateKey" | "setPublicKey" | "setSelectedCredentialType" | "setSelectedIssuerId" | "setSelectedIssuers" | "setTokenResponse" | "setVCMetadata" | "setVerifiableCredential" | "storeKeyPair" | "storeVcMetaContext" | "storeVcsContext" | "storeVerifiableCredentialData" | "storeVerifiableCredentialMeta" | "updateVerificationErrorMessage";
delays: never;
guards: never;
services: never;
guards: "canSelectIssuerAgain" | "hasKeyPair" | "hasUserCancelledBiometric" | "isCustomSecureKeystore" | "isInternetConnected" | "isMultipleCredentialsSupported" | "isOIDCConfigError" | "isOIDCflowCancelled" | "isSignedIn" | "shouldFetchIssuersAgain";
services: "checkInternet" | "downloadCredential" | "downloadCredentialTypes" | "downloadIssuerConfig" | "downloadIssuersList" | "generateKeyPair" | "invokeAuthorization" | "isUserSignedAlready" | "verifyCredential";
};
eventsCausingActions: {
"getKeyPairFromStore": "TRY_AGAIN" | "done.invoke.issuersMachine.performAuthorization:invocation[0]";

View File

@@ -0,0 +1,170 @@
import { assign, send, sendParent } from "xstate";
import i18n from "../../i18n";
import { VCShareFlowType } from "../../shared/Utils";
import { parseMetadatas } from "../../shared/VCMetadata";
import { SHOW_FACE_AUTH_CONSENT_QR_LOGIN_FLOW, MY_VCS_STORE_KEY } from "../../shared/constants";
import { getBindingCertificateConstant } from "../../shared/keystore/SecureKeystore";
import { VC, linkTransactionResponse } from "../VerifiableCredential/VCMetaMachine/vc";
import { StoreEvents } from "../store";
export const QrLoginActions=(model:any)=>{
return{
setShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return !event.isDoNotAskAgainChecked;
},
}),
storeShowFaceAuthConsent: send(
(context, event) =>
StoreEvents.SET(SHOW_FACE_AUTH_CONSENT_QR_LOGIN_FLOW, !event.isDoNotAskAgainChecked),
{
to: context => context.serviceRefs.store,
},
),
forwardToParent: sendParent('DISMISS'),
setScanData: model.assign((context, event) => {
const linkCode = event.linkCode;
const flowType = event.flowType;
const selectedVc = event.selectedVc;
return {
...context,
linkCode: linkCode,
flowType: flowType,
selectedVc: selectedVc,
};
}),
getFaceAuthConsent: send(StoreEvents.GET(SHOW_FACE_AUTH_CONSENT_QR_LOGIN_FLOW), {
to: (context:any) => context.serviceRefs.store,
}),
updateShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return event.response || event.response === null;
},
}),
// TODO: loaded VCMetadatas are not used anywhere. remove?
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
to: context => context.serviceRefs.store,
}),
setMyVcs: model.assign({
myVcs: (_context, event) =>
parseMetadatas((event.response || []) as object[]),
}),
loadThumbprint: send(
context =>
StoreEvents.GET(
getBindingCertificateConstant(
context.selectedVc.walletBindingResponse?.walletBindingId,
),
),
{to: context => context.serviceRefs.store},
),
setThumbprint: assign({
thumbprint: (_context, event) => {
return (event.response || '') as string;
},
}),
resetLinkTransactionId: model.assign({
linkTransactionId: () => '',
}),
resetSelectedVoluntaryClaims: model.assign({
selectedVoluntaryClaims: () => [],
}),
setSelectedVc: assign({
selectedVc: (context, event) => {
return {...event.vc};
},
}),
resetSelectedVc: assign({
selectedVc: {} as VC,
}),
resetFlowType: assign({
flowType: VCShareFlowType.SIMPLE_SHARE,
}),
setlinkTransactionResponse: assign({
linkTransactionResponse: (context, event) =>
event.data as linkTransactionResponse,
}),
expandLinkTransResp: assign({
authFactors: context => context.linkTransactionResponse.authFactors,
authorizeScopes: context =>
context.linkTransactionResponse.authorizeScopes,
clientName: context => context.linkTransactionResponse.clientName,
configs: context => context.linkTransactionResponse.configs,
essentialClaims: context =>
context.linkTransactionResponse.essentialClaims,
linkTransactionId: context =>
context.linkTransactionResponse.linkTransactionId,
logoUrl: context => context.linkTransactionResponse.logoUrl,
voluntaryClaims: context =>
context.linkTransactionResponse.voluntaryClaims,
}),
setClaims: context => {
context.voluntaryClaims.map(claim => {
context.isSharing[claim] = false;
});
},
SetErrorMessage: assign({
errorMessage: (context, event) => {
const message = event.data.name;
const ID_ERRORS_MAP = {
invalid_link_code: 'invalidQR',
};
const errorMessage = ID_ERRORS_MAP[message]
? i18n.t(`errors.${ID_ERRORS_MAP[message]}`, {
ns: 'QrLogin',
})
: i18n.t(`errors.genericError`, {
ns: 'common',
});
return errorMessage;
},
}),
setConsentClaims: assign({
isSharing: (context, event) => {
context.isSharing[event.claim] = !event.enable;
if (!event.enable) {
context.selectedVoluntaryClaims.push(event.claim);
} else {
context.selectedVoluntaryClaims =
context.selectedVoluntaryClaims.filter(
eachClaim => eachClaim !== event.claim,
);
}
return {...context.isSharing};
},
}),
setLinkedTransactionId: assign({
linkedTransactionId: (context, event) =>
event.data.linkedTransactionId as string,
}),
}
}

View File

@@ -0,0 +1,13 @@
import { VCShareFlowType } from "../../shared/Utils";
export const QrLoginGuards={
showFaceAuthConsentScreen: context => {
return context.showFaceAuthConsent;
},
isConsentAlreadyCaptured: (_, event) =>
event.data?.consentAction === 'NOCAPTURE',
isSimpleShareFlow: (context, _event) =>
context.flowType === VCShareFlowType.SIMPLE_SHARE,
}

View File

@@ -0,0 +1,277 @@
import {
ActorRefFrom,
EventFrom
} from 'xstate';
import { AppServices } from '../../shared/GlobalContext';
import { TelemetryConstants } from '../../shared/telemetry/TelemetryConstants';
import {
getEndEventData,
sendEndEvent,
} from '../../shared/telemetry/TelemetryUtils';
import { QrLoginActions } from './QrLoginActions';
import { QrLoginmodel } from './QrLoginModel';
import { QrLoginServices } from './QrLoginServices';
import { QrLoginGuards } from './QrLoginGuards';
const model = QrLoginmodel;
export const QrLoginEvents = model.events;
export type QrLoginRef = ActorRefFrom<typeof qrLoginMachine>;
export const qrLoginMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QEUBOAZA9lAlgOwDocIAbMAYgGUBhAQQDl6BJegcQH0ARAeXoFEA2gAYAuolAAHTLBwAXHJjziQAD0QBGACzqATAQCs+nZv0BmABzqL50zoA0IAJ6IA7KaEFNATnPmv6oUMhHUMAX1CHNCxcQmIyck4mSgBZJMphMSQQKRl5RWU1BF1zFwIvHQrTFxdNCxcANgdnBH16j1NbdT8vfW8q9XDIjGx8AhJ8AGsAFVQAQzxYWYBjPLxyCEUwIjwAN0wJraiRwnG8abmF5dWEfD2l2dWMjOUcuQUlLMKtXQMjEwsrL5bE1EF4XOYCEJ6iEXF4ekIOppzIMQEcYmNJjN5osVu9yGBUKhMKgCBISA8AGbEgC2BDRo1O52xV3eN12mHuj1Ezyyr1WBQ0JQhmlhotMZmM+hcIIQNX0nnUhh65h06hcOhKKPphFgAAtMAB3ADqs1QeHwUHI1F4ADEmAAlZI8yTSN75T6IEIeHT+eo1Lyaeriwwy+pggiwtXw9SKjU6LXDdF6w0ms0Wq0Mah8dDO7Ku-kehBegg+9R+7yB4P6GV+dQRlwBIRqoTqjX1BPRUaUfUGviE4kZ+hZnOiF7594Coo9TSQtyqqUx9zApyINpeCNIoVaYzlUwd45jTCzCDJRwANSWsCoU249r47DvlAACrxKIJR7zx+7QIU-fLofoMaATYWjaKGLjylCOjQi4gSmJoOgNPuSY9jsSzoDgsCyFQ2Z8NQUzsGe1C5nyE6Fr4M4BkI3j6DYtEttWK4tDGEb1H6DbmEYAZschozJgaaEYVh5Bnnw9pMDaACaJFfh8P6uLRZRIoqKl0UI5gyiK9SQtobjUZu0KaLxOqoehmHYYkKRpDJuRkfJRTqRCpj1PoTYBj01QVKGPwVIBHT6AGqq1MZBAUssYC0AArrIurkDatBZoRtDoEwnA2W6cmqJ6rklr6-qVoYjHNP4kI2Jo1ENr01EBiFYVLBF0WxfFiUsGeyWpelBb2cWpbltxVahiKuVqmGXT1IG6heLV4VRTFg7Dp1dlZUWOW9flQaFTW2glrBYK0SqZaqiFtyzOMEBMBAYB4PIsiOAkSSpJQ6Qfi6tnfstf4EABQFdKYoHqKG1EECUrnmEIcJ+FC7YRKiiajCdZ0XVdN13XeUz2pJhFiRJTB0FMTC8It72FD1eUVhtIZMQF2mRjUHT1FYPTQ0MnaEKgYAAI6RXAsjUIosDI1atoOk6L15m9mW-hBX0hD9IGaGBTEIfKvTNoGOhVF4zOw6zBDs1zPN8wsgt0EO2ZE5Lq7S99ql-QrANMe4M6RqNDa6NC5hGTD2oEBseAUJZj3PZkr0ZZOAS+AQzmuZNNGefYSshAYunUdCmvaz7+vc1hFpGwL13rJs2x7AcdJw2znPZ-IeBQHnyNsncDzvE8YukcTq42GUYYtuNgFsQGMqqnWNjwSNiFeACe7e+XeuVzzuf84LBJEiSZKUjSZe61n8813X10NxyTeKC3Ifi2HhYR05LluXHrY1qYO2OanY1sciKJ4Jgl3wFk2pjhLk4AFpGhMQAfKOE4CIGQKnizA8cQwB-3PvZYoEJVT+FVCUaC-hNDeXlDYCoipwRCCIeKEKjIsSXFxO9NulsiguWdpYIMQZoK9F6DKOmX0FYQSITHHoU1p6634qmc0NcEFdWWghNh4MCBlnqGDLQ7EAxvxgeibsho+wr1EUtEmLYIQqmjvbeCKoiqrgCCWTc4JtzeA1qQo8J5zyXk0e3BAIph7jTYvBQIwQLDgWdv4Bo5UkTqjMCFfiglzKOJoX4bS4oGyIR9FUIhCdmgMz0EEqw4pApaGgTrA8dUGoxQiZOGoX0Si1CDFYAKCEkkaChKVUeHQNYNGosdXYp1iBI2unIZoocxGFCRHoKU40VT+H2n4QG8oqig3Bj4LwUIXAhW3lhPeshCmFiROucqhlYK6AXA7Zoxg6wTwgv09JHRfAhT9vAz8-9Cz+HXJUxUCIzBIiRJpaCOkRoNlMGCME8EFlzxzrvRe11Vn2RBtI1oDM-pGAsF4GsdZXJEI8n8Go41wjhCAA */
model.createMachine(
{
predictableActionArguments: true,
preserveActionOrder: true,
tsTypes: {} as import('./QrLoginMachine.typegen').Typegen0,
schema: {
context: model.initialContext,
events: {} as EventFrom<typeof model>,
},
id: 'QrLogin',
initial: 'waitingForData',
entry: ['resetSelectedVc', 'resetFlowType'],
states: {
waitingForData: {
on: {
GET: {
actions: [
'setScanData',
'resetLinkTransactionId',
'resetSelectedVoluntaryClaims',
],
target: 'checkFaceAuthConsent',
},
},
},
checkFaceAuthConsent: {
entry: 'getFaceAuthConsent',
on: {
STORE_RESPONSE: {
actions: 'updateShowFaceAuthConsent',
target: 'linkTransaction',
},
},
},
linkTransaction: {
invoke: {
src: 'linkTransaction',
onDone: [
{
cond: 'isSimpleShareFlow',
actions: [
'setlinkTransactionResponse',
'expandLinkTransResp',
'setClaims',
],
target: 'loadMyVcs',
},
{
cond: 'showFaceAuthConsentScreen',
target: 'faceVerificationConsent',
},
{
actions: [
'setlinkTransactionResponse',
'expandLinkTransResp',
'setClaims',
],
target: 'faceAuth',
},
],
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
ShowError: {
on: {
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
loadMyVcs: {
entry: 'loadMyVcs',
on: {
STORE_RESPONSE: {
actions: 'setMyVcs',
target: 'showvcList',
},
},
},
showvcList: {
on: {
SELECT_VC: {
actions: 'setSelectedVc',
},
VERIFY: [
{
cond: 'showFaceAuthConsentScreen',
target: 'faceVerificationConsent',
},
{
target: 'faceAuth',
},
],
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
faceVerificationConsent: {
on: {
FACE_VERIFICATION_CONSENT: {
actions: ['storeShowFaceAuthConsent', 'setShowFaceAuthConsent'],
target: 'faceAuth',
},
DISMISS: [{
cond:'isSimpleShareFlow',
target: 'showvcList',
},
{
actions: 'forwardToParent',
target: 'waitingForData',
}
]
},
},
faceAuth: {
on: {
FACE_VALID: {
target: 'loadingThumbprint',
},
FACE_INVALID: {
target: 'invalidIdentity',
},
CANCEL: [
{
cond: 'isSimpleShareFlow',
target: 'showvcList',
},
{
actions: 'forwardToParent',
target: 'waitingForData',
},
],
},
},
invalidIdentity: {
on: {
DISMISS: [
{
cond: 'isSimpleShareFlow',
target: 'showvcList',
},
{
actions: 'forwardToParent',
target: 'waitingForData',
},
,],
RETRY_VERIFICATION: {
target: 'faceAuth',
},
},
},
sendingAuthenticate: {
invoke: {
src: 'sendAuthenticate',
onDone: [
{
cond: 'isConsentAlreadyCaptured',
target: 'success',
},
{
target: 'requestConsent',
actions: 'setLinkedTransactionId',
},
],
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
requestConsent: {
on: {
CONFIRM: {
target: 'sendingConsent',
},
TOGGLE_CONSENT_CLAIM: {
actions: 'setConsentClaims',
target: 'requestConsent',
},
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
loadingThumbprint: {
entry: 'loadThumbprint',
on: {
STORE_RESPONSE: {
actions: 'setThumbprint',
target: 'sendingAuthenticate',
},
},
},
sendingConsent: {
invoke: {
src: 'sendConsent',
onDone: {
target: 'success',
},
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
success: {
entry: [
() =>
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.qrLogin,
TelemetryConstants.EndEventStatus.success,
),
),
],
on: {
CONFIRM: {
target: 'done',
},
},
},
done: {
type: 'final',
data: context => context,
},
},
},
{
actions:QrLoginActions(model),
services:QrLoginServices,
guards: QrLoginGuards,
},
);
export function createQrLoginMachine(serviceRefs: AppServices) {
return qrLoginMachine.withContext({
...qrLoginMachine.context,
serviceRefs,
});
}

View File

@@ -0,0 +1,64 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"done.invoke.QrLogin.linkTransaction:invocation[0]": { type: "done.invoke.QrLogin.linkTransaction:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.QrLogin.sendingAuthenticate:invocation[0]": { type: "done.invoke.QrLogin.sendingAuthenticate:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.QrLogin.linkTransaction:invocation[0]": { type: "error.platform.QrLogin.linkTransaction:invocation[0]"; data: unknown };
"error.platform.QrLogin.sendingAuthenticate:invocation[0]": { type: "error.platform.QrLogin.sendingAuthenticate:invocation[0]"; data: unknown };
"error.platform.QrLogin.sendingConsent:invocation[0]": { type: "error.platform.QrLogin.sendingConsent:invocation[0]"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"linkTransaction": "done.invoke.QrLogin.linkTransaction:invocation[0]";
"sendAuthenticate": "done.invoke.QrLogin.sendingAuthenticate:invocation[0]";
"sendConsent": "done.invoke.QrLogin.sendingConsent:invocation[0]";
};
missingImplementations: {
actions: "SetErrorMessage" | "expandLinkTransResp" | "forwardToParent" | "getFaceAuthConsent" | "loadMyVcs" | "loadThumbprint" | "resetFlowType" | "resetLinkTransactionId" | "resetSelectedVc" | "resetSelectedVoluntaryClaims" | "setClaims" | "setConsentClaims" | "setLinkedTransactionId" | "setMyVcs" | "setScanData" | "setSelectedVc" | "setShowFaceAuthConsent" | "setThumbprint" | "setlinkTransactionResponse" | "storeShowFaceAuthConsent" | "updateShowFaceAuthConsent";
delays: never;
guards: "isConsentAlreadyCaptured" | "isSimpleShareFlow" | "showFaceAuthConsentScreen";
services: "linkTransaction" | "sendAuthenticate" | "sendConsent";
};
eventsCausingActions: {
"SetErrorMessage": "error.platform.QrLogin.linkTransaction:invocation[0]" | "error.platform.QrLogin.sendingAuthenticate:invocation[0]" | "error.platform.QrLogin.sendingConsent:invocation[0]";
"expandLinkTransResp": "done.invoke.QrLogin.linkTransaction:invocation[0]";
"forwardToParent": "CANCEL" | "DISMISS";
"getFaceAuthConsent": "GET";
"loadMyVcs": "done.invoke.QrLogin.linkTransaction:invocation[0]";
"loadThumbprint": "FACE_VALID";
"resetFlowType": "xstate.init";
"resetLinkTransactionId": "GET";
"resetSelectedVc": "xstate.init";
"resetSelectedVoluntaryClaims": "GET";
"setClaims": "done.invoke.QrLogin.linkTransaction:invocation[0]";
"setConsentClaims": "TOGGLE_CONSENT_CLAIM";
"setLinkedTransactionId": "done.invoke.QrLogin.sendingAuthenticate:invocation[0]";
"setMyVcs": "STORE_RESPONSE";
"setScanData": "GET";
"setSelectedVc": "SELECT_VC";
"setShowFaceAuthConsent": "FACE_VERIFICATION_CONSENT";
"setThumbprint": "STORE_RESPONSE";
"setlinkTransactionResponse": "done.invoke.QrLogin.linkTransaction:invocation[0]";
"storeShowFaceAuthConsent": "FACE_VERIFICATION_CONSENT";
"updateShowFaceAuthConsent": "STORE_RESPONSE";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
"isConsentAlreadyCaptured": "done.invoke.QrLogin.sendingAuthenticate:invocation[0]";
"isSimpleShareFlow": "CANCEL" | "DISMISS" | "done.invoke.QrLogin.linkTransaction:invocation[0]";
"showFaceAuthConsentScreen": "VERIFY" | "done.invoke.QrLogin.linkTransaction:invocation[0]";
};
eventsCausingServices: {
"linkTransaction": "STORE_RESPONSE";
"sendAuthenticate": "STORE_RESPONSE";
"sendConsent": "CONFIRM";
};
matchesStates: "ShowError" | "checkFaceAuthConsent" | "done" | "faceAuth" | "faceVerificationConsent" | "invalidIdentity" | "linkTransaction" | "loadMyVcs" | "loadingThumbprint" | "requestConsent" | "sendingAuthenticate" | "sendingConsent" | "showvcList" | "success" | "waitingForData";
tags: never;
}

View File

@@ -0,0 +1,69 @@
import { createModel } from "xstate/lib/model";
import { AppServices } from "../../shared/GlobalContext";
import { VCShareFlowType } from "../../shared/Utils";
import { VCMetadata } from "../../shared/VCMetadata";
import { VC, linkTransactionResponse } from "../VerifiableCredential/VCMetaMachine/vc";
const QrLoginEvents= {
SELECT_VC: (vc: VC) => ({vc}),
SCANNING_DONE: (params: string) => ({params}),
STORE_RESPONSE: (response: unknown) => ({response}),
STORE_ERROR: (error: Error) => ({error}),
TOGGLE_CONSENT_CLAIM: (enable: boolean, claim: string) => ({
enable,
claim,
}),
DISMISS: () => ({}),
CONFIRM: () => ({}),
GET: (
linkCode: string,
flowType: string,
selectedVc: VC,
faceAuthConsentGiven: boolean,
) => ({
linkCode,
flowType,
selectedVc,
faceAuthConsentGiven,
}),
VERIFY: () => ({}),
CANCEL: () => ({}),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) => ({
isDoNotAskAgainChecked,
}),
}
export const QrLoginmodel = createModel(
{
serviceRefs: {} as AppServices,
selectedVc: {} as VC,
linkCode: '',
flowType: VCShareFlowType.SIMPLE_SHARE,
myVcs: [] as VCMetadata[],
thumbprint: '',
linkTransactionResponse: {} as linkTransactionResponse,
authFactors: [],
authorizeScopes: null,
clientName: {},
configs: {},
essentialClaims: [],
linkTransactionId: '',
logoUrl: '',
voluntaryClaims: [],
selectedVoluntaryClaims: [],
errorMessage: '',
domainName: '',
consentClaims: ['name', 'picture'],
isSharing: {},
linkedTransactionId: '',
showFaceAuthConsent: true as boolean,
},
{
events:QrLoginEvents,
},
);

View File

@@ -0,0 +1,117 @@
import { StateFrom } from "xstate";
import { getMosipLogo } from "../../components/VC/common/VCUtils";
import { VCMetadata } from "../../shared/VCMetadata";
import { qrLoginMachine } from "./QrLoginMachine";
type State = StateFrom<typeof qrLoginMachine>;
export function selectMyVcs(state: State) {
return state.context.myVcs;
}
export function selectIsWaitingForData(state: State) {
return state.matches('waitingForData');
}
export function selectDomainName(state: State) {
return state.context.domainName;
}
export function selectIsLinkTransaction(state: State) {
return state.matches('linkTransaction');
}
export function selectIsloadMyVcs(state: State) {
return state.matches('loadMyVcs');
}
export function selectIsShowingVcList(state: State) {
return state.matches('showvcList');
}
export function selectIsisVerifyingIdentity(state: State) {
return state.matches('faceAuth');
}
export function selectIsInvalidIdentity(state: State) {
return state.matches('invalidIdentity');
}
export function selectIsShowError(state: State) {
return state.matches('ShowError');
}
export function selectIsRequestConsent(state: State) {
return state.matches('requestConsent');
}
export function selectIsSendingAuthenticate(state: State) {
return state.matches('sendingAuthenticate');
}
export function selectIsSendingConsent(state: State) {
return state.matches('sendingConsent');
}
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 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(),
};
}
export function selectLinkTransactionResponse(state: State) {
return state.context.linkTransactionResponse;
}
export function selectEssentialClaims(state: State) {
return state.context.essentialClaims;
}
export function selectVoluntaryClaims(state: State) {
return state.context.voluntaryClaims;
}
export function selectLogoUrl(state: State) {
return state.context.logoUrl;
}
export function selectClientName(state: State) {
return state.context.clientName;
}
export function selectErrorMessage(state: State) {
return state.context.errorMessage;
}
export function selectIsSharing(state: State) {
return state.context.isSharing;
}
export function selectIsFaceVerificationConsent(state: State) {
return state.matches('faceVerificationConsent');
}

View File

@@ -0,0 +1,111 @@
import {request} from '../../shared/request';
import getAllConfigurations, { API_URLS } from "../../shared/api";
import { ESIGNET_BASE_URL } from "../../shared/constants";
import { isHardwareKeystoreExists, getJWT } from "../../shared/cryptoutil/cryptoUtil";
import { getPrivateKey } from "../../shared/keystore/SecureKeystore";
export const QrLoginServices= {
linkTransaction: async context => {
const response = await request(
API_URLS.linkTransaction.method,
API_URLS.linkTransaction.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkCode: context.linkCode,
},
},
ESIGNET_BASE_URL,
);
return response.response;
},
sendAuthenticate: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
}
var config = await getAllConfigurations();
const header = {
alg: 'RS256',
'x5t#S256': context.thumbprint,
};
const payload = {
iss: config.issuer,
sub: individualId,
aud: config.audience,
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 18000,
};
const jwt = await getJWT(header, payload, individualId, privateKey);
const response = await request(
API_URLS.authenticate.method,
API_URLS.authenticate.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkedTransactionId: context.linkTransactionId,
individualId: individualId,
challengeList: [
{
authFactorType: 'WLA',
challenge: jwt,
format: 'jwt',
},
],
},
},
ESIGNET_BASE_URL,
);
return response.response;
},
sendConsent: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
}
const header = {
alg: 'RS256',
'x5t#S256': context.thumbprint,
};
const payload = {
accepted_claims: context.essentialClaims
.concat(context.selectedVoluntaryClaims)
.sort(),
permitted_authorized_scopes: context.authorizeScopes,
};
const JWT = await getJWT(header, payload, individualId, privateKey);
const jwtComponents = JWT.split('.');
const detachedSignature = jwtComponents[0] + '.' + jwtComponents[2];
const resp = await request(
API_URLS.sendConsent.method,
API_URLS.sendConsent.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkedTransactionId: context.linkedTransactionId,
acceptedClaims: context.essentialClaims
.concat(context.selectedVoluntaryClaims)
.sort(),
permittedAuthorizeScopes: context.authorizeScopes,
signature: detachedSignature,
},
},
ESIGNET_BASE_URL,
);
},
}

View File

@@ -1,710 +0,0 @@
import {
ActorRefFrom,
assign,
EventFrom,
send,
sendParent,
StateFrom,
} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {AppServices} from '../shared/GlobalContext';
import {
ESIGNET_BASE_URL,
FACE_AUTH_CONSENT,
MY_VCS_STORE_KEY,
} from '../shared/constants';
import {StoreEvents} from './store';
import {
linkTransactionResponse,
VC,
} from './VerifiableCredential/VCMetaMachine/vc';
import {request} from '../shared/request';
import {
getJWT,
isHardwareKeystoreExists,
} from '../shared/cryptoutil/cryptoUtil';
import {
getBindingCertificateConstant,
getPrivateKey,
} from '../shared/keystore/SecureKeystore';
import i18n from '../i18n';
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
import {
getEndEventData,
sendEndEvent,
} from '../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../shared/telemetry/TelemetryConstants';
import getAllConfigurations, {API_URLS} from '../shared/api';
import {VCShareFlowType} from '../shared/Utils';
import {getMosipLogo} from '../components/VC/common/VCUtils';
const model = createModel(
{
serviceRefs: {} as AppServices,
selectedVc: {} as VC,
linkCode: '',
flowType: VCShareFlowType.SIMPLE_SHARE,
myVcs: [] as VCMetadata[],
thumbprint: '',
linkTransactionResponse: {} as linkTransactionResponse,
authFactors: [],
authorizeScopes: null,
clientName: {},
configs: {},
essentialClaims: [],
linkTransactionId: '',
logoUrl: '',
voluntaryClaims: [],
selectedVoluntaryClaims: [],
errorMessage: '',
domainName: '',
consentClaims: ['name', 'picture'],
isSharing: {},
linkedTransactionId: '',
showFaceAuthConsent: true as boolean,
},
{
events: {
SELECT_VC: (vc: VC) => ({vc}),
SCANNING_DONE: (params: string) => ({params}),
STORE_RESPONSE: (response: unknown) => ({response}),
STORE_ERROR: (error: Error) => ({error}),
TOGGLE_CONSENT_CLAIM: (enable: boolean, claim: string) => ({
enable,
claim,
}),
DISMISS: () => ({}),
CONFIRM: () => ({}),
GET: (
linkCode: string,
flowType: string,
selectedVc: VC,
faceAuthConsentGiven: boolean,
) => ({
linkCode,
flowType,
selectedVc,
faceAuthConsentGiven,
}),
VERIFY: () => ({}),
CANCEL: () => ({}),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) => ({
isConsentGiven,
}),
},
},
);
export const QrLoginEvents = model.events;
export type QrLoginRef = ActorRefFrom<typeof qrLoginMachine>;
export const qrLoginMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QEUBOAZA9lAlgOwDocIAbMAYgGUBhAQQDl6BJegcQH0ARAeXoFEA2gAYAuolAAHTLBwAXHJjziQAD0QBGACzqATAQCs+nZv0BmABzqL50zoA0IAJ6IA7KaEFNATnPmv6oUMhHUMAX1CHNCxcQmIyck4mSgBZJMphMSQQKRl5RWU1BF1zFwIvHQrTFxdNCxcANgdnBH16j1NbdT8vfW8q9XDIjGx8AhJ8AGsAFVQAQzxYWYBjPLxyCEUwIjwAN0wJraiRwnG8abmF5dWEfD2l2dWMjOUcuQUlLMKtXQMjEwsrL5bE1EF4XOYCEJ6iEXF4ekIOppzIMQEcYmNJjN5osVu9yGBUKhMKgCBISA8AGbEgC2BDRo1O52xV3eN12mHuj1Ezyyr1WBQ0JQhmlhotMZmM+hcIIQNX0nnUhh65h06hcOhKKPphFgAAtMAB3ADqs1QeHwUHI1F4ADEmAAlZI8yTSN75T6IEIeHT+eo1Lyaeriwwy+pggiwtXw9SKjU6LXDdF6w0ms0Wq0Mah8dDO7Ku-kehBegg+9R+7yB4P6GV+dQRlwBIRqoTqjX1BPRUaUfUGviE4kZ+hZnOiF7594Coo9TSQtyqqUx9zApyINpeCNIoVaYzlUwd45jTCzCDJRwANSWsCoU249r47DvlAACrxKIJR7zx+7QIU-fLofoMaATYWjaKGLjylCOjQi4gSmJoOgNPuSY9jsSzoDgsCyFQ2Z8NQUzsGe1C5nyE6Fr4M4BkI3j6DYtEttWK4tDGEb1H6DbmEYAZschozJgaaEYVh5Bnnw9pMDaACaJFfh8P6uLRZRIoqKl0UI5gyiK9SQtobjUZu0KaLxOqoehmHYYkKRpDJuRkfJRTqRCpj1PoTYBj01QVKGPwVIBHT6AGqq1MZBAUssYC0AArrIurkDatBZoRtDoEwnA2W6cmqJ6rklr6-qVoYjHNP4kI2Jo1ENr01EBiFYVLBF0WxfFiUsGeyWpelBb2cWpbltxVahiKuVqmGXT1IG6heLV4VRTFg7Dp1dlZUWOW9flQaFTW2glrBYK0SqZaqiFtyzOMEBMBAYB4PIsiOAkSSpJQ6Qfi6tnfstf4EABQFdKYoHqKG1EECUrnmEIcJ+FC7YRKiiajCdZ0XVdN13XeUz2pJhFiRJTB0FMTC8It72FD1eUVhtIZMQF2mRjUHT1FYPTQ0MnaEKgYAAI6RXAsjUIosDI1atoOk6L15m9mW-hBX0hD9IGaGBTEIfKvTNoGOhVF4zOw6zBDs1zPN8wsgt0EO2ZE5Lq7S99ql-QrANMe4M6RqNDa6NC5hGTD2oEBseAUJZj3PZkr0ZZOAS+AQzmuZNNGefYSshAYunUdCmvaz7+vc1hFpGwL13rJs2x7AcdJw2znPZ-IeBQHnyNsncDzvE8YukcTq42GUYYtuNgFsQGMqqnWNjwSNiFeACe7e+XeuVzzuf84LBJEiSZKUjSZe61n8813X10NxyTeKC3Ifi2HhYR05LluXHrY1qYO2OanY1sciKJ4Jgl3wFk2pjhLk4AFpGhMQAfKOE4CIGQKnizA8cQwB-3PvZYoEJVT+FVCUaC-hNDeXlDYCoipwRCCIeKEKjIsSXFxO9NulsiguWdpYIMQZoK9F6DKOmX0FYQSITHHoU1p6634qmc0NcEFdWWghNh4MCBlnqGDLQ7EAxvxgeibsho+wr1EUtEmLYIQqmjvbeCKoiqrgCCWTc4JtzeA1qQo8J5zyXk0e3BAIph7jTYvBQIwQLDgWdv4Bo5UkTqjMCFfiglzKOJoX4bS4oGyIR9FUIhCdmgMz0EEqw4pApaGgTrA8dUGoxQiZOGoX0Si1CDFYAKCEkkaChKVUeHQNYNGosdXYp1iBI2unIZoocxGFCRHoKU40VT+H2n4QG8oqig3Bj4LwUIXAhW3lhPeshCmFiROucqhlYK6AXA7Zoxg6wTwgv09JHRfAhT9vAz8-9Cz+HXJUxUCIzBIiRJpaCOkRoNlMGCME8EFlzxzrvRe11Vn2RBtI1oDM-pGAsF4GsdZXJEI8n8Go41wjhCAA */
model.createMachine(
{
predictableActionArguments: true,
preserveActionOrder: true,
tsTypes: {} as import('./QrLoginMachine.typegen').Typegen0,
schema: {
context: model.initialContext,
events: {} as EventFrom<typeof model>,
},
id: 'QrLogin',
initial: 'waitingForData',
entry: ['resetSelectedVc', 'resetFlowType'],
states: {
waitingForData: {
on: {
GET: {
actions: [
'setScanData',
'setFaceAuthConsent',
'resetLinkTransactionId',
'resetSelectedVoluntaryClaims',
],
target: 'linkTransaction',
},
},
},
linkTransaction: {
invoke: {
src: 'linkTransaction',
onDone: [
{
cond: 'isSimpleShareFlow',
actions: [
'setlinkTransactionResponse',
'expandLinkTransResp',
'setClaims',
],
target: 'loadMyVcs',
},
{
actions: [
'setlinkTransactionResponse',
'expandLinkTransResp',
'setClaims',
],
target: 'faceAuth',
},
],
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
ShowError: {
on: {
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
loadMyVcs: {
entry: 'loadMyVcs',
on: {
STORE_RESPONSE: {
actions: 'setMyVcs',
target: 'showvcList',
},
},
},
showvcList: {
on: {
SELECT_VC: {
actions: 'setSelectedVc',
},
VERIFY: [
{
cond: 'showFaceAuthConsentScreen',
target: 'faceVerificationConsent',
},
{
target: 'faceAuth',
},
],
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
faceVerificationConsent: {
on: {
FACE_VERIFICATION_CONSENT: {
actions: ['storeShowFaceAuthConsent', 'setShowFaceAuthConsent'],
target: 'faceAuth',
},
DISMISS: {
target: 'showvcList',
},
},
},
faceAuth: {
on: {
FACE_VALID: {
target: 'loadingThumbprint',
},
FACE_INVALID: {
target: 'invalidIdentity',
},
CANCEL: [
{
cond: 'isSimpleShareFlow',
target: 'showvcList',
},
{
actions: 'forwardToParent',
target: 'waitingForData',
},
],
},
},
invalidIdentity: {
on: {
DISMISS: {
target: 'showvcList',
},
RETRY_VERIFICATION: {
target: 'faceAuth',
},
},
},
sendingAuthenticate: {
invoke: {
src: 'sendAuthenticate',
onDone: [
{
cond: 'isConsentAlreadyCaptured',
target: 'success',
},
{
target: 'requestConsent',
actions: 'setLinkedTransactionId',
},
],
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
requestConsent: {
on: {
CONFIRM: {
target: 'sendingConsent',
},
TOGGLE_CONSENT_CLAIM: {
actions: 'setConsentClaims',
target: 'requestConsent',
},
DISMISS: {
actions: 'forwardToParent',
target: 'waitingForData',
},
},
},
loadingThumbprint: {
entry: 'loadThumbprint',
on: {
STORE_RESPONSE: {
actions: 'setThumbprint',
target: 'sendingAuthenticate',
},
},
},
sendingConsent: {
invoke: {
src: 'sendConsent',
onDone: {
target: 'success',
},
onError: [
{
actions: 'SetErrorMessage',
target: 'ShowError',
},
],
},
},
success: {
entry: [
() =>
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.qrLogin,
TelemetryConstants.EndEventStatus.success,
),
),
],
on: {
CONFIRM: {
target: 'done',
},
},
},
done: {
type: 'final',
data: context => context,
},
},
},
{
actions: {
setShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return !event.isConsentGiven;
},
}),
storeShowFaceAuthConsent: send(
(context, event) =>
StoreEvents.SET(FACE_AUTH_CONSENT, !event.isConsentGiven),
{
to: context => context.serviceRefs.store,
},
),
forwardToParent: sendParent('DISMISS'),
setScanData: model.assign((context, event) => {
const linkCode = event.linkCode;
const flowType = event.flowType;
const selectedVc = event.selectedVc;
return {
...context,
linkCode: linkCode,
flowType: flowType,
selectedVc: selectedVc,
};
}),
setFaceAuthConsent: assign({
showFaceAuthConsent: (context, event) => {
return event.faceAuthConsentGiven;
},
}),
// TODO: loaded VCMetadatas are not used anywhere. remove?
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
to: context => context.serviceRefs.store,
}),
setMyVcs: model.assign({
myVcs: (_context, event) =>
parseMetadatas((event.response || []) as object[]),
}),
loadThumbprint: send(
context =>
StoreEvents.GET(
getBindingCertificateConstant(
context.selectedVc.walletBindingResponse?.walletBindingId,
),
),
{to: context => context.serviceRefs.store},
),
setThumbprint: assign({
thumbprint: (_context, event) => {
return (event.response || '') as string;
},
}),
resetLinkTransactionId: model.assign({
linkTransactionId: () => '',
}),
resetSelectedVoluntaryClaims: model.assign({
selectedVoluntaryClaims: () => [],
}),
setSelectedVc: assign({
selectedVc: (context, event) => {
return {...event.vc};
},
}),
resetSelectedVc: assign({
selectedVc: {} as VC,
}),
resetFlowType: assign({
flowType: VCShareFlowType.SIMPLE_SHARE,
}),
setlinkTransactionResponse: assign({
linkTransactionResponse: (context, event) =>
event.data as linkTransactionResponse,
}),
expandLinkTransResp: assign({
authFactors: context => context.linkTransactionResponse.authFactors,
authorizeScopes: context =>
context.linkTransactionResponse.authorizeScopes,
clientName: context => context.linkTransactionResponse.clientName,
configs: context => context.linkTransactionResponse.configs,
essentialClaims: context =>
context.linkTransactionResponse.essentialClaims,
linkTransactionId: context =>
context.linkTransactionResponse.linkTransactionId,
logoUrl: context => context.linkTransactionResponse.logoUrl,
voluntaryClaims: context =>
context.linkTransactionResponse.voluntaryClaims,
}),
setClaims: context => {
context.voluntaryClaims.map(claim => {
context.isSharing[claim] = false;
});
},
SetErrorMessage: assign({
errorMessage: (context, event) => {
const message = event.data.name;
const ID_ERRORS_MAP = {
invalid_link_code: 'invalidQR',
};
const errorMessage = ID_ERRORS_MAP[message]
? i18n.t(`errors.${ID_ERRORS_MAP[message]}`, {
ns: 'QrLogin',
})
: i18n.t(`errors.genericError`, {
ns: 'common',
});
return errorMessage;
},
}),
setConsentClaims: assign({
isSharing: (context, event) => {
context.isSharing[event.claim] = !event.enable;
if (!event.enable) {
context.selectedVoluntaryClaims.push(event.claim);
} else {
context.selectedVoluntaryClaims =
context.selectedVoluntaryClaims.filter(
eachClaim => eachClaim !== event.claim,
);
}
return {...context.isSharing};
},
}),
setLinkedTransactionId: assign({
linkedTransactionId: (context, event) =>
event.data.linkedTransactionId as string,
}),
},
services: {
linkTransaction: async context => {
const response = await request(
API_URLS.linkTransaction.method,
API_URLS.linkTransaction.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkCode: context.linkCode,
},
},
ESIGNET_BASE_URL,
);
return response.response;
},
sendAuthenticate: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
}
var config = await getAllConfigurations();
const header = {
alg: 'RS256',
'x5t#S256': context.thumbprint,
};
const payload = {
iss: config.issuer,
sub: individualId,
aud: config.audience,
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 18000,
};
const jwt = await getJWT(header, payload, individualId, privateKey);
const response = await request(
API_URLS.authenticate.method,
API_URLS.authenticate.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkedTransactionId: context.linkTransactionId,
individualId: individualId,
challengeList: [
{
authFactorType: 'WLA',
challenge: jwt,
format: 'jwt',
},
],
},
},
ESIGNET_BASE_URL,
);
return response.response;
},
sendConsent: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
}
const header = {
alg: 'RS256',
'x5t#S256': context.thumbprint,
};
const payload = {
accepted_claims: context.essentialClaims
.concat(context.selectedVoluntaryClaims)
.sort(),
permitted_authorized_scopes: context.authorizeScopes,
};
const JWT = await getJWT(header, payload, individualId, privateKey);
const jwtComponents = JWT.split('.');
const detachedSignature = jwtComponents[0] + '.' + jwtComponents[2];
const resp = await request(
API_URLS.sendConsent.method,
API_URLS.sendConsent.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
linkedTransactionId: context.linkedTransactionId,
acceptedClaims: context.essentialClaims
.concat(context.selectedVoluntaryClaims)
.sort(),
permittedAuthorizeScopes: context.authorizeScopes,
signature: detachedSignature,
},
},
ESIGNET_BASE_URL,
);
},
},
guards: {
showFaceAuthConsentScreen: context => {
return context.showFaceAuthConsent;
},
isConsentAlreadyCaptured: (_, event) =>
event.data?.consentAction === 'NOCAPTURE',
isSimpleShareFlow: (context, _event) =>
context.flowType === VCShareFlowType.SIMPLE_SHARE,
},
},
);
export function createQrLoginMachine(serviceRefs: AppServices) {
return qrLoginMachine.withContext({
...qrLoginMachine.context,
serviceRefs,
});
}
type State = StateFrom<typeof qrLoginMachine>;
export function selectMyVcs(state: State) {
return state.context.myVcs;
}
export function selectIsWaitingForData(state: State) {
return state.matches('waitingForData');
}
export function selectDomainName(state: State) {
return state.context.domainName;
}
export function selectIsLinkTransaction(state: State) {
return state.matches('linkTransaction');
}
export function selectIsloadMyVcs(state: State) {
return state.matches('loadMyVcs');
}
export function selectIsShowingVcList(state: State) {
return state.matches('showvcList');
}
export function selectIsisVerifyingIdentity(state: State) {
return state.matches('faceAuth');
}
export function selectIsInvalidIdentity(state: State) {
return state.matches('invalidIdentity');
}
export function selectIsShowError(state: State) {
return state.matches('ShowError');
}
export function selectIsRequestConsent(state: State) {
return state.matches('requestConsent');
}
export function selectIsSendingAuthenticate(state: State) {
return state.matches('sendingAuthenticate');
}
export function selectIsSendingConsent(state: State) {
return state.matches('sendingConsent');
}
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 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(),
};
}
export function selectLinkTransactionResponse(state: State) {
return state.context.linkTransactionResponse;
}
export function selectEssentialClaims(state: State) {
return state.context.essentialClaims;
}
export function selectVoluntaryClaims(state: State) {
return state.context.voluntaryClaims;
}
export function selectLogoUrl(state: State) {
return state.context.logoUrl;
}
export function selectClientName(state: State) {
return state.context.clientName;
}
export function selectErrorMessage(state: State) {
return state.context.errorMessage;
}
export function selectIsSharing(state: State) {
return state.context.isSharing;
}
export function selectIsFaceVerificationConsent(state: State) {
return state.matches('faceVerificationConsent');
}

View File

@@ -1,95 +0,0 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.QrLogin.linkTransaction:invocation[0]': {
type: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.QrLogin.sendingAuthenticate:invocation[0]': {
type: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.QrLogin.linkTransaction:invocation[0]': {
type: 'error.platform.QrLogin.linkTransaction:invocation[0]';
data: unknown;
};
'error.platform.QrLogin.sendingAuthenticate:invocation[0]': {
type: 'error.platform.QrLogin.sendingAuthenticate:invocation[0]';
data: unknown;
};
'error.platform.QrLogin.sendingConsent:invocation[0]': {
type: 'error.platform.QrLogin.sendingConsent:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
linkTransaction: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
sendAuthenticate: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
sendConsent: 'done.invoke.QrLogin.sendingConsent:invocation[0]';
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
SetErrorMessage:
| 'error.platform.QrLogin.linkTransaction:invocation[0]'
| 'error.platform.QrLogin.sendingAuthenticate:invocation[0]'
| 'error.platform.QrLogin.sendingConsent:invocation[0]';
expandLinkTransResp: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
forwardToParent: 'CANCEL' | 'DISMISS';
loadMyVcs: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
loadThumbprint: 'FACE_VALID';
resetFlowType: 'xstate.init';
resetLinkTransactionId: 'GET';
resetSelectedVc: 'xstate.init';
resetSelectedVoluntaryClaims: 'GET';
setClaims: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
setConsentClaims: 'TOGGLE_CONSENT_CLAIM';
setFaceAuthConsent: 'GET';
setLinkedTransactionId: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
setMyVcs: 'STORE_RESPONSE';
setScanData: 'GET';
setSelectedVc: 'SELECT_VC';
setShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
setThumbprint: 'STORE_RESPONSE';
setlinkTransactionResponse: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
storeShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
};
eventsCausingDelays: {};
eventsCausingGuards: {
isConsentAlreadyCaptured: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
isSimpleShareFlow:
| 'CANCEL'
| 'done.invoke.QrLogin.linkTransaction:invocation[0]';
showFaceAuthConsentScreen: 'VERIFY';
};
eventsCausingServices: {
linkTransaction: 'GET';
sendAuthenticate: 'STORE_RESPONSE';
sendConsent: 'CONFIRM';
};
matchesStates:
| 'ShowError'
| 'done'
| 'faceAuth'
| 'faceVerificationConsent'
| 'invalidIdentity'
| 'linkTransaction'
| 'loadMyVcs'
| 'loadingThumbprint'
| 'requestConsent'
| 'sendingAuthenticate'
| 'sendingConsent'
| 'showvcList'
| 'success'
| 'waitingForData';
tags: never;
}

View File

@@ -1,384 +1,123 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'': {type: ''};
'done.invoke.checkStatus': {
type: 'done.invoke.checkStatus';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.downloadCredential': {
type: 'done.invoke.downloadCredential';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]': {
type: 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]': {
type: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]': {
type: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]': {
type: 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.verifyingCredential:invocation[0]': {
type: 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]': {
type: 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]': {
type: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]': {
type: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]': {
type: 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]': {
type: 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.checkStatus': {
type: 'error.platform.checkStatus';
data: unknown;
};
'error.platform.downloadCredential': {
type: 'error.platform.downloadCredential';
data: unknown;
};
'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]': {
type: 'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.verifyingCredential:invocation[0]': {
type: 'error.platform.vc-item-machine.verifyingCredential:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]': {
type: 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]': {
type: 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]': {
type: 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]': {
type: 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]';
data: unknown;
};
'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]': {
type: 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
'xstate.stop': {type: 'xstate.stop'};
"": { type: "" };
"done.invoke.checkStatus": { type: "done.invoke.checkStatus"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.downloadCredential": { type: "done.invoke.downloadCredential"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]": { type: "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]": { type: "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]": { type: "done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]": { type: "done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.verifyingCredential:invocation[0]": { type: "done.invoke.vc-item-machine.verifyingCredential:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]": { type: "done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]": { type: "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]": { type: "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]": { type: "done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]": { type: "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.checkStatus": { type: "error.platform.checkStatus"; data: unknown };
"error.platform.downloadCredential": { type: "error.platform.downloadCredential"; data: unknown };
"error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]": { type: "error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.verifyingCredential:invocation[0]": { type: "error.platform.vc-item-machine.verifyingCredential:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]": { type: "error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]": { type: "error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]": { type: "error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]": { type: "error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]"; data: unknown };
"error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]": { type: "error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]"; data: unknown };
"xstate.init": { type: "xstate.init" };
"xstate.stop": { type: "xstate.stop" };
};
invokeSrcNameMap: {
addWalletBindingId: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
checkDownloadExpiryLimit: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]';
checkStatus: 'done.invoke.checkStatus';
downloadCredential: 'done.invoke.downloadCredential';
generateKeyPair: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
isUserSignedAlready:
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'
| 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]';
loadDownloadLimitConfig: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
requestBindingOTP:
| 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]';
updatePrivateKey: 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
verifyCredential: 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]';
"addWalletBindingId": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]";
"checkDownloadExpiryLimit": "done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
"checkStatus": "done.invoke.checkStatus";
"downloadCredential": "done.invoke.downloadCredential";
"generateKeyPair": "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]";
"isUserSignedAlready": "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
"loadDownloadLimitConfig": "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
"requestBindingOTP": "done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]";
"updatePrivateKey": "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"verifyCredential": "done.invoke.vc-item-machine.verifyingCredential:invocation[0]";
};
missingImplementations: {
actions:
| 'addVcToInProgressDownloads'
| 'closeViewVcModal'
| 'incrementDownloadCounter'
| 'logDownloaded'
| 'logRemovedVc'
| 'logWalletBindingFailure'
| 'logWalletBindingSuccess'
| 'refreshAllVcs'
| 'removeVcFromInProgressDownloads'
| 'removeVcItem'
| 'removeVcMetaDataFromStorage'
| 'removeVcMetaDataFromVcMachineContext'
| 'requestVcContext'
| 'resetIsMachineInKebabPopupState'
| 'resetPrivateKey'
| 'sendActivationStartEvent'
| 'sendActivationSuccessEvent'
| 'sendBackupEvent'
| 'sendDownloadLimitExpire'
| 'sendTelemetryEvents'
| 'sendUserCancelledActivationFailedEndEvent'
| 'sendVcUpdated'
| 'sendVerificationError'
| 'sendWalletBindingErrorEvent'
| 'sendWalletBindingSuccess'
| 'setCommunicationDetails'
| 'setContext'
| 'setDownloadInterval'
| 'setErrorAsVerificationError'
| 'setErrorAsWalletBindingError'
| 'setMaxDownloadCount'
| 'setOTP'
| 'setPinCard'
| 'setPrivateKey'
| 'setPublicKey'
| 'setThumbprintForWalletBindingId'
| 'setVcKey'
| 'setVcMetadata'
| 'setWalletBindingResponse'
| 'storeContext'
| 'storeVcInContext'
| 'unSetBindingTransactionId'
| 'unSetError'
| 'unSetOTP';
actions: "addVcToInProgressDownloads" | "closeViewVcModal" | "incrementDownloadCounter" | "logDownloaded" | "logRemovedVc" | "logWalletBindingFailure" | "logWalletBindingSuccess" | "refreshAllVcs" | "removeVcFromInProgressDownloads" | "removeVcItem" | "removeVcMetaDataFromStorage" | "removeVcMetaDataFromVcMachineContext" | "requestVcContext" | "resetIsMachineInKebabPopupState" | "resetPrivateKey" | "sendActivationStartEvent" | "sendActivationSuccessEvent" | "sendBackupEvent" | "sendDownloadLimitExpire" | "sendTelemetryEvents" | "sendUserCancelledActivationFailedEndEvent" | "sendVcUpdated" | "sendVerificationError" | "sendWalletBindingErrorEvent" | "sendWalletBindingSuccess" | "setCommunicationDetails" | "setContext" | "setDownloadInterval" | "setErrorAsVerificationError" | "setErrorAsWalletBindingError" | "setMaxDownloadCount" | "setOTP" | "setPinCard" | "setPrivateKey" | "setPublicKey" | "setThumbprintForWalletBindingId" | "setVcKey" | "setVcMetadata" | "setWalletBindingResponse" | "storeContext" | "storeVcInContext" | "unSetBindingTransactionId" | "unSetError" | "unSetOTP";
delays: never;
guards:
| 'hasCredential'
| 'isCustomSecureKeystore'
| 'isDownloadAllowed'
| 'isSignedIn';
services:
| 'addWalletBindingId'
| 'checkDownloadExpiryLimit'
| 'checkStatus'
| 'downloadCredential'
| 'generateKeyPair'
| 'isUserSignedAlready'
| 'loadDownloadLimitConfig'
| 'requestBindingOTP'
| 'updatePrivateKey'
| 'verifyCredential';
guards: "hasCredential" | "isCustomSecureKeystore" | "isDownloadAllowed" | "isSignedIn";
services: "addWalletBindingId" | "checkDownloadExpiryLimit" | "checkStatus" | "downloadCredential" | "generateKeyPair" | "isUserSignedAlready" | "loadDownloadLimitConfig" | "requestBindingOTP" | "updatePrivateKey" | "verifyCredential";
};
eventsCausingActions: {
addVcToInProgressDownloads: 'GET_VC_RESPONSE';
closeViewVcModal: 'CLOSE_VC_MODAL' | 'STORE_RESPONSE';
incrementDownloadCounter:
| 'POLL'
| 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
logDownloaded: 'STORE_RESPONSE';
logRemovedVc:
| 'STORE_RESPONSE'
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]';
logWalletBindingFailure:
| 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
logWalletBindingSuccess:
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
refreshAllVcs:
| 'STORE_RESPONSE'
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]';
removeVcFromInProgressDownloads: 'STORE_RESPONSE';
removeVcItem: 'CONFIRM';
removeVcMetaDataFromStorage:
| 'STORE_ERROR'
| 'error.platform.vc-item-machine.verifyingCredential:invocation[0]';
removeVcMetaDataFromVcMachineContext: 'DISMISS';
requestVcContext: 'DISMISS' | 'REFRESH' | 'STORE_ERROR' | 'xstate.init';
resetIsMachineInKebabPopupState:
| ''
| 'ADD_WALLET_BINDING_ID'
| 'CANCEL'
| 'CLOSE_VC_MODAL'
| 'DISMISS'
| 'REFRESH'
| 'REMOVE'
| 'SHOW_ACTIVITY'
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'
| 'xstate.stop';
resetPrivateKey:
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
sendActivationStartEvent: 'CONFIRM';
sendActivationSuccessEvent:
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
sendBackupEvent:
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'
| 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]';
sendDownloadLimitExpire:
| 'FAILED'
| 'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]';
sendTelemetryEvents: 'STORE_RESPONSE';
sendUserCancelledActivationFailedEndEvent: 'DISMISS';
sendVcUpdated: 'PIN_CARD';
sendVerificationError: 'STORE_RESPONSE';
sendWalletBindingErrorEvent:
| 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
sendWalletBindingSuccess: 'SHOW_BINDING_STATUS';
setCommunicationDetails:
| 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]';
setContext: 'CREDENTIAL_DOWNLOADED' | 'GET_VC_RESPONSE';
setDownloadInterval: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
setErrorAsVerificationError: 'error.platform.vc-item-machine.verifyingCredential:invocation[0]';
setErrorAsWalletBindingError:
| 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'
| 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
setMaxDownloadCount: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
setOTP: 'INPUT_OTP';
setPinCard: 'PIN_CARD';
setPrivateKey: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
setPublicKey: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
setThumbprintForWalletBindingId:
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
setVcKey: 'REMOVE';
setVcMetadata: 'UPDATE_VC_METADATA';
setWalletBindingResponse: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
storeContext:
| 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
storeVcInContext:
| 'STORE_RESPONSE'
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
unSetBindingTransactionId: 'DISMISS';
unSetError:
| 'CANCEL'
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]';
unSetOTP:
| 'DISMISS'
| 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]';
"addVcToInProgressDownloads": "GET_VC_RESPONSE";
"closeViewVcModal": "CLOSE_VC_MODAL" | "STORE_RESPONSE";
"incrementDownloadCounter": "POLL" | "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
"logDownloaded": "STORE_RESPONSE";
"logRemovedVc": "STORE_RESPONSE" | "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]";
"logWalletBindingFailure": "error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"logWalletBindingSuccess": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"refreshAllVcs": "STORE_RESPONSE" | "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]";
"removeVcFromInProgressDownloads": "STORE_RESPONSE";
"removeVcItem": "CONFIRM";
"removeVcMetaDataFromStorage": "STORE_ERROR" | "error.platform.vc-item-machine.verifyingCredential:invocation[0]";
"removeVcMetaDataFromVcMachineContext": "DISMISS";
"requestVcContext": "DISMISS" | "REFRESH" | "STORE_ERROR" | "xstate.init";
"resetIsMachineInKebabPopupState": "" | "ADD_WALLET_BINDING_ID" | "CANCEL" | "CLOSE_VC_MODAL" | "DISMISS" | "REFRESH" | "REMOVE" | "SHOW_ACTIVITY" | "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]" | "xstate.stop";
"resetPrivateKey": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"sendActivationStartEvent": "CONFIRM";
"sendActivationSuccessEvent": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"sendBackupEvent": "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
"sendDownloadLimitExpire": "FAILED" | "error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
"sendTelemetryEvents": "STORE_RESPONSE";
"sendUserCancelledActivationFailedEndEvent": "DISMISS";
"sendVcUpdated": "PIN_CARD";
"sendVerificationError": "STORE_RESPONSE";
"sendWalletBindingErrorEvent": "error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"sendWalletBindingSuccess": "SHOW_BINDING_STATUS";
"setCommunicationDetails": "done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]";
"setContext": "CREDENTIAL_DOWNLOADED" | "GET_VC_RESPONSE";
"setDownloadInterval": "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
"setErrorAsVerificationError": "error.platform.vc-item-machine.verifyingCredential:invocation[0]";
"setErrorAsWalletBindingError": "error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"setMaxDownloadCount": "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
"setOTP": "INPUT_OTP";
"setPinCard": "PIN_CARD";
"setPrivateKey": "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]";
"setPublicKey": "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]";
"setThumbprintForWalletBindingId": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"setVcKey": "REMOVE";
"setVcMetadata": "UPDATE_VC_METADATA";
"setWalletBindingResponse": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]";
"storeContext": "done.invoke.vc-item-machine.verifyingCredential:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"storeVcInContext": "STORE_RESPONSE" | "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"unSetBindingTransactionId": "DISMISS";
"unSetError": "CANCEL" | "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]";
"unSetOTP": "DISMISS" | "done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]";
};
eventsCausingDelays: {
};
eventsCausingDelays: {};
eventsCausingGuards: {
hasCredential: 'GET_VC_RESPONSE';
isCustomSecureKeystore:
| 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'
| 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
isDownloadAllowed: 'POLL';
isSignedIn:
| 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'
| 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]';
"hasCredential": "GET_VC_RESPONSE";
"isCustomSecureKeystore": "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]" | "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]";
"isDownloadAllowed": "POLL";
"isSignedIn": "done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
};
eventsCausingServices: {
addWalletBindingId: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]';
checkDownloadExpiryLimit:
| 'POLL'
| 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]';
checkStatus: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]';
downloadCredential: 'DOWNLOAD_READY';
generateKeyPair: 'INPUT_OTP';
isUserSignedAlready: 'STORE_RESPONSE';
loadDownloadLimitConfig: 'GET_VC_RESPONSE' | 'STORE_ERROR';
requestBindingOTP: 'CONFIRM' | 'RESEND_OTP';
updatePrivateKey: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]';
verifyCredential: 'CREDENTIAL_DOWNLOADED';
};
matchesStates:
| 'idle'
| 'kebabPopUp'
| 'kebabPopUp.idle'
| 'kebabPopUp.pinCard'
| 'kebabPopUp.removeWallet'
| 'kebabPopUp.removingVc'
| 'kebabPopUp.showActivities'
| 'kebabPopUp.triggerAutoBackup'
| 'loadVc'
| 'loadVc.loadVcFromContext'
| 'loadVc.loadVcFromServer'
| 'loadVc.loadVcFromServer.checkingStatus'
| 'loadVc.loadVcFromServer.downloadingCredential'
| 'loadVc.loadVcFromServer.loadDownloadLimitConfig'
| 'loadVc.loadVcFromServer.savingFailed'
| 'loadVc.loadVcFromServer.savingFailed.idle'
| 'loadVc.loadVcFromServer.savingFailed.viewingVc'
| 'loadVc.loadVcFromServer.verifyingDownloadLimitExpiry'
| 'verifyingCredential'
| 'verifyingCredential.handleVCVerificationFailure'
| 'verifyingCredential.idle'
| 'verifyingCredential.triggerAutoBackupForVcDownload'
| 'walletBinding'
| 'walletBinding.acceptingBindingOTP'
| 'walletBinding.acceptingBindingOTP.idle'
| 'walletBinding.acceptingBindingOTP.resendOTP'
| 'walletBinding.addKeyPair'
| 'walletBinding.addingWalletBindingId'
| 'walletBinding.requestingBindingOTP'
| 'walletBinding.showBindingWarning'
| 'walletBinding.showingWalletBindingError'
| 'walletBinding.updatingContextVariables'
| 'walletBinding.updatingPrivateKey'
| {
kebabPopUp?:
| 'idle'
| 'pinCard'
| 'removeWallet'
| 'removingVc'
| 'showActivities'
| 'triggerAutoBackup';
loadVc?:
| 'loadVcFromContext'
| 'loadVcFromServer'
| {
loadVcFromServer?:
| 'checkingStatus'
| 'downloadingCredential'
| 'loadDownloadLimitConfig'
| 'savingFailed'
| 'verifyingDownloadLimitExpiry'
| {savingFailed?: 'idle' | 'viewingVc'};
};
verifyingCredential?:
| 'handleVCVerificationFailure'
| 'idle'
| 'triggerAutoBackupForVcDownload';
walletBinding?:
| 'acceptingBindingOTP'
| 'addKeyPair'
| 'addingWalletBindingId'
| 'requestingBindingOTP'
| 'showBindingWarning'
| 'showingWalletBindingError'
| 'updatingContextVariables'
| 'updatingPrivateKey'
| {acceptingBindingOTP?: 'idle' | 'resendOTP'};
"addWalletBindingId": "done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]";
"checkDownloadExpiryLimit": "POLL" | "done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
"checkStatus": "done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
"downloadCredential": "DOWNLOAD_READY";
"generateKeyPair": "INPUT_OTP";
"isUserSignedAlready": "STORE_RESPONSE";
"loadDownloadLimitConfig": "GET_VC_RESPONSE" | "STORE_ERROR";
"requestBindingOTP": "CONFIRM" | "RESEND_OTP";
"updatePrivateKey": "done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]";
"verifyCredential": "CREDENTIAL_DOWNLOADED";
};
matchesStates: "idle" | "kebabPopUp" | "kebabPopUp.idle" | "kebabPopUp.pinCard" | "kebabPopUp.removeWallet" | "kebabPopUp.removingVc" | "kebabPopUp.showActivities" | "kebabPopUp.triggerAutoBackup" | "loadVc" | "loadVc.loadVcFromContext" | "loadVc.loadVcFromServer" | "loadVc.loadVcFromServer.checkingStatus" | "loadVc.loadVcFromServer.downloadingCredential" | "loadVc.loadVcFromServer.loadDownloadLimitConfig" | "loadVc.loadVcFromServer.savingFailed" | "loadVc.loadVcFromServer.savingFailed.idle" | "loadVc.loadVcFromServer.savingFailed.viewingVc" | "loadVc.loadVcFromServer.verifyingDownloadLimitExpiry" | "verifyingCredential" | "verifyingCredential.handleVCVerificationFailure" | "verifyingCredential.idle" | "verifyingCredential.triggerAutoBackupForVcDownload" | "walletBinding" | "walletBinding.acceptingBindingOTP" | "walletBinding.acceptingBindingOTP.idle" | "walletBinding.acceptingBindingOTP.resendOTP" | "walletBinding.addKeyPair" | "walletBinding.addingWalletBindingId" | "walletBinding.requestingBindingOTP" | "walletBinding.showBindingWarning" | "walletBinding.showingWalletBindingError" | "walletBinding.updatingContextVariables" | "walletBinding.updatingPrivateKey" | { "kebabPopUp"?: "idle" | "pinCard" | "removeWallet" | "removingVc" | "showActivities" | "triggerAutoBackup";
"loadVc"?: "loadVcFromContext" | "loadVcFromServer" | { "loadVcFromServer"?: "checkingStatus" | "downloadingCredential" | "loadDownloadLimitConfig" | "savingFailed" | "verifyingDownloadLimitExpiry" | { "savingFailed"?: "idle" | "viewingVc"; }; };
"verifyingCredential"?: "handleVCVerificationFailure" | "idle" | "triggerAutoBackupForVcDownload";
"walletBinding"?: "acceptingBindingOTP" | "addKeyPair" | "addingWalletBindingId" | "requestingBindingOTP" | "showBindingWarning" | "showingWalletBindingError" | "updatingContextVariables" | "updatingPrivateKey" | { "acceptingBindingOTP"?: "idle" | "resendOTP"; }; };
tags: never;
}

View File

@@ -1,106 +1,58 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]': {
type: 'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'xstate.init': {type: 'xstate.init'};
"done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]": { type: "done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
isUserSignedAlready: 'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]';
"isUserSignedAlready": "done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]";
};
missingImplementations: {
actions:
| 'addVcToInProgressDownloads'
| 'getVcItemResponse'
| 'loadMyVcs'
| 'loadReceivedVcs'
| 'logTamperedVCsremoved'
| 'prependToMyVcsMetadata'
| 'removeDownloadFailedVcsFromStorage'
| 'removeDownloadingFailedVcsFromMyVcs'
| 'removeVcFromInProgressDownlods'
| 'removeVcFromMyVcsMetadata'
| 'resetDownloadFailedVcs'
| 'resetInProgressVcsDownloaded'
| 'resetTamperedVcs'
| 'resetVerificationErrorMessage'
| 'resetWalletBindingSuccess'
| 'sendBackupEvent'
| 'setDownloadedVc'
| 'setDownloadingFailedVcs'
| 'setMyVcs'
| 'setReceivedVcs'
| 'setUpdatedVcMetadatas'
| 'setVerificationErrorMessage'
| 'setWalletBindingSuccess'
| 'updateMyVcsMetadata';
actions: "addVcToInProgressDownloads" | "getVcItemResponse" | "loadMyVcs" | "loadReceivedVcs" | "logTamperedVCsremoved" | "prependToMyVcsMetadata" | "removeDownloadFailedVcsFromStorage" | "removeDownloadingFailedVcsFromMyVcs" | "removeVcFromInProgressDownlods" | "removeVcFromMyVcsMetadata" | "resetDownloadFailedVcs" | "resetInProgressVcsDownloaded" | "resetTamperedVcs" | "resetVerificationErrorMessage" | "resetWalletBindingSuccess" | "sendBackupEvent" | "setDownloadedVc" | "setDownloadingFailedVcs" | "setMyVcs" | "setReceivedVcs" | "setUpdatedVcMetadatas" | "setVerificationErrorMessage" | "setWalletBindingSuccess" | "updateMyVcsMetadata";
delays: never;
guards: 'isAnyVcTampered' | 'isSignedIn';
services: 'isUserSignedAlready';
guards: "isAnyVcTampered" | "isSignedIn";
services: "isUserSignedAlready";
};
eventsCausingActions: {
addVcToInProgressDownloads: 'ADD_VC_TO_IN_PROGRESS_DOWNLOADS';
getVcItemResponse: 'GET_VC_ITEM';
loadMyVcs:
| 'REFRESH_MY_VCS'
| 'REFRESH_RECEIVED_VCS'
| 'STORE_RESPONSE'
| 'VERIFY_VC_FAILED'
| 'xstate.init';
loadReceivedVcs: 'REFRESH_RECEIVED_VCS' | 'STORE_RESPONSE';
logTamperedVCsremoved: 'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]';
prependToMyVcsMetadata: 'VC_ADDED';
removeDownloadFailedVcsFromStorage: 'DELETE_VC';
removeDownloadingFailedVcsFromMyVcs: 'STORE_RESPONSE';
removeVcFromInProgressDownlods:
| 'DOWNLOAD_LIMIT_EXPIRED'
| 'REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS'
| 'VERIFY_VC_FAILED';
removeVcFromMyVcsMetadata: 'REMOVE_VC_FROM_CONTEXT';
resetDownloadFailedVcs: 'STORE_RESPONSE';
resetInProgressVcsDownloaded: 'RESET_IN_PROGRESS_VCS_DOWNLOADED';
resetTamperedVcs: 'REMOVE_TAMPERED_VCS';
resetVerificationErrorMessage: 'RESET_VERIFY_ERROR';
resetWalletBindingSuccess: 'RESET_WALLET_BINDING_SUCCESS';
sendBackupEvent: 'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]';
setDownloadedVc: 'VC_DOWNLOADED';
setDownloadingFailedVcs: 'DOWNLOAD_LIMIT_EXPIRED';
setMyVcs: 'STORE_RESPONSE';
setReceivedVcs: 'STORE_RESPONSE';
setUpdatedVcMetadatas: 'VC_METADATA_UPDATED';
setVerificationErrorMessage: 'VERIFY_VC_FAILED';
setWalletBindingSuccess: 'WALLET_BINDING_SUCCESS';
updateMyVcsMetadata: 'VC_METADATA_UPDATED';
"addVcToInProgressDownloads": "ADD_VC_TO_IN_PROGRESS_DOWNLOADS";
"getVcItemResponse": "GET_VC_ITEM";
"loadMyVcs": "DOWNLOAD_LIMIT_EXPIRED" | "REFRESH_MY_VCS" | "REFRESH_RECEIVED_VCS" | "STORE_RESPONSE" | "VERIFY_VC_FAILED" | "xstate.init";
"loadReceivedVcs": "REFRESH_RECEIVED_VCS" | "STORE_RESPONSE";
"logTamperedVCsremoved": "done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]";
"prependToMyVcsMetadata": "VC_ADDED";
"removeDownloadFailedVcsFromStorage": "DELETE_VC";
"removeDownloadingFailedVcsFromMyVcs": "STORE_RESPONSE";
"removeVcFromInProgressDownlods": "DOWNLOAD_LIMIT_EXPIRED" | "REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS" | "VERIFY_VC_FAILED";
"removeVcFromMyVcsMetadata": "REMOVE_VC_FROM_CONTEXT";
"resetDownloadFailedVcs": "STORE_RESPONSE";
"resetInProgressVcsDownloaded": "RESET_IN_PROGRESS_VCS_DOWNLOADED";
"resetTamperedVcs": "REMOVE_TAMPERED_VCS";
"resetVerificationErrorMessage": "RESET_VERIFY_ERROR";
"resetWalletBindingSuccess": "RESET_WALLET_BINDING_SUCCESS";
"sendBackupEvent": "done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]";
"setDownloadedVc": "VC_DOWNLOADED";
"setDownloadingFailedVcs": "DOWNLOAD_LIMIT_EXPIRED";
"setMyVcs": "STORE_RESPONSE";
"setReceivedVcs": "STORE_RESPONSE";
"setUpdatedVcMetadatas": "VC_METADATA_UPDATED";
"setVerificationErrorMessage": "VERIFY_VC_FAILED";
"setWalletBindingSuccess": "WALLET_BINDING_SUCCESS";
"updateMyVcsMetadata": "VC_METADATA_UPDATED";
};
eventsCausingDelays: {
};
eventsCausingDelays: {};
eventsCausingGuards: {
isAnyVcTampered: 'SHOW_TAMPERED_POPUP';
isSignedIn: 'done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]';
"isAnyVcTampered": "SHOW_TAMPERED_POPUP";
"isSignedIn": "done.invoke.vcMeta.ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]";
};
eventsCausingServices: {
isUserSignedAlready: 'REMOVE_TAMPERED_VCS';
};
matchesStates:
| 'deletingFailedVcs'
| 'ready'
| 'ready.myVcs'
| 'ready.receivedVcs'
| 'ready.showTamperedPopup'
| 'ready.tamperedVCs'
| 'ready.tamperedVCs.idle'
| 'ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion'
| {
ready?:
| 'myVcs'
| 'receivedVcs'
| 'showTamperedPopup'
| 'tamperedVCs'
| {tamperedVCs?: 'idle' | 'triggerAutoBackupForTamperedVcDeletion'};
"isUserSignedAlready": "REMOVE_TAMPERED_VCS";
};
matchesStates: "deletingFailedVcs" | "ready" | "ready.myVcs" | "ready.receivedVcs" | "ready.showTamperedPopup" | "ready.tamperedVCs" | "ready.tamperedVCs.idle" | "ready.tamperedVCs.triggerAutoBackupForTamperedVcDeletion" | { "ready"?: "myVcs" | "receivedVcs" | "showTamperedPopup" | "tamperedVCs" | { "tamperedVCs"?: "idle" | "triggerAutoBackupForTamperedVcDeletion"; }; };
tags: never;
}

View File

@@ -0,0 +1,327 @@
import { Linking } from "react-native";
import { getDeviceNameSync } from "react-native-device-info";
import { assign, spawn, send, DoneInvokeEvent } from "xstate";
import { VCShareFlowType } from "../../../shared/Utils";
import { VCMetadata } from "../../../shared/VCMetadata";
import { logState } from "../../../shared/commonUtil";
import { SHOW_FACE_AUTH_CONSENT_SHARE_FLOW, isAndroid, DEFAULT_QR_HEADER, 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, getImpressionEventData, sendEndEvent, getEndEventData, sendErrorEvent, getErrorEventData, sendStartEvent, getStartEventData } from "../../../shared/telemetry/TelemetryUtils";
import { createQrLoginMachine } from "../../QrLogin/QrLoginMachine";
import { VcMetaEvents } from "../../VerifiableCredential/VCMetaMachine/VCMetaEvents";
import { ActivityLogEvents } from "../../activityLog";
import { StoreEvents } from "../../store";
import tuvali from '@mosip/tuvali';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
const {wallet, EventTypes, VerificationStatus} = tuvali;
export const ScanActions =(model:any,QR_LOGIN_REF_ID:any)=>{
return{
setChildRef: assign({
QrLoginRef: (context:any) => {
const service = spawn(
createQrLoginMachine(context.serviceRefs),
QR_LOGIN_REF_ID,
);
service.subscribe(logState);
return service;
},
}),
updateShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return event.response || event.response === null;
},
}),
setShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return !event.isDoNotAskAgainChecked;
},
}),
getFaceAuthConsent: send(StoreEvents.GET(SHOW_FACE_AUTH_CONSENT_SHARE_FLOW), {
to: (context:any) => context.serviceRefs.store,
}),
storeShowFaceAuthConsent: send(
(context, event) =>
StoreEvents.SET(SHOW_FACE_AUTH_CONSENT_SHARE_FLOW, !event.isDoNotAskAgainChecked),
{
to: (context:any) => context.serviceRefs.store,
},
),
sendScanData: context =>
context.QrLoginRef.send({
type: 'GET',
linkCode: context.linkCode,
flowType: context.flowType,
selectedVc: context.selectedVc,
}),
openBluetoothSettings: () => {
isAndroid()
? BluetoothStateManager.openSettings().catch()
: Linking.openURL('App-Prefs:Bluetooth');
},
openAppPermission: () => Linking.openSettings(),
enableLocation: async () => {
await Linking.sendIntent('android.settings.LOCATION_SOURCE_SETTINGS');
},
setUri: model.assign({
openId4VpUri: (_context, event) => event.params,
}),
clearUri: assign({
openId4VpUri: '',
}),
setSenderInfo: assign({
senderInfo: () => {
return {name: 'Wallet', deviceName: 'Wallet', deviceId: ''};
},
}),
setReceiverInfo: assign({
receiverInfo: () => {
return {name: 'Verifier', deviceName: 'Verifier', deviceId: ''};
},
}),
setReadyForBluetoothStateCheck: model.assign({
readyForBluetoothStateCheck: () => true,
}),
setBleError: assign({
bleError: (_context, event) => event.bleError,
}),
setSelectedVc: assign({
selectedVc: (_context, event) => event.vc,
}),
resetSelectedVc: assign({
selectedVc: {},
}),
resetShowQuickShareSuccessBanner: assign({
showQuickShareSuccessBanner: false,
}),
setShowQuickShareSuccessBanner: assign({
showQuickShareSuccessBanner: true,
}),
setFlowType: assign({
flowType: (_context, event) => event.flowType,
}),
resetFlowType: assign({
flowType: VCShareFlowType.SIMPLE_SHARE,
}),
registerLoggers: assign({
loggers: () => {
if (__DEV__) {
return [
wallet.handleDataEvents(event => {
console.log(
getDeviceNameSync(),
'<Sender.Event>',
JSON.stringify(event).slice(0, 100),
);
}),
];
} else {
return [];
}
},
}),
removeLoggers: assign({
loggers: ({loggers}) => {
loggers?.forEach(logger => logger.remove());
return [];
},
}),
setShareLogTypeUnverified: model.assign({
shareLogType: 'VC_SHARED',
}),
setShareLogTypeVerified: model.assign({
shareLogType: 'PRESENCE_VERIFIED_AND_VC_SHARED',
}),
updateFaceCaptureBannerStatus: model.assign({
showFaceCaptureSuccessBanner: true,
}),
resetFaceCaptureBannerStatus: model.assign({
showFaceCaptureSuccessBanner: false,
}),
logShared: send(
(context:any) => {
const vcMetadata = context.selectedVc?.vcMetadata;
return ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VCMetadata.fromVC(vcMetadata).getVcKey(),
type: context.shareLogType
? context.shareLogType
: 'VC_SHARED_WITH_VERIFICATION_CONSENT',
id: vcMetadata.id,
idType: getIdType(vcMetadata.issuer),
timestamp: Date.now(),
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: vcMetadata.id,
});
},
{to: context => context.serviceRefs.activityLog},
),
logFailedVerification: send(
context =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VCMetadata.fromVC(context.selectedVc).getVcKey(),
type: 'PRESENCE_VERIFICATION_FAILED',
timestamp: Date.now(),
idType: getIdType(context.selectedVc.vcMetadata.issuer),
id: context.selectedVc.vcMetadata.id,
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: context.selectedVc.vcMetadata.id,
}),
{to: context => context.serviceRefs.activityLog},
),
setLinkCode: assign({
linkCode: (_, event) =>
new URL(event.params).searchParams.get('linkCode'),
}),
setQuickShareData: assign({
quickShareData: (_, event) =>
JSON.parse(decodeData(event.params.split(DEFAULT_QR_HEADER)[1])),
}),
loadMetaDataToMemory: send(
(context:any) => {
let metadata = VCMetadata.fromVC(context.quickShareData?.meta);
return StoreEvents.PREPEND(MY_VCS_STORE_KEY, metadata);
},
{to: context => context.serviceRefs.store},
),
loadVCDataToMemory: send(
(context:any) => {
let metadata = VCMetadata.fromVC(context.quickShareData?.meta);
let verifiableCredential = metadata.isFromOpenId4VCI()
? {credential: context.quickShareData?.verifiableCredential}
: context.quickShareData?.verifiableCredential;
return StoreEvents.SET(metadata.getVcKey(), {
verifiableCredential: verifiableCredential,
});
},
{to: context => context.serviceRefs.store},
),
refreshVCs: send(VcMetaEvents.REFRESH_MY_VCS, {
to: context => context.serviceRefs.vcMeta,
}),
storeLoginItem: send(
(_context, event) => {
return StoreEvents.PREPEND(
MY_LOGIN_STORE_KEY,
(event as DoneInvokeEvent<string>).data,
);
},
{to: (context:any) => context.serviceRefs.store},
),
storingActivityLog: send(
(_, event) =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: '',
id: event.response.selectedVc.vcMetadata.id,
idType: getIdType(event.response.selectedVc.vcMetadata.issuer),
type: 'QRLOGIN_SUCCESFULL',
timestamp: Date.now(),
deviceName: '',
vcLabel: String(event.response.selectedVc.vcMetadata.id),
}),
{
to: (context:any) => context.serviceRefs.activityLog,
},
),
sendVcShareSuccessEvent: () => {
sendImpressionEvent(
getImpressionEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.Screens.vcShareSuccessPage,
),
);
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.success,
),
);
},
sendBLEConnectionErrorEvent: (_context, event) => {
sendErrorEvent(
getErrorEventData(
TelemetryConstants.FlowType.senderVcShare,
event.bleError.code,
event.bleError.message,
),
);
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.failure,
),
);
},
sendVcSharingStartEvent: () => {
sendStartEvent(
getStartEventData(TelemetryConstants.FlowType.senderVcShare),
);
sendImpressionEvent(
getImpressionEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.Screens.scanScreen,
),
);
},
sendVCShareFlowCancelEndEvent: () => {
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.cancel,
{comment: 'User cancelled VC share'},
),
);
},
sendVCShareFlowTimeoutEndEvent: () => {
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.failure,
{comment: 'VC sharing timeout'},
),
);
},
}
}

View File

@@ -0,0 +1,45 @@
import { VCShareFlowType } from "../../../shared/Utils";
import { androidVersion, isAndroid, isIOS } from "../../../shared/constants";
export const ScanGuards = () => {
return {
showFaceAuthConsentScreen: context => {
return context.showFaceAuthConsent;
},
// sample: 'OPENID4VP://connect:?name=OVPMOSIP&key=69dc92a2cc91f02258aa8094d6e2b62877f5b6498924fbaedaaa46af30abb364'
isOpenIdQr: (_context, event) =>
event.params.startsWith('OPENID4VP://'),
// sample: 'INJIQUICKSHARE://NAKDFK:DB:JAHDIHAIDJXKABDAJDHUHW'
isQuickShare: (_context, event) =>
// event.params.startsWith(DEFAULT_QR_HEADER),
// toggling the feature for now
false,
isQrLogin: (context, event) => {
try {
let linkCode = new URL(event.params);
// sample: 'inji://landing-page-name?linkCode=sTjp0XVH3t3dGCU&linkExpireDateTime=2023-11-09T06:56:18.482Z'
return linkCode.searchParams.get('linkCode') !== null;
} catch (e) {
return false;
}
},
uptoAndroid11: () => isAndroid() && androidVersion < 31,
isIOS: () => isIOS(),
isMinimumStorageRequiredForAuditEntryReached: (_context, event) =>
Boolean(event.data),
isFlowTypeMiniViewShareWithSelfie: context =>
context.flowType === VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE,
isFlowTypeMiniViewShare: context =>
context.flowType === VCShareFlowType.MINI_VIEW_SHARE,
isFlowTypeSimpleShare: context =>
context.flowType === VCShareFlowType.SIMPLE_SHARE,
};
};

View File

@@ -1,141 +1,23 @@
/* eslint-disable sonarjs/no-duplicate-string */
import tuvali from '@mosip/tuvali';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
import {
ActorRefFrom,
assign,
DoneInvokeEvent,
EventFrom,
send,
spawn,
StateFrom,
StateFrom
} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {EmitterSubscription, Linking} from 'react-native';
import {DeviceInfo} from '../../../components/DeviceInfoList';
import {getDeviceNameSync, isLocationEnabled} from 'react-native-device-info';
import {VC} from '../../VerifiableCredential/VCMetaMachine/vc';
import { AppServices } from '../../../shared/GlobalContext';
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
import {
androidVersion,
DEFAULT_QR_HEADER,
FACE_AUTH_CONSENT,
isAndroid,
isIOS,
MY_LOGIN_STORE_KEY,
MY_VCS_STORE_KEY,
} from '../../../shared/constants';
import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
import {
check,
checkMultiple,
PERMISSIONS,
PermissionStatus,
requestMultiple,
RESULTS,
} from 'react-native-permissions';
import {
checkLocationPermissionStatus,
requestLocationPermission,
} from '../../../shared/location';
import {CameraCapturedPicture} from 'expo-camera';
import {createQrLoginMachine, qrLoginMachine} from '../../QrLoginMachine';
import {StoreEvents} from '../../store';
import {WalletDataEvent} from '@mosip/tuvali/lib/typescript/types/events';
import {BLEError} from '../types';
import Storage from '../../../shared/storage';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
getEndEventData,
getErrorEventData,
getImpressionEventData,
getStartEventData,
sendEndEvent,
sendErrorEvent,
sendImpressionEvent,
sendStartEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import { TelemetryConstants } from '../../../shared/telemetry/TelemetryConstants';
import {logState} from '../../../shared/commonUtil';
import {VCShareFlowType} from '../../../shared/Utils';
import {getIdType} from '../../../shared/openId4VCI/Utils';
import {VcMetaEvents} from '../../VerifiableCredential/VCMetaMachine/VCMetaMachine';
import {
getStartEventData,
sendStartEvent
} from '../../../shared/telemetry/TelemetryUtils';
import { qrLoginMachine } from '../../QrLogin/QrLoginMachine';
// @ts-ignore
import {decodeData} from '@mosip/pixelpass';
import { ScanActions } from './scanActions';
import { ScanGuards } from './scanGuards';
import { ScanModel } from './scanModel';
import { ScanServices } from './scanServices';
const {wallet, EventTypes, VerificationStatus} = tuvali;
const model = createModel(
{
serviceRefs: {} as AppServices,
senderInfo: {} as DeviceInfo,
receiverInfo: {} as DeviceInfo,
selectedVc: {} as VC,
bleError: {} as BLEError,
loggers: [] as EmitterSubscription[],
vcName: '',
flowType: VCShareFlowType.SIMPLE_SHARE,
verificationImage: {} as CameraCapturedPicture,
openId4VpUri: '',
shareLogType: '' as ActivityLogType,
QrLoginRef: {} as ActorRefFrom<typeof qrLoginMachine>,
showQuickShareSuccessBanner: false,
linkCode: '',
quickShareData: {},
showFaceAuthConsent: true as boolean,
readyForBluetoothStateCheck: false,
showFaceCaptureSuccessBanner: false,
},
{
events: {
SELECT_VC: (vc: VC, flowType: string) => ({vc, flowType}),
SCAN: (params: string) => ({params}),
ACCEPT_REQUEST: () => ({}),
VERIFY_AND_ACCEPT_REQUEST: () => ({}),
VC_ACCEPTED: () => ({}),
VC_REJECTED: () => ({}),
VC_SENT: () => ({}),
CANCEL: () => ({}),
CLOSE_BANNER: () => ({}),
STAY_IN_PROGRESS: () => ({}),
RETRY: () => ({}),
DISMISS: () => ({}),
DISMISS_QUICK_SHARE_BANNER: () => ({}),
GOTO_HISTORY: () => ({}),
CONNECTED: () => ({}),
DISCONNECT: () => ({}),
BLE_ERROR: (bleError: BLEError) => ({bleError}),
CONNECTION_DESTROYED: () => ({}),
SCREEN_BLUR: () => ({}),
SCREEN_FOCUS: () => ({}),
BLUETOOTH_PERMISSION_ENABLED: () => ({}),
BLUETOOTH_PERMISSION_DENIED: () => ({}),
BLUETOOTH_STATE_ENABLED: () => ({}),
BLUETOOTH_STATE_DISABLED: () => ({}),
NEARBY_ENABLED: () => ({}),
NEARBY_DISABLED: () => ({}),
GOTO_SETTINGS: () => ({}),
START_PERMISSION_CHECK: () => ({}),
LOCATION_ENABLED: () => ({}),
LOCATION_DISABLED: () => ({}),
LOCATION_REQUEST: () => ({}),
CHECK_FLOW_TYPE: () => ({}),
UPDATE_VC_NAME: (vcName: string) => ({vcName}),
STORE_RESPONSE: (response: any) => ({response}),
APP_ACTIVE: () => ({}),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
RESET: () => ({}),
FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) => ({
isConsentGiven,
}),
ALLOWED: () => ({}),
DENIED: () => ({}),
},
},
);
const model=ScanModel;
const QR_LOGIN_REF_ID = 'QrLogin';
export const ScanEvents = model.events;
@@ -167,6 +49,7 @@ export const scanMachine =
actions: ['sendBLEConnectionErrorEvent', 'setBleError'],
},
RESET: {
actions:['removeLoggers', 'resetFlowType', 'resetSelectedVc'],
target: '.checkStorage',
},
DISMISS: {
@@ -562,7 +445,7 @@ export const scanMachine =
},
{
cond: 'isFlowTypeMiniViewShareWithSelfie',
target: '.verifyingIdentity',
target:'.checkFaceAuthConsentForMiniView' ,
},
],
},
@@ -701,6 +584,16 @@ export const scanMachine =
entry: ['resetFlowType', 'resetSelectedVc'],
always: '#scan.disconnected',
},
checkFaceAuthConsentForMiniView: {
always:[
{
cond: 'showFaceAuthConsentScreen',
target: 'faceVerificationConsent'
},
{
target: 'verifyingIdentity'
}
],},
faceVerificationConsent: {
on: {
FACE_VERIFICATION_CONSENT: {
@@ -710,9 +603,15 @@ export const scanMachine =
],
target: 'verifyingIdentity',
},
DISMISS: {
target: '#scan.reviewing.selectingVc',
DISMISS:[
{
cond: 'isFlowTypeMiniViewShareWithSelfie',
target: '#scan.checkFaceAuthConsent',
},
{
target: '#scan.reviewing.selectingVc',
}
],
},
},
verifyingIdentity: {
@@ -860,534 +759,19 @@ export const scanMachine =
},
},
{
actions: {
setChildRef: assign({
QrLoginRef: context => {
const service = spawn(
createQrLoginMachine(context.serviceRefs),
QR_LOGIN_REF_ID,
);
service.subscribe(logState);
return service;
},
}),
actions: ScanActions(model,QR_LOGIN_REF_ID),
updateShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return event.response || event.response === null;
},
}),
setShowFaceAuthConsent: model.assign({
showFaceAuthConsent: (_, event) => {
return !event.isConsentGiven;
},
}),
getFaceAuthConsent: send(StoreEvents.GET(FACE_AUTH_CONSENT), {
to: context => context.serviceRefs.store,
}),
storeShowFaceAuthConsent: send(
(context, event) =>
StoreEvents.SET(FACE_AUTH_CONSENT, !event.isConsentGiven),
{
to: context => context.serviceRefs.store,
},
),
sendScanData: context =>
context.QrLoginRef.send({
type: 'GET',
linkCode: context.linkCode,
flowType: context.flowType,
selectedVc: context.selectedVc,
faceAuthConsentGiven: context.showFaceAuthConsent,
}),
openBluetoothSettings: () => {
isAndroid()
? BluetoothStateManager.openSettings().catch()
: Linking.openURL('App-Prefs:Bluetooth');
},
openAppPermission: () => Linking.openSettings(),
enableLocation: async () => {
await Linking.sendIntent('android.settings.LOCATION_SOURCE_SETTINGS');
},
setUri: model.assign({
openId4VpUri: (_context, event) => event.params,
}),
clearUri: assign({
openId4VpUri: '',
}),
setSenderInfo: assign({
senderInfo: () => {
return {name: 'Wallet', deviceName: 'Wallet', deviceId: ''};
},
}),
setReceiverInfo: assign({
receiverInfo: () => {
return {name: 'Verifier', deviceName: 'Verifier', deviceId: ''};
},
}),
setReadyForBluetoothStateCheck: model.assign({
readyForBluetoothStateCheck: () => true,
}),
setBleError: assign({
bleError: (_context, event) => event.bleError,
}),
setSelectedVc: assign({
selectedVc: (_context, event) => event.vc,
}),
resetSelectedVc: assign({
selectedVc: {},
}),
resetShowQuickShareSuccessBanner: assign({
showQuickShareSuccessBanner: false,
}),
setShowQuickShareSuccessBanner: assign({
showQuickShareSuccessBanner: true,
}),
setFlowType: assign({
flowType: (_context, event) => event.flowType,
}),
resetFlowType: assign({
flowType: VCShareFlowType.SIMPLE_SHARE,
}),
registerLoggers: assign({
loggers: () => {
if (__DEV__) {
return [
wallet.handleDataEvents(event => {
console.log(
getDeviceNameSync(),
'<Sender.Event>',
JSON.stringify(event).slice(0, 100),
);
}),
];
} else {
return [];
}
},
}),
removeLoggers: assign({
loggers: ({loggers}) => {
loggers?.forEach(logger => logger.remove());
return [];
},
}),
setShareLogTypeUnverified: model.assign({
shareLogType: 'VC_SHARED',
}),
setShareLogTypeVerified: model.assign({
shareLogType: 'PRESENCE_VERIFIED_AND_VC_SHARED',
}),
updateFaceCaptureBannerStatus: model.assign({
showFaceCaptureSuccessBanner: true,
}),
resetFaceCaptureBannerStatus: model.assign({
showFaceCaptureSuccessBanner: false,
}),
logShared: send(
context => {
const vcMetadata = context.selectedVc?.vcMetadata;
return ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VCMetadata.fromVC(vcMetadata).getVcKey(),
type: context.shareLogType
? context.shareLogType
: 'VC_SHARED_WITH_VERIFICATION_CONSENT',
id: vcMetadata.id,
idType: getIdType(vcMetadata.issuer),
timestamp: Date.now(),
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: vcMetadata.id,
});
},
{to: context => context.serviceRefs.activityLog},
),
logFailedVerification: send(
context =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VCMetadata.fromVC(context.selectedVc).getVcKey(),
type: 'PRESENCE_VERIFICATION_FAILED',
timestamp: Date.now(),
idType: getIdType(context.selectedVc.vcMetadata.issuer),
id: context.selectedVc.vcMetadata.id,
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: context.selectedVc.vcMetadata.id,
}),
{to: context => context.serviceRefs.activityLog},
),
setLinkCode: assign({
linkCode: (_, event) =>
new URL(event.params).searchParams.get('linkCode'),
}),
setQuickShareData: assign({
quickShareData: (_, event) =>
JSON.parse(decodeData(event.params.split(DEFAULT_QR_HEADER)[1])),
}),
loadMetaDataToMemory: send(
context => {
let metadata = VCMetadata.fromVC(context.quickShareData?.meta);
return StoreEvents.PREPEND(MY_VCS_STORE_KEY, metadata);
},
{to: context => context.serviceRefs.store},
),
loadVCDataToMemory: send(
context => {
let metadata = VCMetadata.fromVC(context.quickShareData?.meta);
let verifiableCredential = metadata.isFromOpenId4VCI()
? {credential: context.quickShareData?.verifiableCredential}
: context.quickShareData?.verifiableCredential;
return StoreEvents.SET(metadata.getVcKey(), {
verifiableCredential: verifiableCredential,
});
},
{to: context => context.serviceRefs.store},
),
refreshVCs: send(VcMetaEvents.REFRESH_MY_VCS, {
to: context => context.serviceRefs.vcMeta,
}),
storeLoginItem: send(
(_context, event) => {
return StoreEvents.PREPEND(
MY_LOGIN_STORE_KEY,
(event as DoneInvokeEvent<string>).data,
);
},
{to: context => context.serviceRefs.store},
),
storingActivityLog: send(
(_, event) =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: '',
id: event.response.selectedVc.vcMetadata.id,
idType: getIdType(event.response.selectedVc.vcMetadata.issuer),
type: 'QRLOGIN_SUCCESFULL',
timestamp: Date.now(),
deviceName: '',
vcLabel: String(event.response.selectedVc.vcMetadata.id),
}),
{
to: context => context.serviceRefs.activityLog,
},
),
sendVcShareSuccessEvent: () => {
sendImpressionEvent(
getImpressionEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.Screens.vcShareSuccessPage,
),
);
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.success,
),
);
},
sendBLEConnectionErrorEvent: (_context, event) => {
sendErrorEvent(
getErrorEventData(
TelemetryConstants.FlowType.senderVcShare,
event.bleError.code,
event.bleError.message,
),
);
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.failure,
),
);
},
sendVcSharingStartEvent: () => {
sendStartEvent(
getStartEventData(TelemetryConstants.FlowType.senderVcShare),
);
sendImpressionEvent(
getImpressionEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.Screens.scanScreen,
),
);
},
sendVCShareFlowCancelEndEvent: () => {
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.cancel,
{comment: 'User cancelled VC share'},
),
);
},
sendVCShareFlowTimeoutEndEvent: () => {
sendEndEvent(
getEndEventData(
TelemetryConstants.FlowType.senderVcShare,
TelemetryConstants.EndEventStatus.failure,
{comment: 'VC sharing timeout'},
),
);
},
},
services: {
checkBluetoothPermission: () => async callback => {
// wait a bit for animation to finish when app becomes active
await new Promise(resolve => setTimeout(resolve, 250));
try {
// Passing Granted for android since permission status is always granted even if its denied.
let response: PermissionStatus = RESULTS.GRANTED;
if (isIOS()) {
response = await check(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL);
}
if (response === RESULTS.GRANTED) {
callback(model.events.BLUETOOTH_PERMISSION_ENABLED());
} else {
callback(model.events.BLUETOOTH_PERMISSION_DENIED());
}
} catch (e) {
console.error(e);
}
},
checkBluetoothState: () => callback => {
const subscription = BluetoothStateManager.onStateChange(state => {
if (state === 'PoweredOn') {
callback(model.events.BLUETOOTH_STATE_ENABLED());
} else {
callback(model.events.BLUETOOTH_STATE_DISABLED());
}
}, true);
return () => subscription.remove();
},
requestBluetooth: () => callback => {
BluetoothStateManager.requestToEnable()
.then(() => callback(model.events.BLUETOOTH_STATE_ENABLED()))
.catch(() => callback(model.events.BLUETOOTH_STATE_DISABLED()));
},
requestToEnableLocationPermission: () => callback => {
requestLocationPermission(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
monitorConnection: () => callback => {
const walletErrorCodePrefix = 'TVW';
const subscription = wallet.handleDataEvents(event => {
if (event.type === EventTypes.onDisconnected) {
callback({type: 'DISCONNECT'});
}
if (
event.type === EventTypes.onError &&
event.code.includes(walletErrorCodePrefix)
) {
callback({
type: 'BLE_ERROR',
bleError: {message: event.message, code: event.code},
});
console.error('BLE Exception: ' + event.message);
}
});
return () => subscription.remove();
},
checkNearByDevicesPermission: () => callback => {
checkMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
requestNearByDevicesPermission: () => callback => {
requestMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
checkLocationPermission: () => callback => {
checkLocationPermissionStatus(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
checkLocationStatus: () => async callback => {
const isEnabled: boolean = await isLocationEnabled();
if (isEnabled) {
callback(model.events.LOCATION_ENABLED());
} else {
callback(model.events.LOCATION_DISABLED());
}
},
startConnection: context => callback => {
wallet.startConnection(context.openId4VpUri);
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onSecureChannelEstablished) {
callback({type: 'CONNECTED'});
}
};
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
sendVc: context => callback => {
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onDataSent) {
callback({type: 'VC_SENT'});
} else if (event.type === EventTypes.onVerificationStatusReceived) {
callback({
type:
event.status === VerificationStatus.ACCEPTED
? 'VC_ACCEPTED'
: 'VC_REJECTED',
});
}
};
wallet.sendData(
JSON.stringify({
...context.selectedVc,
}),
);
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
disconnect: () => () => {
try {
wallet.disconnect();
} catch (e) {
// pass
}
},
checkStorageAvailability: () => async () => {
return Promise.resolve(
Storage.isMinimumLimitReached('minStorageRequiredForAuditEntry'),
);
},
},
guards: {
showFaceAuthConsentScreen: context => {
return context.showFaceAuthConsent;
},
// sample: 'OPENID4VP://connect:?name=OVPMOSIP&key=69dc92a2cc91f02258aa8094d6e2b62877f5b6498924fbaedaaa46af30abb364'
isOpenIdQr: (_context, event) =>
event.params.startsWith('OPENID4VP://'),
// sample: 'INJIQUICKSHARE://NAKDFK:DB:JAHDIHAIDJXKABDAJDHUHW'
isQuickShare: (_context, event) =>
// event.params.startsWith(DEFAULT_QR_HEADER),
// toggling the feature for now
false,
isQrLogin: (context, event) => {
try {
let linkCode = new URL(event.params);
// sample: 'inji://landing-page-name?linkCode=sTjp0XVH3t3dGCU&linkExpireDateTime=2023-11-09T06:56:18.482Z'
return linkCode.searchParams.get('linkCode') !== null;
} catch (e) {
return false;
}
},
uptoAndroid11: () => isAndroid() && androidVersion < 31,
isIOS: () => isIOS(),
isMinimumStorageRequiredForAuditEntryReached: (_context, event) =>
Boolean(event.data),
isFlowTypeMiniViewShareWithSelfie: context =>
context.flowType === VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE,
isFlowTypeMiniViewShare: context =>
context.flowType === VCShareFlowType.MINI_VIEW_SHARE,
isFlowTypeSimpleShare: context =>
context.flowType === VCShareFlowType.SIMPLE_SHARE,
},
services: ScanServices(model),
guards: ScanGuards(),
delays: {
DESTROY_TIMEOUT: 500,
CONNECTION_TIMEOUT: 5 * 1000,
SHARING_TIMEOUT: 15 * 1000,
},
},
);
type State = StateFrom<typeof scanMachine>;
@@ -1399,12 +783,3 @@ export function createScanMachine(serviceRefs: AppServices) {
});
}
export function selectIsMinimumStorageRequiredForAuditEntryLimitReached(
state: State,
) {
return state.matches('restrictSharingVc');
}
export function selectIsFaceVerificationConsent(state: State) {
return state.matches('reviewing.faceVerificationConsent');
}

View File

@@ -1,238 +1,121 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'': {type: ''};
'done.invoke.QrLogin': {
type: 'done.invoke.QrLogin';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.scan.checkStorage:invocation[0]': {
type: 'done.invoke.scan.checkStorage:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection': {
type: 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
};
'xstate.init': {type: 'xstate.init'};
"": { type: "" };
"done.invoke.QrLogin": { type: "done.invoke.QrLogin"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.scan.checkStorage:invocation[0]": { type: "done.invoke.scan.checkStorage:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection": { type: "xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection" };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
checkBluetoothPermission: 'done.invoke.scan.checkBluetoothPermission.checking:invocation[0]';
checkBluetoothState:
| 'done.invoke.scan.checkBluetoothState.checking:invocation[0]'
| 'done.invoke.scan.recheckBluetoothState.checking:invocation[0]';
checkLocationPermission: 'done.invoke.scan.checkingLocationState.checkingPermissionStatus:invocation[0]';
checkLocationStatus: 'done.invoke.scan.checkingLocationState.checkLocationService:invocation[0]';
checkNearByDevicesPermission: 'done.invoke.scan.checkNearbyDevicesPermission.checking:invocation[0]';
checkStorageAvailability: 'done.invoke.scan.checkStorage:invocation[0]';
disconnect:
| 'done.invoke.scan.clearingConnection:invocation[0]'
| 'done.invoke.scan.disconnectDevice:invocation[0]'
| 'done.invoke.scan.reviewing.disconnect:invocation[0]';
monitorConnection: 'done.invoke.scan:invocation[0]';
requestBluetooth: 'done.invoke.scan.checkBluetoothState.requesting:invocation[0]';
requestNearByDevicesPermission: 'done.invoke.scan.checkNearbyDevicesPermission.requesting:invocation[0]';
requestToEnableLocationPermission: 'done.invoke.scan.checkingLocationState.requestToEnableLocation:invocation[0]';
sendVc: 'done.invoke.scan.reviewing.sendingVc:invocation[0]';
startConnection: 'done.invoke.scan.connecting:invocation[0]';
"checkBluetoothPermission": "done.invoke.scan.checkBluetoothPermission.checking:invocation[0]";
"checkBluetoothState": "done.invoke.scan.checkBluetoothState.checking:invocation[0]" | "done.invoke.scan.recheckBluetoothState.checking:invocation[0]";
"checkLocationPermission": "done.invoke.scan.checkingLocationState.checkingPermissionStatus:invocation[0]";
"checkLocationStatus": "done.invoke.scan.checkingLocationState.checkLocationService:invocation[0]";
"checkNearByDevicesPermission": "done.invoke.scan.checkNearbyDevicesPermission.checking:invocation[0]";
"checkStorageAvailability": "done.invoke.scan.checkStorage:invocation[0]";
"disconnect": "done.invoke.scan.clearingConnection:invocation[0]" | "done.invoke.scan.disconnectDevice:invocation[0]" | "done.invoke.scan.reviewing.disconnect:invocation[0]";
"monitorConnection": "done.invoke.scan:invocation[0]";
"requestBluetooth": "done.invoke.scan.checkBluetoothState.requesting:invocation[0]";
"requestNearByDevicesPermission": "done.invoke.scan.checkNearbyDevicesPermission.requesting:invocation[0]";
"requestToEnableLocationPermission": "done.invoke.scan.checkingLocationState.requestToEnableLocation:invocation[0]";
"sendVc": "done.invoke.scan.reviewing.sendingVc:invocation[0]";
"startConnection": "done.invoke.scan.connecting:invocation[0]";
};
missingImplementations: {
actions: never;
actions: "clearUri" | "enableLocation" | "getFaceAuthConsent" | "loadMetaDataToMemory" | "loadVCDataToMemory" | "logFailedVerification" | "logShared" | "openAppPermission" | "openBluetoothSettings" | "refreshVCs" | "registerLoggers" | "removeLoggers" | "resetFaceCaptureBannerStatus" | "resetFlowType" | "resetSelectedVc" | "resetShowQuickShareSuccessBanner" | "sendBLEConnectionErrorEvent" | "sendScanData" | "sendVCShareFlowCancelEndEvent" | "sendVCShareFlowTimeoutEndEvent" | "sendVcShareSuccessEvent" | "sendVcSharingStartEvent" | "setBleError" | "setChildRef" | "setFlowType" | "setLinkCode" | "setQuickShareData" | "setReadyForBluetoothStateCheck" | "setReceiverInfo" | "setSelectedVc" | "setSenderInfo" | "setShareLogTypeUnverified" | "setShareLogTypeVerified" | "setShowFaceAuthConsent" | "setShowQuickShareSuccessBanner" | "setUri" | "storeLoginItem" | "storeShowFaceAuthConsent" | "storingActivityLog" | "updateFaceCaptureBannerStatus" | "updateShowFaceAuthConsent";
delays: never;
guards: never;
services: never;
guards: "isFlowTypeMiniViewShare" | "isFlowTypeMiniViewShareWithSelfie" | "isFlowTypeSimpleShare" | "isIOS" | "isMinimumStorageRequiredForAuditEntryReached" | "isOpenIdQr" | "isQrLogin" | "isQuickShare" | "showFaceAuthConsentScreen" | "uptoAndroid11";
services: "checkBluetoothPermission" | "checkBluetoothState" | "checkLocationPermission" | "checkLocationStatus" | "checkNearByDevicesPermission" | "checkStorageAvailability" | "disconnect" | "monitorConnection" | "requestBluetooth" | "requestNearByDevicesPermission" | "requestToEnableLocationPermission" | "sendVc" | "startConnection";
};
eventsCausingActions: {
clearUri: 'STORE_RESPONSE';
getFaceAuthConsent:
| 'DISCONNECT'
| 'DISMISS'
| 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
loadMetaDataToMemory: 'SCAN';
loadVCDataToMemory: 'STORE_RESPONSE';
logFailedVerification: 'FACE_INVALID';
logShared: 'VC_ACCEPTED';
openAppPermission: 'GOTO_SETTINGS' | 'LOCATION_REQUEST';
openBluetoothSettings: 'GOTO_SETTINGS';
refreshVCs: 'STORE_RESPONSE';
registerLoggers: 'STORE_RESPONSE';
removeLoggers:
| 'DISCONNECT'
| 'DISMISS'
| 'DISMISS_QUICK_SHARE_BANNER'
| 'SCREEN_BLUR'
| 'STORE_RESPONSE'
| 'xstate.init';
resetFaceCaptureBannerStatus: 'ACCEPT_REQUEST' | 'CLOSE_BANNER';
resetFlowType:
| 'DISCONNECT'
| 'DISMISS'
| 'DISMISS_QUICK_SHARE_BANNER'
| 'GOTO_HISTORY'
| 'SCREEN_BLUR'
| 'xstate.init';
resetSelectedVc:
| 'DISCONNECT'
| 'DISMISS'
| 'DISMISS_QUICK_SHARE_BANNER'
| 'GOTO_HISTORY'
| 'SCREEN_BLUR'
| 'xstate.init';
resetShowQuickShareSuccessBanner: 'DISMISS' | 'DISMISS_QUICK_SHARE_BANNER';
sendBLEConnectionErrorEvent: 'BLE_ERROR';
sendScanData: 'SCAN';
sendVCShareFlowCancelEndEvent: 'CANCEL';
sendVCShareFlowTimeoutEndEvent: 'CANCEL' | 'RETRY';
sendVcShareSuccessEvent: 'VC_ACCEPTED';
sendVcSharingStartEvent: 'SCAN';
setBleError: 'BLE_ERROR';
setChildRef: 'STORE_RESPONSE';
setFlowType: 'SELECT_VC';
setLinkCode: 'SCAN';
setQuickShareData: 'SCAN';
setReadyForBluetoothStateCheck: 'BLUETOOTH_PERMISSION_ENABLED';
setReceiverInfo: 'CONNECTED';
setSelectedVc: 'SELECT_VC';
setSenderInfo: 'CONNECTED';
setShareLogTypeUnverified: 'ACCEPT_REQUEST' | 'CHECK_FLOW_TYPE';
setShareLogTypeVerified: 'FACE_VALID';
setShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
setShowQuickShareSuccessBanner: 'STORE_RESPONSE';
setUri: 'SCAN';
storeLoginItem: 'done.invoke.QrLogin';
storeShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT';
storingActivityLog: 'STORE_RESPONSE';
updateFaceCaptureBannerStatus: 'FACE_VALID';
updateShowFaceAuthConsent: 'STORE_RESPONSE';
"clearUri": "STORE_RESPONSE";
"enableLocation": "ALLOWED" | "LOCATION_REQUEST";
"getFaceAuthConsent": "DISCONNECT" | "DISMISS" | "xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection";
"loadMetaDataToMemory": "SCAN";
"loadVCDataToMemory": "STORE_RESPONSE";
"logFailedVerification": "FACE_INVALID";
"logShared": "VC_ACCEPTED";
"openAppPermission": "GOTO_SETTINGS" | "LOCATION_REQUEST";
"openBluetoothSettings": "GOTO_SETTINGS";
"refreshVCs": "STORE_RESPONSE";
"registerLoggers": "STORE_RESPONSE";
"removeLoggers": "DISCONNECT" | "DISMISS" | "DISMISS_QUICK_SHARE_BANNER" | "RESET" | "SCREEN_BLUR" | "STORE_RESPONSE" | "xstate.init";
"resetFaceCaptureBannerStatus": "ACCEPT_REQUEST" | "CLOSE_BANNER";
"resetFlowType": "DISCONNECT" | "DISMISS" | "DISMISS_QUICK_SHARE_BANNER" | "GOTO_HISTORY" | "RESET" | "SCREEN_BLUR" | "xstate.init";
"resetSelectedVc": "DISCONNECT" | "DISMISS" | "DISMISS_QUICK_SHARE_BANNER" | "GOTO_HISTORY" | "RESET" | "SCREEN_BLUR" | "xstate.init";
"resetShowQuickShareSuccessBanner": "DISMISS" | "DISMISS_QUICK_SHARE_BANNER";
"sendBLEConnectionErrorEvent": "BLE_ERROR";
"sendScanData": "SCAN";
"sendVCShareFlowCancelEndEvent": "CANCEL";
"sendVCShareFlowTimeoutEndEvent": "CANCEL" | "RETRY";
"sendVcShareSuccessEvent": "VC_ACCEPTED";
"sendVcSharingStartEvent": "SCAN";
"setBleError": "BLE_ERROR";
"setChildRef": "STORE_RESPONSE";
"setFlowType": "SELECT_VC";
"setLinkCode": "SCAN";
"setQuickShareData": "SCAN";
"setReadyForBluetoothStateCheck": "BLUETOOTH_PERMISSION_ENABLED";
"setReceiverInfo": "CONNECTED";
"setSelectedVc": "SELECT_VC";
"setSenderInfo": "CONNECTED";
"setShareLogTypeUnverified": "ACCEPT_REQUEST" | "CHECK_FLOW_TYPE";
"setShareLogTypeVerified": "FACE_VALID";
"setShowFaceAuthConsent": "FACE_VERIFICATION_CONSENT";
"setShowQuickShareSuccessBanner": "STORE_RESPONSE";
"setUri": "SCAN";
"storeLoginItem": "done.invoke.QrLogin";
"storeShowFaceAuthConsent": "FACE_VERIFICATION_CONSENT";
"storingActivityLog": "STORE_RESPONSE";
"updateFaceCaptureBannerStatus": "FACE_VALID";
"updateShowFaceAuthConsent": "STORE_RESPONSE";
};
eventsCausingDelays: {
CONNECTION_TIMEOUT: 'SCAN';
DESTROY_TIMEOUT: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'RETRY';
SHARING_TIMEOUT: 'ACCEPT_REQUEST' | 'CHECK_FLOW_TYPE' | 'FACE_VALID';
"CONNECTION_TIMEOUT": "SCAN";
"DESTROY_TIMEOUT": "" | "DISMISS" | "LOCATION_ENABLED" | "RETRY";
"SHARING_TIMEOUT": "ACCEPT_REQUEST" | "CHECK_FLOW_TYPE" | "FACE_VALID";
};
eventsCausingGuards: {
isFlowTypeMiniViewShare: 'CHECK_FLOW_TYPE';
isFlowTypeMiniViewShareWithSelfie: 'CHECK_FLOW_TYPE';
isFlowTypeSimpleShare: 'CANCEL' | 'CHECK_FLOW_TYPE' | 'DISMISS';
isIOS: 'BLUETOOTH_STATE_DISABLED' | 'START_PERMISSION_CHECK';
isMinimumStorageRequiredForAuditEntryReached: 'done.invoke.scan.checkStorage:invocation[0]';
isOpenIdQr: 'SCAN';
isQrLogin: 'SCAN';
isQuickShare: 'SCAN';
showFaceAuthConsentScreen: 'VERIFY_AND_ACCEPT_REQUEST';
uptoAndroid11: '' | 'START_PERMISSION_CHECK';
"isFlowTypeMiniViewShare": "CHECK_FLOW_TYPE";
"isFlowTypeMiniViewShareWithSelfie": "CHECK_FLOW_TYPE" | "DISMISS";
"isFlowTypeSimpleShare": "CANCEL" | "CHECK_FLOW_TYPE" | "DISMISS";
"isIOS": "BLUETOOTH_STATE_DISABLED" | "START_PERMISSION_CHECK";
"isMinimumStorageRequiredForAuditEntryReached": "done.invoke.scan.checkStorage:invocation[0]";
"isOpenIdQr": "SCAN";
"isQrLogin": "SCAN";
"isQuickShare": "SCAN";
"showFaceAuthConsentScreen": "" | "VERIFY_AND_ACCEPT_REQUEST";
"uptoAndroid11": "" | "START_PERMISSION_CHECK";
};
eventsCausingServices: {
QrLogin: 'SCAN';
checkBluetoothPermission:
| ''
| 'BLUETOOTH_STATE_DISABLED'
| 'NEARBY_ENABLED'
| 'START_PERMISSION_CHECK';
checkBluetoothState: '' | 'APP_ACTIVE';
checkLocationPermission: 'APP_ACTIVE' | 'LOCATION_ENABLED';
checkLocationStatus: '' | 'LOCATION_REQUEST';
checkNearByDevicesPermission: 'APP_ACTIVE' | 'START_PERMISSION_CHECK';
checkStorageAvailability: 'RESET' | 'SCREEN_FOCUS' | 'SELECT_VC';
disconnect: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'RETRY' | 'SCREEN_BLUR';
monitorConnection: 'DISMISS' | 'SCREEN_BLUR' | 'xstate.init';
requestBluetooth: 'BLUETOOTH_STATE_DISABLED';
requestNearByDevicesPermission: 'NEARBY_DISABLED';
requestToEnableLocationPermission: 'LOCATION_DISABLED';
sendVc: 'ACCEPT_REQUEST' | 'CHECK_FLOW_TYPE' | 'FACE_VALID';
startConnection: 'SCAN';
};
matchesStates:
| 'bluetoothDenied'
| 'bluetoothPermissionDenied'
| 'checkBluetoothPermission'
| 'checkBluetoothPermission.checking'
| 'checkBluetoothPermission.enabled'
| 'checkBluetoothState'
| 'checkBluetoothState.checking'
| 'checkBluetoothState.enabled'
| 'checkBluetoothState.requesting'
| 'checkFaceAuthConsent'
| 'checkNearbyDevicesPermission'
| 'checkNearbyDevicesPermission.checking'
| 'checkNearbyDevicesPermission.enabled'
| 'checkNearbyDevicesPermission.requesting'
| 'checkStorage'
| 'checkingLocationState'
| 'checkingLocationState.checkLocationService'
| 'checkingLocationState.checkingPermissionStatus'
| 'checkingLocationState.denied'
| 'checkingLocationState.disabled'
| 'checkingLocationState.requestToEnableLocation'
| 'clearingConnection'
| 'connecting'
| 'connecting.inProgress'
| 'connecting.timeout'
| 'decodeQuickShareData'
| 'disconnectDevice'
| 'disconnected'
| 'findingConnection'
| 'handlingBleError'
| 'inactive'
| 'invalid'
| 'loadVCS'
| 'loadVCS.idle'
| 'loadVCS.navigatingToHome'
| 'nearByDevicesPermissionDenied'
| 'recheckBluetoothState'
| 'recheckBluetoothState.checking'
| 'recheckBluetoothState.enabled'
| 'restrictSharingVc'
| 'reviewing'
| 'reviewing.accepted'
| 'reviewing.cancelling'
| 'reviewing.disconnect'
| 'reviewing.faceVerificationConsent'
| 'reviewing.idle'
| 'reviewing.invalidIdentity'
| 'reviewing.navigateToHistory'
| 'reviewing.rejected'
| 'reviewing.selectingVc'
| 'reviewing.sendingVc'
| 'reviewing.sendingVc.inProgress'
| 'reviewing.sendingVc.sent'
| 'reviewing.sendingVc.timeout'
| 'reviewing.verifyingIdentity'
| 'showQrLogin'
| 'showQrLogin.idle'
| 'showQrLogin.navigatingToHistory'
| 'showQrLogin.storing'
| 'startPermissionCheck'
| {
checkBluetoothPermission?: 'checking' | 'enabled';
checkBluetoothState?: 'checking' | 'enabled' | 'requesting';
checkNearbyDevicesPermission?: 'checking' | 'enabled' | 'requesting';
checkingLocationState?:
| 'checkLocationService'
| 'checkingPermissionStatus'
| 'denied'
| 'disabled'
| 'requestToEnableLocation';
connecting?: 'inProgress' | 'timeout';
loadVCS?: 'idle' | 'navigatingToHome';
recheckBluetoothState?: 'checking' | 'enabled';
reviewing?:
| 'accepted'
| 'cancelling'
| 'disconnect'
| 'faceVerificationConsent'
| 'idle'
| 'invalidIdentity'
| 'navigateToHistory'
| 'rejected'
| 'selectingVc'
| 'sendingVc'
| 'verifyingIdentity'
| {sendingVc?: 'inProgress' | 'sent' | 'timeout'};
showQrLogin?: 'idle' | 'navigatingToHistory' | 'storing';
"QrLogin": "SCAN";
"checkBluetoothPermission": "" | "BLUETOOTH_STATE_DISABLED" | "NEARBY_ENABLED" | "START_PERMISSION_CHECK";
"checkBluetoothState": "" | "APP_ACTIVE";
"checkLocationPermission": "LOCATION_ENABLED";
"checkLocationStatus": "" | "APP_ACTIVE" | "LOCATION_REQUEST";
"checkNearByDevicesPermission": "APP_ACTIVE" | "START_PERMISSION_CHECK";
"checkStorageAvailability": "RESET" | "SCREEN_FOCUS" | "SELECT_VC";
"disconnect": "" | "DISMISS" | "LOCATION_ENABLED" | "RETRY" | "SCREEN_BLUR";
"monitorConnection": "DISMISS" | "SCREEN_BLUR" | "xstate.init";
"requestBluetooth": "BLUETOOTH_STATE_DISABLED";
"requestNearByDevicesPermission": "NEARBY_DISABLED";
"requestToEnableLocationPermission": "LOCATION_DISABLED";
"sendVc": "ACCEPT_REQUEST" | "CHECK_FLOW_TYPE" | "FACE_VALID";
"startConnection": "SCAN";
};
matchesStates: "bluetoothDenied" | "bluetoothPermissionDenied" | "checkBluetoothPermission" | "checkBluetoothPermission.checking" | "checkBluetoothPermission.enabled" | "checkBluetoothState" | "checkBluetoothState.checking" | "checkBluetoothState.enabled" | "checkBluetoothState.requesting" | "checkFaceAuthConsent" | "checkNearbyDevicesPermission" | "checkNearbyDevicesPermission.checking" | "checkNearbyDevicesPermission.enabled" | "checkNearbyDevicesPermission.requesting" | "checkStorage" | "checkingLocationState" | "checkingLocationState.LocationPermissionRationale" | "checkingLocationState.checkLocationService" | "checkingLocationState.checkingPermissionStatus" | "checkingLocationState.denied" | "checkingLocationState.disabled" | "checkingLocationState.requestToEnableLocation" | "clearingConnection" | "connecting" | "connecting.inProgress" | "connecting.timeout" | "decodeQuickShareData" | "disconnectDevice" | "disconnected" | "findingConnection" | "handlingBleError" | "inactive" | "invalid" | "loadVCS" | "loadVCS.idle" | "loadVCS.navigatingToHome" | "nearByDevicesPermissionDenied" | "recheckBluetoothState" | "recheckBluetoothState.checking" | "recheckBluetoothState.enabled" | "restrictSharingVc" | "reviewing" | "reviewing.accepted" | "reviewing.cancelling" | "reviewing.checkFaceAuthConsentForMiniView" | "reviewing.disconnect" | "reviewing.faceVerificationConsent" | "reviewing.idle" | "reviewing.invalidIdentity" | "reviewing.navigateToHistory" | "reviewing.rejected" | "reviewing.selectingVc" | "reviewing.sendingVc" | "reviewing.sendingVc.inProgress" | "reviewing.sendingVc.sent" | "reviewing.sendingVc.timeout" | "reviewing.verifyingIdentity" | "showQrLogin" | "showQrLogin.idle" | "showQrLogin.navigatingToHistory" | "showQrLogin.storing" | "startPermissionCheck" | { "checkBluetoothPermission"?: "checking" | "enabled";
"checkBluetoothState"?: "checking" | "enabled" | "requesting";
"checkNearbyDevicesPermission"?: "checking" | "enabled" | "requesting";
"checkingLocationState"?: "LocationPermissionRationale" | "checkLocationService" | "checkingPermissionStatus" | "denied" | "disabled" | "requestToEnableLocation";
"connecting"?: "inProgress" | "timeout";
"loadVCS"?: "idle" | "navigatingToHome";
"recheckBluetoothState"?: "checking" | "enabled";
"reviewing"?: "accepted" | "cancelling" | "checkFaceAuthConsentForMiniView" | "disconnect" | "faceVerificationConsent" | "idle" | "invalidIdentity" | "navigateToHistory" | "rejected" | "selectingVc" | "sendingVc" | "verifyingIdentity" | { "sendingVc"?: "inProgress" | "sent" | "timeout"; };
"showQrLogin"?: "idle" | "navigatingToHistory" | "storing"; };
tags: never;
}

View File

@@ -0,0 +1,84 @@
import { CameraCapturedPicture } from "expo-camera";
import { EmitterSubscription } from "react-native";
import { ActorRefFrom } from "xstate";
import { createModel } from "xstate/lib/model";
import { DeviceInfo } from "../../../components/DeviceInfoList";
import { AppServices } from "../../../shared/GlobalContext";
import { VCShareFlowType } from "../../../shared/Utils";
import { qrLoginMachine } from "../../QrLogin/QrLoginMachine";
import { VC } from "../../VerifiableCredential/VCMetaMachine/vc";
import { ActivityLogType } from "../../activityLog";
import { BLEError } from "../types";
const ScanEvents ={
SELECT_VC: (vc: VC, flowType: string) => ({vc, flowType}),
SCAN: (params: string) => ({params}),
ACCEPT_REQUEST: () => ({}),
VERIFY_AND_ACCEPT_REQUEST: () => ({}),
VC_ACCEPTED: () => ({}),
VC_REJECTED: () => ({}),
VC_SENT: () => ({}),
CANCEL: () => ({}),
CLOSE_BANNER: () => ({}),
STAY_IN_PROGRESS: () => ({}),
RETRY: () => ({}),
DISMISS: () => ({}),
DISMISS_QUICK_SHARE_BANNER: () => ({}),
GOTO_HISTORY: () => ({}),
CONNECTED: () => ({}),
DISCONNECT: () => ({}),
BLE_ERROR: (bleError: BLEError) => ({bleError}),
CONNECTION_DESTROYED: () => ({}),
SCREEN_BLUR: () => ({}),
SCREEN_FOCUS: () => ({}),
BLUETOOTH_PERMISSION_ENABLED: () => ({}),
BLUETOOTH_PERMISSION_DENIED: () => ({}),
BLUETOOTH_STATE_ENABLED: () => ({}),
BLUETOOTH_STATE_DISABLED: () => ({}),
NEARBY_ENABLED: () => ({}),
NEARBY_DISABLED: () => ({}),
GOTO_SETTINGS: () => ({}),
START_PERMISSION_CHECK: () => ({}),
LOCATION_ENABLED: () => ({}),
LOCATION_DISABLED: () => ({}),
LOCATION_REQUEST: () => ({}),
CHECK_FLOW_TYPE: () => ({}),
UPDATE_VC_NAME: (vcName: string) => ({vcName}),
STORE_RESPONSE: (response: any) => ({response}),
APP_ACTIVE: () => ({}),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
RESET: () => ({}),
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) => ({
isDoNotAskAgainChecked,
}),
ALLOWED: () => ({}),
DENIED: () => ({}),
}
export const ScanModel = createModel(
{
serviceRefs: {} as AppServices,
senderInfo: {} as DeviceInfo,
receiverInfo: {} as DeviceInfo,
selectedVc: {} as VC,
bleError: {} as BLEError,
loggers: [] as EmitterSubscription[],
vcName: '',
flowType: VCShareFlowType.SIMPLE_SHARE,
verificationImage: {} as CameraCapturedPicture,
openId4VpUri: '',
shareLogType: '' as ActivityLogType,
QrLoginRef: {} as ActorRefFrom<typeof qrLoginMachine>,
showQuickShareSuccessBanner: false,
linkCode: '',
quickShareData: {},
showFaceAuthConsent: true as boolean,
readyForBluetoothStateCheck: false,
showFaceCaptureSuccessBanner: false,
},
{
events: ScanEvents,
},
);

View File

@@ -117,3 +117,13 @@ export function selectIsQrLoginStoring(state: State) {
export function selectIsDone(state: State) {
return state.matches('reviewing.disconnect');
}
export function selectIsMinimumStorageRequiredForAuditEntryLimitReached(
state: State,
) {
return state.matches('restrictSharingVc');
}
export function selectIsFaceVerificationConsent(state: State) {
return state.matches('reviewing.faceVerificationConsent');
}

View File

@@ -0,0 +1,197 @@
import {WalletDataEvent} from '@mosip/tuvali/lib/typescript/types/events';
import { isLocationEnabled } from "react-native-device-info";
import Storage from '../../../shared/storage';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
import tuvali from '@mosip/tuvali';
import {
check,
checkMultiple,
PERMISSIONS,
PermissionStatus,
requestMultiple,
RESULTS,
} from 'react-native-permissions';
import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
import { requestLocationPermission, checkLocationPermissionStatus } from "../../../shared/location";
import { isIOS } from "../../../shared/constants";
const {wallet, EventTypes, VerificationStatus} = tuvali;
export const ScanServices=(model:any)=>{
return { checkBluetoothPermission: () => async callback => {
// wait a bit for animation to finish when app becomes active
await new Promise(resolve => setTimeout(resolve, 250));
try {
// Passing Granted for android since permission status is always granted even if its denied.
let response: PermissionStatus = RESULTS.GRANTED;
if (isIOS()) {
response = await check(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL);
}
if (response === RESULTS.GRANTED) {
callback(model.events.BLUETOOTH_PERMISSION_ENABLED());
} else {
callback(model.events.BLUETOOTH_PERMISSION_DENIED());
}
} catch (e) {
console.error(e);
}
},
checkBluetoothState: () => callback => {
const subscription = BluetoothStateManager.onStateChange(state => {
if (state === 'PoweredOn') {
callback(model.events.BLUETOOTH_STATE_ENABLED());
} else {
callback(model.events.BLUETOOTH_STATE_DISABLED());
}
}, true);
return () => subscription.remove();
},
requestBluetooth: () => callback => {
BluetoothStateManager.requestToEnable()
.then(() => callback(model.events.BLUETOOTH_STATE_ENABLED()))
.catch(() => callback(model.events.BLUETOOTH_STATE_DISABLED()));
},
requestToEnableLocationPermission: () => callback => {
requestLocationPermission(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
monitorConnection: () => callback => {
const walletErrorCodePrefix = 'TVW';
const subscription = wallet.handleDataEvents(event => {
if (event.type === EventTypes.onDisconnected) {
callback({type: 'DISCONNECT'});
}
if (
event.type === EventTypes.onError &&
event.code.includes(walletErrorCodePrefix)
) {
callback({
type: 'BLE_ERROR',
bleError: {message: event.message, code: event.code},
});
console.error('BLE Exception: ' + event.message);
}
});
return () => subscription.remove();
},
checkNearByDevicesPermission: () => callback => {
checkMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
requestNearByDevicesPermission: () => callback => {
requestMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
checkLocationPermission: () => callback => {
checkLocationPermissionStatus(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
checkLocationStatus: () => async callback => {
const isEnabled: boolean = await isLocationEnabled();
if (isEnabled) {
callback(model.events.LOCATION_ENABLED());
} else {
callback(model.events.LOCATION_DISABLED());
}
},
startConnection: (context:any) => callback => {
wallet.startConnection(context.openId4VpUri);
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onSecureChannelEstablished) {
callback({type: 'CONNECTED'});
}
};
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
sendVc: context => callback => {
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onDataSent) {
callback({type: 'VC_SENT'});
} else if (event.type === EventTypes.onVerificationStatusReceived) {
callback({
type:
event.status === VerificationStatus.ACCEPTED
? 'VC_ACCEPTED'
: 'VC_REJECTED',
});
}
};
wallet.sendData(
JSON.stringify({
...context.selectedVc,
}),
);
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
disconnect: () => () => {
try {
console.log("inside wallet disconnect");
wallet.disconnect();
} catch (e) {
// pass
}
},
checkStorageAvailability: () => async () => {
return Promise.resolve(
Storage.isMinimumLimitReached('minStorageRequiredForAuditEntry'),
);
},
};
}

View File

@@ -17,7 +17,7 @@ import {
MY_VCS_STORE_KEY,
RECEIVED_VCS_STORE_KEY,
SETTINGS_STORE_KEY,
FACE_AUTH_CONSENT,
SHOW_FACE_AUTH_CONSENT_SHARE_FLOW,
ENOENT,
} from '../shared/constants';
import SecureKeystore from '@mosip/secure-keystore';
@@ -563,7 +563,7 @@ export async function setItem(
appId,
};
encryptedData = JSON.stringify(settings);
} else if (key === FACE_AUTH_CONSENT) {
} else if (key === SHOW_FACE_AUTH_CONSENT_SHARE_FLOW) {
encryptedData = JSON.stringify(value);
} else {
encryptedData = await encryptJson(encryptionKey, JSON.stringify(value));
@@ -632,7 +632,7 @@ export async function getItem(
parsedData.encryptedData = JSON.parse(decryptedData);
}
return parsedData;
} else if (key === FACE_AUTH_CONSENT) {
} else if (key === SHOW_FACE_AUTH_CONSENT_SHARE_FLOW) {
return JSON.parse(data);
}
decryptedData = await decryptJson(encryptionKey, data);

View File

@@ -1,28 +1,21 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke._store': {
type: 'done.invoke._store';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.store.resettingStorage:invocation[0]': {
type: 'done.invoke.store.resettingStorage:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform._store': {type: 'error.platform._store'; data: unknown};
'xstate.init': {type: 'xstate.init'};
"done.invoke._store": { type: "done.invoke._store"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.store.resettingStorage:invocation[0]": { type: "done.invoke.store.resettingStorage:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform._store": { type: "error.platform._store"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
checkStorageInitialisedOrNot: 'done.invoke.store.checkStorageInitialisation:invocation[0]';
clear: 'done.invoke.store.resettingStorage:invocation[0]';
generateEncryptionKey: 'done.invoke.store.generatingEncryptionKey:invocation[0]';
getEncryptionKey: 'done.invoke.store.gettingEncryptionKey:invocation[0]';
hasAndroidEncryptionKey: 'done.invoke.store.checkEncryptionKey:invocation[0]';
store: 'done.invoke._store';
"checkStorageInitialisedOrNot": "done.invoke.store.checkStorageInitialisation:invocation[0]";
"clear": "done.invoke.store.resettingStorage:invocation[0]";
"generateEncryptionKey": "done.invoke.store.generatingEncryptionKey:invocation[0]";
"getEncryptionKey": "done.invoke.store.gettingEncryptionKey:invocation[0]";
"hasAndroidEncryptionKey": "done.invoke.store.checkEncryptionKey:invocation[0]";
"store": "done.invoke._store";
};
missingImplementations: {
actions: never;
@@ -31,47 +24,25 @@ export interface Typegen0 {
services: never;
};
eventsCausingActions: {
forwardStoreRequest:
| 'APPEND'
| 'CLEAR'
| 'EXPORT'
| 'GET'
| 'GET_VCS_DATA'
| 'PREPEND'
| 'REMOVE'
| 'REMOVE_ITEMS'
| 'REMOVE_VC_METADATA'
| 'RESTORE_BACKUP'
| 'SET'
| 'UPDATE';
notifyParent:
| 'KEY_RECEIVED'
| 'READY'
| 'done.invoke.store.resettingStorage:invocation[0]';
setEncryptionKey: 'KEY_RECEIVED';
"forwardStoreRequest": "APPEND" | "CLEAR" | "EXPORT" | "GET" | "GET_VCS_DATA" | "PREPEND" | "REMOVE" | "REMOVE_ITEMS" | "REMOVE_VC_METADATA" | "RESTORE_BACKUP" | "SET" | "UPDATE";
"notifyParent": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
"setEncryptionKey": "KEY_RECEIVED";
};
eventsCausingDelays: {
};
eventsCausingDelays: {};
eventsCausingGuards: {
isCustomSecureKeystore: 'KEY_RECEIVED';
"isCustomSecureKeystore": "KEY_RECEIVED";
};
eventsCausingServices: {
checkStorageInitialisedOrNot: 'ERROR';
clear: 'KEY_RECEIVED';
generateEncryptionKey: 'ERROR' | 'IGNORE' | 'READY';
getEncryptionKey: 'TRY_AGAIN';
hasAndroidEncryptionKey: never;
store:
| 'KEY_RECEIVED'
| 'READY'
| 'done.invoke.store.resettingStorage:invocation[0]';
"checkStorageInitialisedOrNot": "ERROR";
"clear": "KEY_RECEIVED";
"generateEncryptionKey": "ERROR" | "IGNORE" | "READY";
"getEncryptionKey": "TRY_AGAIN";
"hasAndroidEncryptionKey": never;
"store": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
};
matchesStates:
| 'checkEncryptionKey'
| 'checkStorageInitialisation'
| 'failedReadingKey'
| 'generatingEncryptionKey'
| 'gettingEncryptionKey'
| 'ready'
| 'resettingStorage';
matchesStates: "checkEncryptionKey" | "checkStorageInitialisation" | "failedReadingKey" | "generatingEncryptionKey" | "gettingEncryptionKey" | "ready" | "resettingStorage";
tags: never;
}

View File

@@ -1,20 +1,15 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.HomeScreen.tabs.checkStorage:invocation[0]': {
type: 'done.invoke.HomeScreen.tabs.checkStorage:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'xstate.after(100)#HomeScreen.tabs.init': {
type: 'xstate.after(100)#HomeScreen.tabs.init';
};
'xstate.init': {type: 'xstate.init'};
"done.invoke.HomeScreen.tabs.checkStorage:invocation[0]": { type: "done.invoke.HomeScreen.tabs.checkStorage:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"xstate.after(100)#HomeScreen.tabs.init": { type: "xstate.after(100)#HomeScreen.tabs.init" };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
checkStorageAvailability: 'done.invoke.HomeScreen.tabs.checkStorage:invocation[0]';
"checkStorageAvailability": "done.invoke.HomeScreen.tabs.checkStorage:invocation[0]";
};
missingImplementations: {
actions: never;
@@ -23,43 +18,23 @@ export interface Typegen0 {
services: never;
};
eventsCausingActions: {
resetSelectedVc: 'DISMISS_MODAL' | 'xstate.init';
sendAddEvent: 'DOWNLOAD_ID';
setSelectedVc: 'VIEW_VC';
spawnTabActors: 'xstate.init';
"resetSelectedVc": "DISMISS_MODAL" | "xstate.init";
"sendAddEvent": "DOWNLOAD_ID";
"setSelectedVc": "VIEW_VC";
"spawnTabActors": "xstate.init";
};
eventsCausingDelays: {
};
eventsCausingDelays: {};
eventsCausingGuards: {
isMinimumStorageLimitReached: 'done.invoke.HomeScreen.tabs.checkStorage:invocation[0]';
"isMinimumStorageLimitReached": "done.invoke.HomeScreen.tabs.checkStorage:invocation[0]";
};
eventsCausingServices: {
checkStorageAvailability: 'GOTO_ISSUERS';
issuersMachine: 'done.invoke.HomeScreen.tabs.checkStorage:invocation[0]';
};
matchesStates:
| 'modals'
| 'modals.none'
| 'modals.viewingVc'
| 'tabs'
| 'tabs.checkStorage'
| 'tabs.gotoIssuers'
| 'tabs.history'
| 'tabs.idle'
| 'tabs.init'
| 'tabs.myVcs'
| 'tabs.receivedVcs'
| 'tabs.storageLimitReached'
| {
modals?: 'none' | 'viewingVc';
tabs?:
| 'checkStorage'
| 'gotoIssuers'
| 'history'
| 'idle'
| 'init'
| 'myVcs'
| 'receivedVcs'
| 'storageLimitReached';
"checkStorageAvailability": "GOTO_ISSUERS";
"issuersMachine": "done.invoke.HomeScreen.tabs.checkStorage:invocation[0]";
};
matchesStates: "modals" | "modals.none" | "modals.viewingVc" | "tabs" | "tabs.checkStorage" | "tabs.gotoIssuers" | "tabs.history" | "tabs.idle" | "tabs.init" | "tabs.myVcs" | "tabs.receivedVcs" | "tabs.storageLimitReached" | { "modals"?: "none" | "viewingVc";
"tabs"?: "checkStorage" | "gotoIssuers" | "history" | "idle" | "init" | "myVcs" | "receivedVcs" | "storageLimitReached"; };
tags: never;
}

View File

@@ -3,7 +3,7 @@ import {Button, Centered, Column, Text} from '../../components/ui';
import {Theme} from '../../components/ui/styleUtils';
import {useTranslation} from 'react-i18next';
import {useQrLogin} from './QrLoginController';
import {QrLoginRef} from '../../machines/QrLoginMachine';
import {QrLoginRef} from '../../machines/QrLogin/QrLoginMachine';
import {Icon} from 'react-native-elements';
import {Modal} from '../../components/ui/Modal';
import {VcItemContainer} from '../../components/VC/VcItemContainer';

View File

@@ -6,7 +6,7 @@ import {useQrLogin} from './QrLoginController';
import {Image, View} from 'react-native';
import {Icon, ListItem, Switch} from 'react-native-elements';
import {Modal} from '../../components/ui/Modal';
import {QrLoginRef} from '../../machines/QrLoginMachine';
import {QrLoginRef} from '../../machines/QrLogin/QrLoginMachine';
import {ScrollView} from 'react-native';
import {getClientNameForCurrentLanguage} from '../../i18n';

View File

@@ -7,7 +7,7 @@ import {MessageOverlay} from '../../components/MessageOverlay';
import {MyBindedVcs} from './MyBindedVcs';
import {QrLoginSuccess} from './QrLoginSuccessMessage';
import {QrConsent} from './QrConsent';
import {QrLoginRef} from '../../machines/QrLoginMachine';
import {QrLoginRef} from '../../machines/QrLogin/QrLoginMachine';
import {Icon} from 'react-native-elements';
import {View} from 'react-native';
import {FaceVerificationAlertOverlay} from '../Scan/FaceVerificationAlertOverlay';

View File

@@ -1,8 +1,8 @@
import {useSelector} from '@xstate/react';
import {useContext, useState} from 'react';
import {ActorRefFrom} from 'xstate';
import { QrLoginEvents } from '../../machines/QrLogin/QrLoginMachine';
import {
QrLoginEvents,
selectClientName,
selectErrorMessage,
selectEssentialClaims,
@@ -25,7 +25,7 @@ import {
selectVoluntaryClaims,
selectCredential,
selectVerifiableCredentialData,
} from '../../machines/QrLoginMachine';
} from '../../machines/QrLogin/QrLoginSelectors';
import {selectBindedVcsMetadata} from '../../machines/VerifiableCredential/VCMetaMachine/VCMetaSelectors';
import {GlobalContext} from '../../shared/GlobalContext';
import {QrLoginProps} from './QrLogin';
@@ -85,8 +85,8 @@ export function useQrLogin({service}: QrLoginProps) {
const vcData = vcRef.getSnapshot().context;
service.send(QrLoginEvents.SELECT_VC(vcData));
},
FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) =>
service.send(QrLoginEvents.FACE_VERIFICATION_CONSENT(isConsentGiven)),
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) =>
service.send(QrLoginEvents.FACE_VERIFICATION_CONSENT(isDoNotAskAgainChecked)),
DISMISS: () => service.send(QrLoginEvents.DISMISS()),
SCANNING_DONE: (qrCode: string) =>
service.send(QrLoginEvents.SCANNING_DONE(qrCode)),

View File

@@ -6,7 +6,7 @@ import {Modal} from '../../components/ui/Modal';
import {Centered, Button, Text, Column} from '../../components/ui';
import {Theme} from '../../components/ui/styleUtils';
import {useQrLogin} from './QrLoginController';
import {QrLoginRef} from '../../machines/QrLoginMachine';
import {QrLoginRef} from '../../machines/QrLogin/QrLoginMachine';
import {getClientNameForCurrentLanguage} from '../../i18n';
export const QrLoginSuccess: React.FC<QrLoginSuccessProps> = props => {

View File

@@ -90,7 +90,7 @@ export const FaceVerificationAlertOverlay: React.FC<
interface FaceVerificationAlertProps {
isVisible: boolean;
onConfirm: (isConsentGiven: boolean) => void;
onConfirm: (isDoNotAskAgainChecked: boolean) => void;
close: () => void;
isQrLogin?: boolean;
}

View File

@@ -24,7 +24,7 @@ import {
selectIsFaceIdentityVerified,
selectCredential,
selectVerifiableCredentialData,
} from '../../machines/bleShare/scan/selectors';
} from '../../machines/bleShare/scan/scanSelectors';
import {
selectBleError,
selectIsAccepted,

View File

@@ -16,10 +16,13 @@ import {BannerNotificationContainer} from '../../components/BannerNotificationCo
import {SharingStatusModal} from './SharingStatusModal';
import {SvgImage} from '../../components/ui/svg';
import {LocationPermissionRational} from './LocationPermissionRational';
import { FaceVerificationAlertOverlay } from './FaceVerificationAlertOverlay';
import { useSendVcScreen } from './SendVcScreenController';
export const ScanScreen: React.FC = () => {
const {t} = useTranslation('ScanScreen');
const controller = useScanScreen();
const scanScreenController = useScanScreen();
const sendVcScreenController = useSendVcScreen();
const [isBluetoothOn, setIsBluetoothOn] = useState(false);
useEffect(() => {
@@ -36,13 +39,13 @@ export const ScanScreen: React.FC = () => {
// TODO(kludge): skip running this hook on every render
useEffect(() => {
if (controller.isStartPermissionCheck && !controller.isEmpty)
controller.START_PERMISSION_CHECK();
if (scanScreenController.isStartPermissionCheck && !scanScreenController.isEmpty)
scanScreenController.START_PERMISSION_CHECK();
});
useEffect(() => {
if (controller.isQuickShareDone) controller.GOTO_HOME();
}, [controller.isQuickShareDone]);
if (scanScreenController.isQuickShareDone) scanScreenController.GOTO_HOME();
}, [scanScreenController.isQuickShareDone]);
const openSettings = () => {
Linking.openSettings();
@@ -123,14 +126,14 @@ export const ScanScreen: React.FC = () => {
testID="enableLocationServicesMessage"
align="center"
color={Theme.Colors.errorMessage}>
{controller.locationError.message}
{scanScreenController.locationError.message}
</Text>
</Centered>
<Button
testID="enableLocationServicesButton"
title={controller.locationError.button}
onPress={controller.LOCATION_REQUEST}
title={scanScreenController.locationError.button}
onPress={scanScreenController.LOCATION_REQUEST}
/>
</Column>
);
@@ -139,58 +142,58 @@ export const ScanScreen: React.FC = () => {
function qrScannerComponent() {
return (
<Column crossAlign="center" margin="0 0 0 -6">
<QrScanner onQrFound={controller.SCAN} title={t('scanningGuide')} />
<QrScanner onQrFound={scanScreenController.SCAN} title={t('scanningGuide')} />
</Column>
);
}
function loadQRScanner() {
if (controller.isEmpty) {
if (scanScreenController.isEmpty) {
return noShareableVcText();
}
if (controller.selectIsInvalid) {
if (scanScreenController.selectIsInvalid) {
return displayInvalidQRpopup();
}
if (controller.isNearByDevicesPermissionDenied) {
if (scanScreenController.isNearByDevicesPermissionDenied) {
return allowNearbyDevicesPermissionComponent();
}
if (
(controller.isBluetoothDenied || !isBluetoothOn) &&
controller.isReadyForBluetoothStateCheck
(scanScreenController.isBluetoothDenied || !isBluetoothOn) &&
scanScreenController.isReadyForBluetoothStateCheck
) {
return bluetoothIsOffText();
}
if (controller.isLocalPermissionRational) {
if (scanScreenController.isLocalPermissionRational) {
return (
<LocationPermissionRational
onConfirm={controller.ALLOWED}
onCancel={controller.DENIED}
onConfirm={scanScreenController.ALLOWED}
onCancel={scanScreenController.DENIED}
/>
);
}
if (controller.isLocationDisabled || controller.isLocationDenied) {
if (scanScreenController.isLocationDisabled || scanScreenController.isLocationDenied) {
return allowLocationComponent();
}
if (controller.isBluetoothPermissionDenied) {
if (scanScreenController.isBluetoothPermissionDenied) {
return allowBluetoothPermissionComponent();
}
if (controller.isScanning) {
if (scanScreenController.isScanning) {
return qrScannerComponent();
}
}
function displayStorageLimitReachedError(): React.ReactNode {
return (
!controller.isEmpty && (
!scanScreenController.isEmpty && (
<ErrorMessageOverlay
testID="storageLimitReachedError"
isVisible={
controller.isMinimumStorageRequiredForAuditEntryLimitReached
scanScreenController.isMinimumStorageRequiredForAuditEntryLimitReached
}
translationPath={'ScanScreen'}
error="errors.storageLimitReached"
onDismiss={controller.GOTO_HOME}
onDismiss={scanScreenController.GOTO_HOME}
/>
)
);
@@ -198,17 +201,17 @@ export const ScanScreen: React.FC = () => {
function displayInvalidQRpopup(): React.ReactNode {
return (
!controller.isEmpty && (
!scanScreenController.isEmpty && (
<SharingStatusModal
isVisible={controller.selectIsInvalid}
isVisible={scanScreenController.selectIsInvalid}
testId={'invalidQrPopup'}
image={SvgImage.ErrorLogo()}
title={t(`status.bleError.TVW_CON_001.title`)}
message={t(`status.bleError.TVW_CON_001.message`)}
gradientButtonTitle={t('status.bleError.retry')}
clearButtonTitle={t('status.bleError.home')}
onGradientButton={controller.DISMISS}
onClearButton={controller.GOTO_HOME}
onGradientButton={scanScreenController.DISMISS}
onClearButton={scanScreenController.GOTO_HOME}
/>
)
);
@@ -217,19 +220,24 @@ export const ScanScreen: React.FC = () => {
return (
<Column fill backgroundColor={Theme.Colors.whiteBackgroundColor}>
<BannerNotificationContainer />
<FaceVerificationAlertOverlay
isVisible={sendVcScreenController.isFaceVerificationConsent}
onConfirm={sendVcScreenController.FACE_VERIFICATION_CONSENT}
close={sendVcScreenController.DISMISS}
/>
<Centered
padding="24 0"
align="space-evenly"
backgroundColor={Theme.Colors.whiteBackgroundColor}>
{loadQRScanner()}
{controller.isQrLogin && (
{scanScreenController.isQrLogin && (
<QrLogin
isVisible={controller.isQrLogin}
service={controller.isQrRef}
isVisible={scanScreenController.isQrLogin}
service={scanScreenController.isQrRef}
/>
)}
<MessageOverlay
isVisible={controller.isQrLoginstoring}
isVisible={scanScreenController.isQrLoginstoring}
title={t('loading')}
progress
/>

View File

@@ -13,7 +13,7 @@ import {
selectQrLoginRef,
selectIsQuickShareDone,
selectShowQuickShareSuccessBanner,
} from '../../machines/bleShare/scan/selectors';
} from '../../machines/bleShare/scan/scanSelectors';
import {
selectIsBluetoothDenied,
selectIsNearByDevicesPermissionDenied,
@@ -24,8 +24,8 @@ import {
} from '../../machines/bleShare/commonSelectors';
import {
ScanEvents,
selectIsMinimumStorageRequiredForAuditEntryLimitReached,
} from '../../machines/bleShare/scan/scanMachine';
import { selectIsMinimumStorageRequiredForAuditEntryLimitReached } from '../../machines/bleShare/scan/scanSelectors';
import {BOTTOM_TAB_ROUTES} from '../../routes/routesConstants';
import {MainBottomTabParamList} from '../../routes/routeTypes';
import {useNavigation, NavigationProp} from '@react-navigation/native';

View File

@@ -9,7 +9,6 @@ import {useInterpret} from '@xstate/react';
import {GlobalContext} from '../../shared/GlobalContext';
import {useFocusEffect} from '@react-navigation/native';
import {VcItemContainer} from '../../components/VC/VcItemContainer';
import {VCMetadata} from '../../shared/VCMetadata';
import {createVCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine';
import {
getImpressionEventData,

View File

@@ -9,7 +9,7 @@ import {
selectReceiverInfo,
selectVcName,
selectVerifiableCredentialData,
} from '../../machines/bleShare/scan/selectors';
} from '../../machines/bleShare/scan/scanSelectors';
import {
selectIsCancelling,
selectIsInvalidIdentity,
@@ -17,8 +17,8 @@ import {
} from '../../machines/bleShare/commonSelectors';
import {
ScanEvents,
selectIsFaceVerificationConsent,
} from '../../machines/bleShare/scan/scanMachine';
import { selectIsFaceVerificationConsent } from '../../machines/bleShare/scan/scanSelectors';
import {VCShareFlowType} from '../../shared/Utils';
import {NavigationProp, useNavigation} from '@react-navigation/native';
import {RootRouteProps} from '../../routes';
@@ -63,8 +63,8 @@ export function useSendVcScreen() {
),
CANCEL: () => scanService.send(ScanEvents.CANCEL()),
ACCEPT_REQUEST: () => scanService.send(ScanEvents.ACCEPT_REQUEST()),
FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) =>
scanService.send(ScanEvents.FACE_VERIFICATION_CONSENT(isConsentGiven)),
FACE_VERIFICATION_CONSENT: (isDoNotAskAgainChecked: boolean) =>
scanService.send(ScanEvents.FACE_VERIFICATION_CONSENT(isDoNotAskAgainChecked)),
VERIFY_AND_ACCEPT_REQUEST: () =>
scanService.send(ScanEvents.VERIFY_AND_ACCEPT_REQUEST()),
DISMISS: () => scanService.send(ScanEvents.DISMISS()),

View File

@@ -41,7 +41,9 @@ export const SETTINGS_STORE_KEY = 'settings';
export const APP_ID_LENGTH = 12;
export const FACE_AUTH_CONSENT = 'faceAuthConsent';
export const SHOW_FACE_AUTH_CONSENT_SHARE_FLOW = 'showFaceAuthConsentShareFlow';
export const SHOW_FACE_AUTH_CONSENT_QR_LOGIN_FLOW='showFaceAuthConsentQrLoginFlow'
//Banner Status
export const BANNER_TYPE_SUCCESS = 'success';