[INJIMOB-3389]: Implement singleton pattern for OVP and VCI client Modules (#2005)

* [INJIMOB-3389]: Implement Singleton pattern for Openid4vp Module

Signed-off-by: balachandarg-tw <balachandar.g@thoughtworks.com>

* [INJIMOB-3389]: Implement singleton pattern for VCI client module

Signed-off-by: balachandarg-tw <balachandar.g@thoughtworks.com>

* [INJIMOB-3389]: Update constructor for Openid4Vp singleton

Signed-off-by: balachandarg-tw <balachandar.g@thoughtworks.com>

---------

Signed-off-by: balachandarg-tw <balachandar.g@thoughtworks.com>
This commit is contained in:
balachandarg-tw
2025-07-10 23:30:55 +05:30
committed by GitHub
parent 29daa0b34d
commit e5b8f4ed40
9 changed files with 302 additions and 255 deletions

View File

@@ -399,3 +399,6 @@ fileignoreconfig:
- filename: machines/openID4VP/openID4VPModel.ts
checksum: 5d1ed430f84852d6c85bc439c47641cfb5b19cbd1a03faf8918429685db51e07
version: ""
- filename: shared/openID4VP/OpenID4VPHelper.ts
checksum: 2ab5f935ea3d1ec4d109d8614c2246f40e284594288566338f185611470e6928
version: "1.0"

View File

@@ -13,11 +13,11 @@ import {
updateCredentialInformation,
verifyCredentialData,
} from '../../shared/openId4VCI/Utils';
import {VciClient} from '../../shared/vciClient/VciClient';
import VciClient from '../../shared/vciClient/VciClient';
import {issuerType} from './IssuersMachine';
import {setItem} from '../store';
import {API_CACHED_STORAGE_KEYS} from '../../shared/constants';
import { createCacheObject } from '../../shared/Utils';
import {createCacheObject} from '../../shared/Utils';
export const IssuersService = () => {
return {
@@ -77,31 +77,32 @@ export const IssuersService = () => {
cNonce: cNonce,
});
};
const credential = await VciClient.requestCredentialFromTrustedIssuer(
constructIssuerMetaData(
context.selectedIssuer,
context.selectedCredentialType,
context.selectedCredentialType.scope,
),
{
clientId: context.selectedIssuer.client_id,
redirectUri: context.selectedIssuer.redirect_uri,
},
getProofJwt,
navigateToAuthView,
);
const credential =
await VciClient.getInstance().requestCredentialFromTrustedIssuer(
constructIssuerMetaData(
context.selectedIssuer,
context.selectedCredentialType,
context.selectedCredentialType.scope,
),
{
clientId: context.selectedIssuer.client_id,
redirectUri: context.selectedIssuer.redirect_uri,
},
getProofJwt,
navigateToAuthView,
);
return updateCredentialInformation(context, credential);
},
sendTxCode: async (context: any) => {
await VciClient.client.sendTxCodeFromJS(context.txCode);
await VciClient.getInstance().sendTxCode(context.txCode);
},
sendConsentGiven: async () => {
await VciClient.client.sendIssuerTrustResponseFromJS(true);
await VciClient.getInstance().sendIssuerConsent(true);
},
sendConsentNotGiven: async () => {
await VciClient.client.sendIssuerTrustResponseFromJS(false);
await VciClient.getInstance().sendIssuerConsent(false);
},
checkIssuerIdInStoredTrustedIssuers: async (context: any) => {
@@ -144,7 +145,7 @@ export const IssuersService = () => {
) => {
let issuer = issuerMetadata as issuerType;
issuer.issuer_id = issuer.credential_issuer;
const wellknownCacheObject= createCacheObject(issuer)
const wellknownCacheObject = createCacheObject(issuer);
await setItem(
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(issuer.issuer_id),
wellknownCacheObject,
@@ -194,7 +195,7 @@ export const IssuersService = () => {
});
};
const credential = await VciClient.requestCredentialByOffer(
const credential = await VciClient.getInstance().requestCredentialByOffer(
context.qrData,
getTxCode,
getSignedProofJwt,
@@ -222,7 +223,7 @@ export const IssuersService = () => {
true,
context.cNonce,
);
await VciClient.client.sendProofFromJS(proofJWT);
await VciClient.getInstance().sendProof(proofJWT);
return proofJWT;
},
constructProofForTrustedIssuers: async (context: any) => {
@@ -237,7 +238,7 @@ export const IssuersService = () => {
false,
context.cNonce,
);
await VciClient.client.sendProofFromJS(proofJWT);
await VciClient.getInstance().sendProof(proofJWT);
return proofJWT;
},

View File

@@ -12,7 +12,7 @@ import {JSONPath} from 'jsonpath-plus';
import {VCShareFlowType} from '../../shared/Utils';
import {ActivityLogEvents} from '../activityLog';
import {VPShareActivityLog} from '../../components/VPShareActivityLogEvent';
import {OpenID4VP} from '../../shared/openID4VP/OpenID4VP';
import OpenID4VP from '../../shared/openID4VP/OpenID4VP';
import {VCFormat} from '../../shared/VCFormat';
import {
getIssuerAuthenticationAlorithmForMdocVC,
@@ -218,7 +218,7 @@ export const openID4VPActions = (model: any) => {
),
shareDeclineStatus: () => {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
@@ -290,7 +290,7 @@ function getVcsMatchingAuthRequest(context, event) {
}
if (Object.keys(matchingVCs).length === 0) {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.NO_MATCHING_VCS,
OVP_ERROR_CODE.NO_MATCHING_VCS,
);

View File

@@ -5,16 +5,16 @@ import {
} from '../../shared/cryptoutil/cryptoUtil';
import {getJWK, hasKeyPair} from '../../shared/openId4VCI/Utils';
import base64url from 'base64url';
import {
constructDetachedJWT,
isClientValidationRequired,
OpenID4VP,
} from '../../shared/openID4VP/OpenID4VP';
import OpenID4VP from '../../shared/openID4VP/OpenID4VP';
import {VCFormat} from '../../shared/VCFormat';
import {KeyTypes} from '../../shared/cryptoutil/KeyTypes';
import {getMdocAuthenticationAlorithm} from '../../components/VC/common/VCUtils';
import {isIOS} from '../../shared/constants';
import {canonicalize} from '../../shared/Utils';
import {
constructDetachedJWT,
isClientValidationRequired,
} from '../../shared/openID4VP/OpenID4VPHelper';
const signatureSuite = 'JsonWebSignature2020';
@@ -29,8 +29,7 @@ export const openID4VPServices = () => {
},
getAuthenticationResponse: (context: any) => async () => {
OpenID4VP.initialize();
const serviceRes = await OpenID4VP.authenticateVerifier(
const serviceRes = await OpenID4VP.getInstance().authenticateVerifier(
context.urlEncodedAuthorizationRequest,
context.trustedVerifiers,
);
@@ -48,10 +47,12 @@ export const openID4VPServices = () => {
},
sendVP: (context: any) => async () => {
const openid = OpenID4VP.getInstance();
const jwk = await getJWK(context.publicKey, context.keyType);
const holderId = 'did:jwk:' + base64url(JSON.stringify(jwk)) + '#0';
const unSignedVpTokens = await OpenID4VP.constructUnsignedVPToken(
const unSignedVpTokens = await openid.constructUnsignedVPToken(
context.selectedVCs,
holderId,
signatureSuite,
@@ -136,9 +137,7 @@ export const openID4VPServices = () => {
vpTokenSigningResultMap[formatType] = signedData;
}
}
return await OpenID4VP.shareVerifiablePresentation(
vpTokenSigningResultMap,
);
return await openid.shareVerifiablePresentation(vpTokenSigningResultMap);
},
};
};

View File

@@ -1,133 +1,142 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import React, {useEffect, useRef, useState, useCallback} from 'react';
import psl from 'psl';
import {
View,
ActivityIndicator,
Alert,
TouchableOpacity,
Text, BackHandler
View,
ActivityIndicator,
Alert,
TouchableOpacity,
Text,
BackHandler,
} from 'react-native';
import { WebView } from 'react-native-webview';
import { Ionicons } from '@expo/vector-icons';
import { VciClient } from '../shared/vciClient/VciClient';
import { Theme } from '../components/ui/styleUtils';
import { useTranslation } from 'react-i18next';
import {WebView} from 'react-native-webview';
import {Ionicons} from '@expo/vector-icons';
import VciClient from '../shared/vciClient/VciClient';
import {Theme} from '../components/ui/styleUtils';
import {useTranslation} from 'react-i18next';
const AuthWebViewScreen: React.FC<any> = ({ route, navigation }) => {
const { authorizationURL, clientId, redirectUri, controller } = route.params;
const webViewRef = useRef<WebView>(null);
const [showWebView, setShowWebView] = useState(false);
const { t } = useTranslation('authWebView');
const AuthWebViewScreen: React.FC<any> = ({route, navigation}) => {
const {authorizationURL, clientId, redirectUri, controller} = route.params;
const webViewRef = useRef<WebView>(null);
const [showWebView, setShowWebView] = useState(false);
const {t} = useTranslation('authWebView');
const hostName = new URL(authorizationURL).hostname; // example.mosip.net
const parsed = psl.parse(hostName);
const rootDomain = parsed.domain || hostName;
const ALERT_TITLE = t('title', {
wallet: "Inji Wallet",
domain: rootDomain || 'mosip.net'
});
const ALERT_MESSAGE = t('message');
const hostName = new URL(authorizationURL).hostname; // example.mosip.net
const parsed = psl.parse(hostName);
const rootDomain = parsed.domain || hostName;
const ALERT_TITLE = t('title', {
wallet: 'Inji Wallet',
domain: rootDomain || 'mosip.net',
});
const ALERT_MESSAGE = t('message');
const handleBackPress = useCallback(() => {
return true;
}, []);
const handleBackPress = useCallback(() => {
return true;
}, []);
useEffect(() => {
if (!authorizationURL || !clientId || !redirectUri) {
console.error('Missing required parameters for authentication');
navigation.goBack();
return;
}
useEffect(() => {
if (!authorizationURL || !clientId || !redirectUri) {
console.error('Missing required parameters for authentication');
navigation.goBack();
return;
}
navigation.setOptions({gestureEnabled: false});
navigation.setOptions({ gestureEnabled: false });
const backHandler = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
Alert.alert(ALERT_TITLE, ALERT_MESSAGE, [
{
text: t('cancel'),
style: 'cancel',
onPress: () => {
controller.CANCEL();
navigation.goBack();
},
},
{
text: t('continue'),
style: 'default',
onPress: () => setShowWebView(true),
},
]);
return () => backHandler.remove();
}, [authorizationURL, clientId, redirectUri, navigation, controller, handleBackPress]);
const handleNavigationRequest = (request: any) => {
const { url } = request;
if (url.startsWith(redirectUri)) {
try {
const uri = new URL(url);
const code = uri.searchParams.get('code');
if (!code) {
controller.CANCEL();
navigation.goBack();
return false;
}
VciClient.client.sendAuthCodeFromJS(code);
navigation.goBack();
return false;
} catch (err: any) {
console.error('Error parsing redirect URL:', err);
controller.CANCEL();
navigation.goBack();
return false;
}
}
return true;
};
const Header = () => (
<View style={Theme.AuthWebViewScreenStyle.header}>
<TouchableOpacity onPress={() => {
controller.CANCEL();
navigation.goBack();
}}>
<Ionicons name="arrow-back" size={24} color="black" />
</TouchableOpacity>
<Text style={Theme.AuthWebViewScreenStyle.headerText}>Authenticate</Text>
<View style={{ width: 24 }} />
</View>
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
handleBackPress,
);
return (
<View style={{ flex: 1 }}>
<Header />
{showWebView && (
<WebView
ref={webViewRef}
originWhitelist={['*']}
source={{ uri: authorizationURL }}
onShouldStartLoadWithRequest={handleNavigationRequest}
startInLoadingState
renderLoading={() => (
<View style={Theme.AuthWebViewScreenStyle.loader}>
<ActivityIndicator size="large" />
</View>
)}
javaScriptEnabled
incognito
sharedCookiesEnabled={false}
thirdPartyCookiesEnabled={false}
/>
)}
</View>
);
Alert.alert(ALERT_TITLE, ALERT_MESSAGE, [
{
text: t('cancel'),
style: 'cancel',
onPress: () => {
controller.CANCEL();
navigation.goBack();
},
},
{
text: t('continue'),
style: 'default',
onPress: () => setShowWebView(true),
},
]);
return () => backHandler.remove();
}, [
authorizationURL,
clientId,
redirectUri,
navigation,
controller,
handleBackPress,
]);
const handleNavigationRequest = (request: any) => {
const {url} = request;
if (url.startsWith(redirectUri)) {
try {
const uri = new URL(url);
const code = uri.searchParams.get('code');
if (!code) {
controller.CANCEL();
navigation.goBack();
return false;
}
VciClient.getInstance().sendAuthCode(code);
navigation.goBack();
return false;
} catch (err: any) {
console.error('Error parsing redirect URL:', err);
controller.CANCEL();
navigation.goBack();
return false;
}
}
return true;
};
const Header = () => (
<View style={Theme.AuthWebViewScreenStyle.header}>
<TouchableOpacity
onPress={() => {
controller.CANCEL();
navigation.goBack();
}}>
<Ionicons name="arrow-back" size={24} color="black" />
</TouchableOpacity>
<Text style={Theme.AuthWebViewScreenStyle.headerText}>Authenticate</Text>
<View style={{width: 24}} />
</View>
);
return (
<View style={{flex: 1}}>
<Header />
{showWebView && (
<WebView
ref={webViewRef}
originWhitelist={['*']}
source={{uri: authorizationURL}}
onShouldStartLoadWithRequest={handleNavigationRequest}
startInLoadingState
renderLoading={() => (
<View style={Theme.AuthWebViewScreenStyle.loader}>
<ActivityIndicator size="large" />
</View>
)}
javaScriptEnabled
incognito
sharedCookiesEnabled={false}
thirdPartyCookiesEnabled={false}
/>
)}
</View>
);
};
export default AuthWebViewScreen;

View File

@@ -28,7 +28,7 @@ import {SvgImage} from '../../components/ui/svg';
import {Loader} from '../../components/ui/Loader';
import {Icon} from 'react-native-elements';
import {ScanLayoutProps} from '../../routes/routeTypes';
import {OpenID4VP} from '../../shared/openID4VP/OpenID4VP';
import OpenID4VP from '../../shared/openID4VP/OpenID4VP';
import {GlobalContext} from '../../shared/GlobalContext';
import {APP_EVENTS} from '../../machines/app';
import {useScanScreen} from './ScanScreenController';
@@ -47,7 +47,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
if (controller.errorModal.show && controller.isOVPViaDeepLink) {
const timeout = setTimeout(
() => {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.NO_MATCHING_VCS,
OVP_ERROR_CODE.NO_MATCHING_VCS,
);
@@ -107,7 +107,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
});
const handleDismiss = () => {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
@@ -120,7 +120,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
};
const handleRejectButtonEvent = () => {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
@@ -209,7 +209,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
const getPrimaryButtonEvent = () => {
if (controller.showConfirmationPopup && controller.isOVPViaDeepLink) {
return () => {
OpenID4VP.sendErrorToVerifier(
OpenID4VP.getInstance().sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);

View File

@@ -4,21 +4,28 @@ import {
SelectedCredentialsForVPSharing,
VC,
} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
import {createSignatureED, encodeB64} from '../cryptoutil/cryptoUtil';
import getAllConfigurations from '../api';
import {base64ToByteArray, parseJSON} from '../Utils';
import {walletMetadata} from './walletMetadata';
import {getWalletMetadata, isClientValidationRequired} from './OpenID4VPHelper';
import {parseJSON} from '../Utils';
export const OpenID4VP_Proof_Sign_Algo = 'EdDSA';
export class OpenID4VP {
static InjiOpenID4VP = NativeModules.InjiOpenID4VP;
class OpenID4VP {
private static instance: OpenID4VP;
private InjiOpenID4VP = NativeModules.InjiOpenID4VP;
static initialize() {
OpenID4VP.InjiOpenID4VP.init(__AppId.getValue());
private constructor() {
this.InjiOpenID4VP.init(__AppId.getValue());
}
static async authenticateVerifier(
public static getInstance(): OpenID4VP {
if (!OpenID4VP.instance) {
OpenID4VP.instance = new OpenID4VP();
}
return OpenID4VP.instance;
}
async authenticateVerifier(
urlEncodedAuthorizationRequest: string,
trustedVerifiersList: any,
) {
@@ -26,7 +33,7 @@ export class OpenID4VP {
const metadata = (await getWalletMetadata()) || walletMetadata;
const authenticationResponse =
await OpenID4VP.InjiOpenID4VP.authenticateVerifier(
await this.InjiOpenID4VP.authenticateVerifier(
urlEncodedAuthorizationRequest,
trustedVerifiersList,
metadata,
@@ -35,83 +42,49 @@ export class OpenID4VP {
return JSON.parse(authenticationResponse);
}
static async constructUnsignedVPToken(
async constructUnsignedVPToken(
selectedVCs: Record<string, VC[]>,
holderId,
signatureAlgorithm,
holderId: string,
signatureAlgorithm: string,
) {
let updatedSelectedVCs = this.processSelectedVCs(selectedVCs);
const unSignedVpTokens =
await OpenID4VP.InjiOpenID4VP.constructUnsignedVPToken(
updatedSelectedVCs,
holderId,
signatureAlgorithm,
);
const updatedSelectedVCs = this.processSelectedVCs(selectedVCs);
const unSignedVpTokens = await this.InjiOpenID4VP.constructUnsignedVPToken(
updatedSelectedVCs,
holderId,
signatureAlgorithm,
);
return parseJSON(unSignedVpTokens);
}
static async shareVerifiablePresentation(
async shareVerifiablePresentation(
vpTokenSigningResultMap: Record<string, any>,
) {
return await OpenID4VP.InjiOpenID4VP.shareVerifiablePresentation(
return await this.InjiOpenID4VP.shareVerifiablePresentation(
vpTokenSigningResultMap,
);
}
static sendErrorToVerifier(errorMessage: string, errorCode: string) {
OpenID4VP.InjiOpenID4VP.sendErrorToVerifier(errorMessage, errorCode);
sendErrorToVerifier(errorMessage: string, errorCode: string) {
this.InjiOpenID4VP.sendErrorToVerifier(errorMessage, errorCode);
}
private static processSelectedVCs(selectedVCs: Record<string, VC[]>) {
private processSelectedVCs(selectedVCs: Record<string, VC[]>) {
const selectedVcsData: SelectedCredentialsForVPSharing = {};
Object.entries(selectedVCs).forEach(([inputDescriptorId, vcsArray]) => {
vcsArray.forEach(vcData => {
const credentialFormat = vcData.vcMetadata.format;
vcData = vcData.verifiableCredential.credential;
const credential = vcData.verifiableCredential.credential;
if (!selectedVcsData[inputDescriptorId]) {
selectedVcsData[inputDescriptorId] = {};
}
if (!selectedVcsData[inputDescriptorId][credentialFormat]) {
selectedVcsData[inputDescriptorId][credentialFormat] = [];
}
selectedVcsData[inputDescriptorId][credentialFormat].push(vcData);
selectedVcsData[inputDescriptorId][credentialFormat].push(credential);
});
});
return selectedVcsData;
}
}
export async function constructDetachedJWT(
privateKey: any,
vpToken: string,
keyType: string,
): Promise<string> {
const jwtHeader = {
alg: OpenID4VP_Proof_Sign_Algo,
crit: ['b64'],
b64: false,
};
const header64 = encodeB64(JSON.stringify(jwtHeader));
const headerBytes = new TextEncoder().encode(header64);
const vpTokenBytes = base64ToByteArray(vpToken);
const payloadBytes = new Uint8Array([...headerBytes, 46, ...vpTokenBytes]);
const signature = await createSignatureED(privateKey, payloadBytes);
return header64 + '..' + signature;
}
export async function isClientValidationRequired() {
const config = await getAllConfigurations();
return config.openid4vpClientValidation === 'true';
}
export async function getWalletMetadata() {
const config = await getAllConfigurations();
if (!config.walletMetadata) {
return null;
}
const walletMetadata = JSON.parse(config.walletMetadata);
return walletMetadata;
}
export default OpenID4VP;

View File

@@ -0,0 +1,38 @@
import {createSignatureED, encodeB64} from '../cryptoutil/cryptoUtil';
import {base64ToByteArray} from '../Utils';
import getAllConfigurations from '../api';
import {OpenID4VP_Proof_Sign_Algo} from './OpenID4VP';
export async function constructDetachedJWT(
privateKey: any,
vpToken: string,
keyType: string,
): Promise<string> {
const jwtHeader = {
alg: OpenID4VP_Proof_Sign_Algo,
crit: ['b64'],
b64: false,
};
const header64 = encodeB64(JSON.stringify(jwtHeader));
const headerBytes = new TextEncoder().encode(header64);
const vpTokenBytes = base64ToByteArray(vpToken);
const payloadBytes = new Uint8Array([...headerBytes, 46, ...vpTokenBytes]);
const signature = await createSignatureED(privateKey, payloadBytes);
return header64 + '..' + signature;
}
export async function isClientValidationRequired() {
const config = await getAllConfigurations();
return config.openid4vpClientValidation === 'true';
}
export async function getWalletMetadata() {
const config = await getAllConfigurations();
if (!config.walletMetadata) {
return null;
}
const walletMetadata = JSON.parse(config.walletMetadata);
return walletMetadata;
}

View File

@@ -4,14 +4,38 @@ import {VerifiableCredential} from '../../machines/VerifiableCredential/VCMetaMa
const emitter = new NativeEventEmitter(NativeModules.InjiVciClient);
export class VciClient {
static get client() {
const nativeClient = NativeModules.InjiVciClient;
nativeClient.init(__AppId.getValue());
return nativeClient;
class VciClient {
private static instance: VciClient;
private InjiVciClient = NativeModules.InjiVciClient;
private constructor() {
this.InjiVciClient.init(__AppId.getValue());
}
static async requestCredentialByOffer(
static getInstance(): VciClient {
if (!VciClient.instance) {
VciClient.instance = new VciClient();
}
return VciClient.instance;
}
async sendProof(jwt: string) {
this.InjiVciClient.sendProofFromJS(jwt);
}
async sendAuthCode(authCode: string) {
this.InjiVciClient.sendAuthCodeFromJS(authCode);
}
async sendTxCode(code: string) {
this.InjiVciClient.sendTxCodeFromJS(code);
}
async sendIssuerConsent(consent: boolean) {
this.InjiVciClient.sendIssuerTrustResponseFromJS(consent);
}
async requestCredentialByOffer(
credentialOffer: string,
getTxCode: (
inputMode: string | undefined,
@@ -19,22 +43,17 @@ export class VciClient {
length: number | undefined,
) => void,
getProofJwt: (
accesToken: string,
accessToken: string,
cNonce: string | null,
issuerMetadata: object,
credentialConfigurationId: string,
) => void,
navigateToAuthView: (authorizationEndpoint: string) => void,
requestTrustIssuerConsent: (issuerMetadata: object) => void,
) {
): Promise<VerifiableCredential> {
const proofListener = emitter.addListener(
'onRequestProof',
async ({
accessToken,
cNonce,
issuerMetadata,
credentialConfigurationId,
}) => {
({accessToken, cNonce, issuerMetadata, credentialConfigurationId}) => {
getProofJwt(
accessToken,
cNonce,
@@ -46,27 +65,28 @@ export class VciClient {
const authListener = emitter.addListener(
'onRequestAuthCode',
async ({authorizationEndpoint}) => {
({authorizationEndpoint}) => {
navigateToAuthView(authorizationEndpoint);
},
);
const txCodeListener = emitter.addListener(
'onRequestTxCode',
async ({inputMode, description, length}) => {
({inputMode, description, length}) => {
getTxCode(inputMode, description, length);
},
);
const trustIssuerListener = emitter.addListener(
'onCheckIssuerTrust',
async ({issuerMetadata}) => {
const issuerMetadataObject = JSON.parse(issuerMetadata);
requestTrustIssuerConsent(issuerMetadataObject);
({issuerMetadata}) => {
requestTrustIssuerConsent(JSON.parse(issuerMetadata));
},
);
let response = '';
try {
response = await VciClient.client.requestCredentialByOffer(
response = await this.InjiVciClient.requestCredentialByOffer(
credentialOffer,
JSON.stringify({
clientId: 'wallet',
@@ -76,24 +96,25 @@ export class VciClient {
} catch (error) {
console.error('Error requesting credential by offer:', error);
throw error;
} finally {
proofListener.remove();
authListener.remove();
txCodeListener.remove();
trustIssuerListener.remove();
}
proofListener.remove();
authListener.remove();
txCodeListener.remove();
trustIssuerListener.remove();
return JSON.parse(response) as VerifiableCredential;
}
static async requestCredentialFromTrustedIssuer(
resolvedIssuerMetaData: Object,
clientMetadata: Object,
getProofJwt: (accessToken: string, cNonce: string) => Promise<void>,
async requestCredentialFromTrustedIssuer(
resolvedIssuerMetaData: object,
clientMetadata: object,
getProofJwt: (accessToken: string, cNonce: string) => void,
navigateToAuthView: (authorizationEndpoint: string) => void,
) {
): Promise<VerifiableCredential> {
const proofListener = emitter.addListener(
'onRequestProof',
async ({accessToken, cNonce}) => {
({accessToken, cNonce}) => {
getProofJwt(accessToken, cNonce);
proofListener.remove();
},
@@ -105,20 +126,23 @@ export class VciClient {
navigateToAuthView(authorizationEndpoint);
},
);
let response = '';
try {
response = await VciClient.client.requestCredentialFromTrustedIssuer(
response = await this.InjiVciClient.requestCredentialFromTrustedIssuer(
JSON.stringify(resolvedIssuerMetaData),
JSON.stringify(clientMetadata),
);
} catch (error) {
console.error('Error requesting credential from trusted issuer:', error);
throw error;
} finally {
proofListener.remove();
authListener.remove();
}
proofListener.remove();
authListener.remove();
return JSON.parse(response);
return JSON.parse(response) as VerifiableCredential;
}
}
export default VciClient;