From dad7417fdbc1bbd9e13508a742ea4c2e61a32e74 Mon Sep 17 00:00:00 2001 From: KiruthikaJeyashankar Date: Wed, 15 Oct 2025 15:49:21 +0530 Subject: [PATCH] [INJIMOB-3550] | [INJIMOB-3551] : refactor: replace OVP shareVerifiablePresentation with sendAuthorizationResponseToVerifier (#2104) * [INJIMOB-3550] refactor: update ovp java module to use sendAuthorizationResponse instead of shareVerifiablePresentation Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3551] refactor: modify ovp swift native module Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3550] refactor: modify sendError to verifier as fire and forget call Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3550] refactor: modify error's verifier response param from native module Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3550] refactor: modiy error message for verifier returning non 200 response Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3550] fix: app stuck on loading - trusted verifiers api failure Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3550] refactor: modify verifier response parsing Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3551] chore: update inji-openid4vp-ios-swift lib version Signed-off-by: KiruthikaJeyashankar * [INJIMOB-3534] chore: update inji-openid4vp-ios-swift version Signed-off-by: KiruthikaJeyashankar --------- Signed-off-by: KiruthikaJeyashankar --- .../residentapp/InjiOpenID4VPModule.java | 44 +++++++++---------- .../xcshareddata/swiftpm/Package.resolved | 2 +- ios/RNOpenID4VPModule.swift | 33 ++++++++++++-- locales/ara.json | 4 ++ locales/en.json | 4 ++ locales/fil.json | 6 ++- locales/hin.json | 7 ++- locales/kan.json | 4 ++ locales/tam.json | 4 ++ machines/openID4VP/openID4VPActions.ts | 4 +- machines/openID4VP/openID4VPMachine.ts | 10 ++++- machines/openID4VP/openID4VPServices.ts | 10 ++++- screens/Scan/SendVPScreen.tsx | 12 +++-- shared/hooks/useOvpErrorModal.ts | 9 ++++ shared/openID4VP/OpenID4VP.ts | 8 ++-- 15 files changed, 120 insertions(+), 41 deletions(-) diff --git a/android/app/src/main/java/io/mosip/residentapp/InjiOpenID4VPModule.java b/android/app/src/main/java/io/mosip/residentapp/InjiOpenID4VPModule.java index 05e2403c..8f5ee4c3 100644 --- a/android/app/src/main/java/io/mosip/residentapp/InjiOpenID4VPModule.java +++ b/android/app/src/main/java/io/mosip/residentapp/InjiOpenID4VPModule.java @@ -1,10 +1,12 @@ package io.mosip.residentapp; import static io.mosip.openID4VP.authorizationResponse.AuthorizationResponseUtilsKt.toJsonString; +import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.ACCESS_DENIED; +import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.INVALID_TRANSACTION_DATA; +import static io.mosip.openID4VP.constants.FormatType.DC_SD_JWT; import static io.mosip.openID4VP.constants.FormatType.LDP_VC; import static io.mosip.openID4VP.constants.FormatType.MSO_MDOC; import static io.mosip.openID4VP.constants.FormatType.VC_SD_JWT; -import static io.mosip.openID4VP.constants.FormatType.DC_SD_JWT; import android.annotation.SuppressLint; import android.util.Log; @@ -26,31 +28,17 @@ import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.mosip.openID4VP.authorizationRequest.clientMetadata.ClientMetadata; -import io.mosip.openID4VP.authorizationRequest.clientMetadata.ClientMetadataSerializer; -import io.mosip.openID4VP.authorizationRequest.clientMetadata.Jwks; -import io.mosip.openID4VP.constants.ClientIdScheme; -import io.mosip.openID4VP.constants.ContentEncryptionAlgorithm; -import io.mosip.openID4VP.constants.KeyManagementAlgorithm; -import io.mosip.openID4VP.constants.RequestSigningAlgorithm; -import io.mosip.openID4VP.constants.ResponseType; -import io.mosip.openID4VP.constants.VPFormatType; -import io.mosip.openID4VP.exceptions.OpenID4VPExceptions; - -import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.ACCESS_DENIED; -import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.INVALID_TRANSACTION_DATA; - import org.json.JSONArray; import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; -import java.util.function.Function; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import io.mosip.openID4VP.OpenID4VP; import io.mosip.openID4VP.authorizationRequest.AuthorizationRequest; @@ -63,9 +51,15 @@ import io.mosip.openID4VP.authorizationResponse.vpTokenSigningResult.types.ldp.L import io.mosip.openID4VP.authorizationResponse.vpTokenSigningResult.types.mdoc.DeviceAuthentication; import io.mosip.openID4VP.authorizationResponse.vpTokenSigningResult.types.mdoc.MdocVPTokenSigningResult; import io.mosip.openID4VP.authorizationResponse.vpTokenSigningResult.types.sdJwt.SdJwtVPTokenSigningResult; +import io.mosip.openID4VP.constants.ClientIdScheme; +import io.mosip.openID4VP.constants.ContentEncryptionAlgorithm; import io.mosip.openID4VP.constants.FormatType; +import io.mosip.openID4VP.constants.KeyManagementAlgorithm; +import io.mosip.openID4VP.constants.RequestSigningAlgorithm; +import io.mosip.openID4VP.constants.ResponseType; +import io.mosip.openID4VP.constants.VPFormatType; +import io.mosip.openID4VP.exceptions.OpenID4VPExceptions; import io.mosip.openID4VP.networkManager.NetworkResponse; -import kotlinx.serialization.json.Json; public class InjiOpenID4VPModule extends ReactContextBaseJavaModule { private static final String TAG = "InjiOpenID4VPModule"; @@ -135,20 +129,22 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule { public void shareVerifiablePresentation(ReadableMap vpTokenSigningResultMap, Promise promise) { try { Map authContainer = parseVPTokenSigningResult(vpTokenSigningResultMap); - String response = openID4VP.shareVerifiablePresentation(authContainer); - promise.resolve(response); + NetworkResponse verifierResponse = openID4VP.sendAuthorizationResponseToVerifier(authContainer); + String verifierResponseJson = gson.toJson(verifierResponse, NetworkResponse.class); + + promise.resolve(verifierResponseJson); } catch (Exception e) { rejectWithOpenID4VPExceptions(e, promise); } } @ReactMethod - private static void rejectWithOpenID4VPExceptions(Exception e, Promise promise) { + private void rejectWithOpenID4VPExceptions(Exception e, Promise promise) { if (e instanceof OpenID4VPExceptions exception) { WritableMap errorMap = Arguments.createMap(); errorMap.putString("errorCode", exception.getErrorCode()); errorMap.putString("message", exception.getMessage()); - errorMap.putString("response", exception.getResponse()); + errorMap.putString("response", gson.toJson(exception.getNetworkResponse())); promise.reject(exception.getErrorCode(), exception.getMessage(), exception, errorMap); } else { @@ -173,8 +169,10 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule { break; } - String verifierResponse = openID4VP.sendErrorResponseToVerifier(exception); - promise.resolve(verifierResponse); + NetworkResponse verifierResponse = openID4VP.sendErrorResponseToVerifier(exception); + String verifierResponseJson = gson.toJson(verifierResponse, NetworkResponse.class); + + promise.resolve(verifierResponseJson); } catch (Exception exception) { rejectWithOpenID4VPExceptions(exception, promise); } diff --git a/ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved index a9b9c642..6803e966 100644 --- a/ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -79,7 +79,7 @@ "location" : "https://github.com/mosip/inji-openid4vp-ios-swift.git", "state" : { "branch" : "develop", - "revision" : "9a2551d1a936c53e80e4ade6122543d606a99504" + "revision" : "01fa8c01f5b9ee16b5377073f061ca0186288df4" } }, { diff --git a/ios/RNOpenID4VPModule.swift b/ios/RNOpenID4VPModule.swift index 94c5cd44..52548787 100644 --- a/ios/RNOpenID4VPModule.swift +++ b/ios/RNOpenID4VPModule.swift @@ -161,8 +161,8 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule { } } - let response = try await openID4VP?.shareVerifiablePresentation(vpTokenSigningResults: formattedVPTokenSigningResults) - resolve(response) + let verifierResponse = try await openID4VP?.sendAuthorizationResponseToVerifier(vpTokenSigningResults: formattedVPTokenSigningResults) + try resolveToJsonData(verifierResponse, resolver: resolve, rejecter: reject) } catch { rejectWithOpenID4VPError(error, reject: reject) } @@ -187,7 +187,7 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule { do { let verifierResponse = try await openID4VP?.sendErrorResponseToVerifier(error: exception) - resolve(verifierResponse) + try resolveToJsonData(verifierResponse, resolver: resolve, rejecter: reject) } catch { rejectWithOpenID4VPError(error, reject: reject) } @@ -231,7 +231,7 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule { let errorMap: [String: Any] = [ "errorCode": openidError.errorCode, "message": openidError.message, - "response": openidError.response ?? "" + "response": Inji.toJsonString(openidError.networkResponse) ?? "" ] let nsError = NSError( domain: "OPENID4VP", @@ -313,3 +313,28 @@ func mapStringsToEnum( return match } } + +fileprivate func resolveToJsonData(_ response: NetworkResponse?,resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) throws { + let jsonData = try toJsonData(response) + + if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] { + resolve(jsonObject) + } else { + reject("ERROR", "Failed to serialize JSON", nil) + } +} + +fileprivate func toJsonString(_ input: T) -> String? where T: Encodable { + if let jsonData = try? toJsonData(input), + let jsonString = String(data: jsonData, encoding: .utf8) { + return jsonString + } + return nil +} + +fileprivate func toJsonData(_ input: T) throws -> Data where T: Encodable { + let encoder = JSONEncoder() + encoder.keyEncodingStrategy = .convertToSnakeCase + return try encoder.encode(input) +} diff --git a/locales/ara.json b/locales/ara.json index b86d4a0c..7f1ae728 100644 --- a/locales/ara.json +++ b/locales/ara.json @@ -921,6 +921,10 @@ "title": "حدث خطأ ما", "message": "حدثت مشكلة فنية أثناء مشاركة بطاقتك. انقر على \"إعادة المحاولة\" للمحاولة مرة أخرى أو ارجع إلى \"الرئيسية\"." }, + "verifierResponseError": { + "title": "تعذر على المحقق معالجة الاعتماد", + "message": "حدثت مشكلة أثناء معالجة المحقق لاعتمادك." + }, "noImage": { "title": "حدث خطأ!", "message": "يتطلب التحقق من الوجه صورة في بيانات الاعتماد المحددة. الرجاء استخدام خيار المشاركة أو تحديد بيانات اعتماد تتضمن صورة." diff --git a/locales/en.json b/locales/en.json index 7f16bf02..e38e249b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -933,6 +933,10 @@ "title": "Something Went Wrong", "message": "There was a technical issue while sharing your card. Tap \"Retry\" to try again or go back to \"Home\"." }, + "verifierResponseError": { + "title": "Verifier could not process the credential", + "message": "There was an issue while the verifier was processing your credential." + }, "noImage": { "title": "An Error Occured!", "message": "Face verification requires a photo in the selected credential(s). Please use the Share option or select a credential that includes an image." diff --git a/locales/fil.json b/locales/fil.json index af77a85d..f2da8e7f 100644 --- a/locales/fil.json +++ b/locales/fil.json @@ -924,6 +924,10 @@ "title": "May Nangyaring Mali", "message": "Nagkaroon ng teknikal na problema habang ibinabahagi ang iyong card. Pindutin ang \"Subukang Muli\" para subukang muli o bumalik sa \"Home\"." }, + "verifierResponseError": { + "title": "Hindi maproseso ng tagapagsuri ang kredensyal", + "message": "May naging isyu habang pinoproseso ng tagapagsuri ang iyong kredensyal." + }, "noImage": { "title": "Isang Error ang Naganap!", "message": "Ang pag-verify ng mukha ay nangangailangan ng larawan sa napiling (mga) kredensyal. Pakigamit ang opsyong Ibahagi o pumili ng kredensyal na may kasamang larawan." @@ -1131,4 +1135,4 @@ "verifierConfirm": "Oo, pinagkakatiwalaan ko ang verifier na ito", "cancel": "Hindi, Ibalik Mo Ako" } -} \ No newline at end of file +} diff --git a/locales/hin.json b/locales/hin.json index 032ab138..ba701201 100644 --- a/locales/hin.json +++ b/locales/hin.json @@ -927,6 +927,10 @@ "title": "कुछ गलत हो गया", "message": "आपका कार्ड साझा करते समय एक तकनीकी समस्या हुई। फिर से प्रयास करने के लिए \"पुनः प्रयास करें\" टैप करें या \"होम\" पर वापस जाएं।" }, + "verifierResponseError": { + "title": "सत्यापनकर्ता क्रेडेंशियल को प्रोसेस नहीं कर सका", + "message": "सत्यापनकर्ता द्वारा आपके क्रेडेंशियल को प्रोसेस करते समय एक समस्या आई।" + }, "noImage": { "title": "एक त्रुटि हुई!", "message": "चेहरे के सत्यापन के लिए चयनित क्रेडेंशियल में एक फोटो की आवश्यकता होती है। कृपया शेयर विकल्प का उपयोग करें या एक क्रेडेंशियल चुनें जिसमें एक छवि शामिल हो।" @@ -1132,4 +1136,5 @@ "verifierConfirm": "हाँ, मुझे इस सत्यापनकर्ता पर भरोसा है", "cancel": "नहीं, मुझे वापस ले चलें" } -} \ No newline at end of file +} + diff --git a/locales/kan.json b/locales/kan.json index 72064232..182431e1 100644 --- a/locales/kan.json +++ b/locales/kan.json @@ -925,6 +925,10 @@ "title": "ಏನೋ ತಪ್ಪು ಸಂಭವಿಸಿದೆ", "message": "ನಿಮ್ಮ ಕಾರ್ಡ್ ಹಂಚಿಕೊಳ್ಳುವಾಗ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆ ಸಂಭವಿಸಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು \"ಮರುಪ್ರಯತ್ನಿಸಿ\" ಒತ್ತಿರಿ ಅಥವಾ \"ಮುಖಪುಟ\" ಗೆ ಹಿಂದಿರುಗಿ." }, + "verifierResponseError": { + "title": "ಪರಿಶೀಲಕನು ರುಜುವಾತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ", + "message": "ಪರಿಶೀಲಕನು ನಿಮ್ಮ ರುಜುವಾತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಯಿತು." + }, "noImage": { "title": "ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ!", "message": "ಮುಖ ಪರಿಶೀಲನೆಗೆ ಆಯ್ಕೆಮಾಡಿದ ರುಜುವಾತು(ಗಳಲ್ಲಿ) ಫೋಟೋ ಅಗತ್ಯವಿದೆ. ದಯವಿಟ್ಟು ಹಂಚಿಕೆ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ ಅಥವಾ ಚಿತ್ರವನ್ನು ಒಳಗೊಂಡಿರುವ ರುಜುವಾತುಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ." diff --git a/locales/tam.json b/locales/tam.json index 98188efa..4c8a94fc 100644 --- a/locales/tam.json +++ b/locales/tam.json @@ -925,6 +925,10 @@ "title": "ஏதோ தவறு நடந்துவிட்டது", "message": "உங்கள் கார்டைப் பகிரும்போது தொழில்நுட்ப சிக்கல் ஏற்பட்டது. மீண்டும் முயற்சிக்க \"மீண்டும் முயற்சி செய்\" என்பதைத் தட்டவும் அல்லது \"முகப்பு\" பக்கம் திரும்பவும்." }, + "verifierResponseError": { + "title": "சான்றிதழை சரிபார்ப்பவர் செயலாக்க முடியவில்லை", + "message": "சான்றிதழை செயலாக்கும் போது சரிபார்ப்பவருக்கு சிக்கல் ஏற்பட்டது." + }, "noImage": { "title": "ஒரு பிழை ஏற்பட்டது!", "message": "முகம் சரிபார்ப்புக்கு தேர்ந்தெடுக்கப்பட்ட நற்சான்றிதழில்(களில்) ஒரு புகைப்படம் தேவை. பகிர் விருப்பத்தைப் பயன்படுத்தவும் அல்லது படத்தை உள்ளடக்கிய நற்சான்றிதழைத் தேர்ந்தெடுக்கவும்." diff --git a/machines/openID4VP/openID4VPActions.ts b/machines/openID4VP/openID4VPActions.ts index 7e1e0b7d..c4230b17 100644 --- a/machines/openID4VP/openID4VPActions.ts +++ b/machines/openID4VP/openID4VPActions.ts @@ -157,7 +157,7 @@ export const openID4VPActions = (model: any) => { error: (_, event) => { console.error( 'Error occurred during the authenticateVerifier call :', - event.data.userInfo, + event.data.userInfo, ); return event.data.code; }, @@ -165,7 +165,7 @@ export const openID4VPActions = (model: any) => { setTrustedVerifiersApiCallError: model.assign({ error: (_, event) => { - console.error('Error:', event.data.message); + console.error('Error while fetching trusted verifiers:', event.data); return 'api error - ' + event.data.message; }, }), diff --git a/machines/openID4VP/openID4VPMachine.ts b/machines/openID4VP/openID4VPMachine.ts index ff06adec..3cb66ded 100644 --- a/machines/openID4VP/openID4VPMachine.ts +++ b/machines/openID4VP/openID4VPMachine.ts @@ -82,7 +82,10 @@ export const openID4VPMachine = model.createMachine( target: 'getKeyPairFromKeystore', }, onError: { - actions: 'setTrustedVerifiersApiCallError', + actions: [ + 'setTrustedVerifiersApiCallError', + 'resetIsShowLoadingScreen', + ], }, }, }, @@ -442,6 +445,11 @@ export const openID4VPMachine = model.createMachine( shareVPDeclineStatusToVerifier: { invoke: { src: 'shareDeclineStatus', + onError: (_, event) => + console.error( + 'Failed to send decline status to verifier - ', + event.data, + ), }, after: { 200: { diff --git a/machines/openID4VP/openID4VPServices.ts b/machines/openID4VP/openID4VPServices.ts index 0ef694bd..9672d5ed 100644 --- a/machines/openID4VP/openID4VPServices.ts +++ b/machines/openID4VP/openID4VPServices.ts @@ -218,9 +218,17 @@ export const openID4VPServices = () => { vpTokenSigningResultMap[formatType] = uuidToSignature; } } - return await OpenID4VP.shareVerifiablePresentation( + const verifierResponse = await OpenID4VP.shareVerifiablePresentation( vpTokenSigningResultMap, ); + if (verifierResponse['status_code'] !== 200) { + console.error( + 'Error response from verifier during sharing the VP :', + verifierResponse, + ); + throw new Error('VERIFIER_RESPONSE_ERROR'); + } + return verifierResponse; }, }; }; diff --git a/screens/Scan/SendVPScreen.tsx b/screens/Scan/SendVPScreen.tsx index 17ae62b4..e5773d1a 100644 --- a/screens/Scan/SendVPScreen.tsx +++ b/screens/Scan/SendVPScreen.tsx @@ -68,7 +68,8 @@ export const SendVPScreen: React.FC = props => { if (errorModal.show && controller.isOVPViaDeepLink) { const timeout = setTimeout( async () => { - await OpenID4VP.sendErrorToVerifier( + // Send error to verifier is initiated and its response is not listened to here. + void OpenID4VP.sendErrorToVerifier( OVP_ERROR_MESSAGES.NO_MATCHING_VCS, OVP_ERROR_CODE.NO_MATCHING_VCS, ); @@ -132,7 +133,8 @@ export const SendVPScreen: React.FC = props => { }; const handleDismiss = async () => { - await OpenID4VP.sendErrorToVerifier( + // Send error to verifier is initiated and its response is not listened to here. + void OpenID4VP.sendErrorToVerifier( OVP_ERROR_MESSAGES.DECLINED, OVP_ERROR_CODE.DECLINED, ); @@ -147,7 +149,8 @@ export const SendVPScreen: React.FC = props => { }; const handleRejectButtonEvent = async () => { - await OpenID4VP.sendErrorToVerifier( + // Send error to verifier is initiated and its response is not listened to here. + void OpenID4VP.sendErrorToVerifier( OVP_ERROR_MESSAGES.DECLINED, OVP_ERROR_CODE.DECLINED, ); @@ -234,7 +237,8 @@ export const SendVPScreen: React.FC = props => { const getPrimaryButtonEvent = () => { if (controller.showConfirmationPopup && controller.isOVPViaDeepLink) { return async () => { - await OpenID4VP.sendErrorToVerifier( + // Send error to verifier is initiated and its response is not listened to here. + void OpenID4VP.sendErrorToVerifier( OVP_ERROR_MESSAGES.DECLINED, OVP_ERROR_CODE.DECLINED, ); diff --git a/shared/hooks/useOvpErrorModal.ts b/shared/hooks/useOvpErrorModal.ts index 8b5e3620..bc54faa5 100644 --- a/shared/hooks/useOvpErrorModal.ts +++ b/shared/hooks/useOvpErrorModal.ts @@ -147,6 +147,15 @@ export function useOvpErrorModal({ showRetryButton: false, }); generateAndStoreLogMessage('REQUEST_COULD_NOT_BE_PROCESSED'); + } else if (error.includes('VERIFIER_RESPONSE_ERROR')) { + setErrorModal({ + show: true, + title: t('errors.verifierResponseError.title'), + message: t('errors.verifierResponseError.message'), + additionalMessage, + showRetryButton: false, + }); + generateAndStoreLogMessage('SEND_VP_ERROR'); } else if (error.startsWith('send vp')) { setErrorModal({ show: true, diff --git a/shared/openID4VP/OpenID4VP.ts b/shared/openID4VP/OpenID4VP.ts index f9d9f2bb..3d299ce2 100644 --- a/shared/openID4VP/OpenID4VP.ts +++ b/shared/openID4VP/OpenID4VP.ts @@ -71,9 +71,11 @@ class OpenID4VP { ) { const openID4VP = await OpenID4VP.getInstance(); - return await openID4VP.InjiOpenID4VP.shareVerifiablePresentation( - vpTokenSigningResultMap, - ); + const verifierResponse = + await openID4VP.InjiOpenID4VP.shareVerifiablePresentation( + vpTokenSigningResultMap, + ); + return parseJSON(verifierResponse); } static async sendErrorToVerifier(errorMessage: string, errorCode: string) {