Merge branch 'INJIMOB-3365' into develop

Signed-off-by: Swati Goel <meet2swati@gmail.com>
This commit is contained in:
Swati Goel
2025-08-01 00:05:18 +05:30
committed by GitHub
50 changed files with 1702 additions and 1896 deletions

View File

@@ -341,7 +341,7 @@ fileignoreconfig:
- filename: android/app/src/main/java/io/mosip/residentapp/InjiVciClientModule.java
checksum: 17f55840bab193bc353034445ba4fce53e1ce466e95f616c15a1351f8d2f23bc
- filename: ios/Inji.xcworkspace/xcshareddata/swiftpm/Package.resolved
checksum: 785f2a3390ffd7b93974f449132008c7c1b8b6d1e8deaba7b1d0737be1b5f2f4
checksum: 81d8398a4f4b052c3fb10d92f0e0af398d09095d9f882cc35335c43630c6c371
- filename: injitest/src/main/resources/Vids.json
checksum: 8bcffed7a6dd565ae695e1b29de0655e10bd5c5420af2718defd593a687b8817
- filename: injitest/src/main/java/inji/utils/UpdateNetworkSettings.java
@@ -398,7 +398,8 @@ fileignoreconfig:
checksum: 6600a3d75033af4d702dd8c9663e12ad7c2c096a529bac2771bb856cc75a5ed0
- filename: machines/openID4VP/openID4VPModel.ts
checksum: 5d1ed430f84852d6c85bc439c47641cfb5b19cbd1a03faf8918429685db51e07
version: ""
- filename: shared/hooks/useOvpErrorModal.ts
checksum: dc83cf9d688fda587cfb10e738b966694794181f233dd1b6f5cf3f818dae412d
- filename: shared/openID4VP/OpenID4VPHelper.ts
checksum: 2ab5f935ea3d1ec4d109d8614c2246f40e284594288566338f185611470e6928
- filename: components/VC/common/VCUtils.tsx

View File

@@ -1,3 +1,4 @@
import 'react-native-get-random-values';
import React, {useContext, useEffect, useState} from 'react';
import {AppLayout} from './screens/AppLayout';
import {useFont} from './shared/hooks/useFont';

View File

@@ -280,7 +280,7 @@ dependencies {
implementation("io.mosip:pixelpass-aar:0.7.0-SNAPSHOT")
implementation("io.mosip:secure-keystore:0.3.0")
implementation("io.mosip:tuvali:0.5.2")
implementation("io.mosip:inji-vci-client:0.4.0-SNAPSHOT")
implementation("io.mosip:inji-vci-client-aar:0.4.0-SNAPSHOT")
implementation("com.google.code.gson:gson:2.10.1")
implementation("io.mosip:vcverifier-aar:1.3.0-SNAPSHOT") {
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'

View File

@@ -4,6 +4,7 @@ import static io.mosip.openID4VP.authorizationResponse.AuthorizationResponseUtil
import static io.mosip.openID4VP.constants.FormatType.LDP_VC;
import static io.mosip.openID4VP.constants.FormatType.MSO_MDOC;
import android.annotation.SuppressLint;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -20,20 +21,23 @@ import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.mosip.openID4VP.constants.ClientIdScheme;
import io.mosip.openID4VP.constants.ContentEncrytionAlgorithm;
import io.mosip.openID4VP.constants.KeyManagementAlgorithm;
import io.mosip.openID4VP.constants.RequestSigningAlgorithm;
import io.mosip.openID4VP.exceptions.OpenID4VPExceptions;
import io.mosip.openID4VP.exceptions.OpenID4VPExceptions.AccessDenied;
import io.mosip.openID4VP.exceptions.OpenID4VPExceptions.InvalidTransactionData;
import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.ACCESS_DENIED;
import static io.mosip.openID4VP.common.OpenID4VPErrorCodes.INVALID_TRANSACTION_DATA;
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.Optional;
import io.mosip.openID4VP.OpenID4VP;
import io.mosip.openID4VP.authorizationRequest.AuthorizationRequest;
@@ -64,10 +68,14 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
return MODULE_NAME;
}
@SuppressLint("LogNotTimber")
@ReactMethod
public void init(String appId) {
public void initSdk(String appId, ReadableMap walletMetadata ) {
Log.d(TAG, "Initializing InjiOpenID4VPModule with " + appId);
openID4VP = new OpenID4VP(appId);
WalletMetadata metadata = parseWalletMetadata(walletMetadata);
openID4VP = new OpenID4VP(appId, metadata);
gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.disableHtmlEscaping()
@@ -77,29 +85,21 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
@ReactMethod
public void authenticateVerifier(String urlEncodedAuthorizationRequest,
ReadableArray trustedVerifiers,
ReadableMap walletMetadata,
Boolean shouldValidateClient,
Promise promise) {
try {
WalletMetadata walletMetadataObj = parseWalletMetadata(walletMetadata);
List<Verifier> verifierList = parseVerifiers(trustedVerifiers);
AuthorizationRequest authRequest = openID4VP.authenticateVerifier(
urlEncodedAuthorizationRequest,
verifierList,
shouldValidateClient,
walletMetadataObj
shouldValidateClient
);
String authRequestJson = gson.toJson(authRequest, AuthorizationRequest.class);
promise.resolve(authRequestJson);
} catch (Exception e) {
if (e instanceof OpenID4VPExceptions) {
OpenID4VPExceptions ex = (OpenID4VPExceptions) e;
promise.reject(ex.getErrorCode(), ex.getMessage());
} else {
promise.reject(e);
}
rejectWithOpenID4VPExceptions(e, promise);
}
}
@@ -110,12 +110,7 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
Map<FormatType, UnsignedVPToken> vpTokens = openID4VP.constructUnsignedVPToken(selectedVCsMap, holderId, signatureSuite);
promise.resolve(toJsonString(vpTokens));
} catch (Exception e) {
if (e instanceof OpenID4VPExceptions) {
OpenID4VPExceptions ex = (OpenID4VPExceptions) e;
promise.reject(ex.getErrorCode(), ex.getMessage());
} else {
promise.reject(e);
}
rejectWithOpenID4VPExceptions(e, promise);
}
}
@@ -126,12 +121,17 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
String response = openID4VP.shareVerifiablePresentation(authContainer);
promise.resolve(response);
} catch (Exception e) {
if (e instanceof OpenID4VPExceptions) {
OpenID4VPExceptions ex = (OpenID4VPExceptions) e;
promise.reject(ex.getErrorCode(), ex.getMessage());
} else {
promise.reject(e);
}
rejectWithOpenID4VPExceptions(e, promise);
}
}
@ReactMethod
private static void rejectWithOpenID4VPExceptions(Exception e, Promise promise) {
if (e instanceof OpenID4VPExceptions) {
OpenID4VPExceptions ex = (OpenID4VPExceptions) e;
promise.reject(ex.getErrorCode(), ex.getMessage(), ex);
} else {
promise.reject("ERR_UNKNOWN", e.getMessage(), e);
}
}
@@ -159,30 +159,58 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
? walletMetadata.getBoolean("presentation_definition_uri_supported")
: null;
Map<String, VPFormatSupported> vpFormatsSupportedMap = new HashMap<>();
if (walletMetadata.hasKey("vp_formats_supported")) {
ReadableMap vpFormatsMap = walletMetadata.getMap("vp_formats_supported");
if (vpFormatsMap != null && vpFormatsMap.hasKey("ldp_vc")) {
ReadableMap ldpVc = vpFormatsMap.getMap("ldp_vc");
if (ldpVc != null && ldpVc.hasKey("alg_values_supported")) {
ReadableArray ldpVcAlgArray = ldpVc.getArray("alg_values_supported");
List<String> algValuesList = ldpVcAlgArray != null
? convertReadableArrayToList(ldpVcAlgArray)
: null;
vpFormatsSupportedMap.put("ldp_vc", new VPFormatSupported(algValuesList));
}
}
}
return new WalletMetadata(
Map<FormatType, VPFormatSupported> vpFormatsSupportedMap = parseVpFormatsSupported(walletMetadata);
ContentEncrytionAlgorithm algorithm = ContentEncrytionAlgorithm.Companion.fromValue("value");
WalletMetadata walletMetadata1 = new WalletMetadata(
presentationDefinitionUriSupported,
vpFormatsSupportedMap,
extractStringListOrNull(walletMetadata, "client_id_schemes_supported"),
extractStringListOrNull(walletMetadata, "request_object_signing_alg_values_supported"),
extractStringListOrNull(walletMetadata, "authorization_encryption_alg_values_supported"),
extractStringListOrNull(walletMetadata, "authorization_encryption_enc_values_supported")
convertReadableArrayToEnumList(walletMetadata, "client_id_schemes_supported", ClientIdScheme.Companion::fromValue),
convertReadableArrayToEnumList(walletMetadata, "request_object_signing_alg_values_supported", RequestSigningAlgorithm.Companion::fromValue),
convertReadableArrayToEnumList(walletMetadata, "authorization_encryption_alg_values_supported", KeyManagementAlgorithm.Companion::fromValue),
convertReadableArrayToEnumList(walletMetadata, "authorization_encryption_enc_values_supported", ContentEncrytionAlgorithm.Companion::fromValue)
);
System.out.println("Wallet Metadata: " + walletMetadata1);
return walletMetadata1;
}
private Map<FormatType, VPFormatSupported> parseVpFormatsSupported(ReadableMap walletMetadata) {
Map<FormatType, VPFormatSupported> vpFormatsSupportedMap = new HashMap<>();
if (walletMetadata.hasKey("vp_formats_supported")) {
ReadableMap vpFormatsMap = walletMetadata.getMap("vp_formats_supported");
if (vpFormatsMap != null) {
addVpFormatSupported(vpFormatsMap, "ldp_vc", vpFormatsSupportedMap);
addVpFormatSupported(vpFormatsMap, "mso_mdoc", vpFormatsSupportedMap);
}
}
return vpFormatsSupportedMap;
}
private <T> List<T> convertReadableArrayToEnumList(ReadableMap readableMap, String key, Function<String, T> converter) {
if (!readableMap.hasKey(key)) return null;
ReadableArray readableArray = readableMap.getArray(key);
List<T> list = new ArrayList<>();
for (int i = 0; i < Objects.requireNonNull(readableArray).size(); i++) {
list.add(converter.apply(readableArray.getString(i)));
}
return list;
}
private void addVpFormatSupported(ReadableMap vpFormatsMap, String key, Map<FormatType, VPFormatSupported> vpFormatsSupportedMap) {
if (vpFormatsMap.hasKey(key)) {
ReadableMap formatMap = vpFormatsMap.getMap(key);
if (formatMap != null && formatMap.hasKey("alg_values_supported")) {
ReadableArray algArray = formatMap.getArray("alg_values_supported");
List<String> algValuesList = algArray != null ? convertReadableArrayToList(algArray) : null;
vpFormatsSupportedMap.put(FormatType.Companion.fromValue(key), new VPFormatSupported(algValuesList));
}
}
}
private List<Verifier> parseVerifiers(ReadableArray verifiersArray) {
List<Verifier> verifiers = new ArrayList<>();
@@ -316,14 +344,7 @@ public class InjiOpenID4VPModule extends ReactContextBaseJavaModule {
} else if (MSO_MDOC.getValue().equals(formatStr)) {
return MSO_MDOC;
}
throw new UnsupportedOperationException("Credential format not supported: " + formatStr);
}
private List<String> extractStringListOrNull(ReadableMap readableMap, String key) {
return Optional.ofNullable(readableMap.getArray(key))
.map(this::convertReadableArrayToList)
.filter(list -> !list.isEmpty())
.orElse(null);
throw new UnsupportedOperationException("Credential format '" + formatStr + "' is not supported");
}
private List<String> convertReadableArrayToList(ReadableArray readableArray) {

View File

@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.google.gson.Gson
import io.mosip.vciclient.token.TokenResponse
import kotlinx.coroutines.CompletableDeferred
object VCIClientCallbackBridge {
@@ -11,6 +12,7 @@ object VCIClientCallbackBridge {
private var deferredAuthCode: CompletableDeferred<String>? = null
private var deferredTxCode: CompletableDeferred<String>? = null
private var deferredIssuerTrustResponse: CompletableDeferred<Boolean>? = null
private var deferredTokenResponse: CompletableDeferred<TokenResponse>? = null
fun createProofDeferred(): CompletableDeferred<String> {
deferredProof = CompletableDeferred()
@@ -22,72 +24,86 @@ object VCIClientCallbackBridge {
return deferredAuthCode!!
}
fun createTokenResponseDeferred(): CompletableDeferred<TokenResponse> {
deferredTokenResponse = CompletableDeferred()
return deferredTokenResponse!!
}
fun createTxCodeDeferred(): CompletableDeferred<String> {
deferredTxCode = CompletableDeferred()
return deferredTxCode!!
}
fun createIsuerTrustResponseDeferred(): CompletableDeferred<Boolean> {
fun createIssuerTrustResponseDeferred(): CompletableDeferred<Boolean> {
deferredIssuerTrustResponse = CompletableDeferred()
return deferredIssuerTrustResponse!!
}
fun emitRequestProof(
context: ReactApplicationContext,
accessToken: String,
cNonce: String?,
issuerMetadata: Map<String, *>? = null,
credentialConfigurationId: String? = null
context: ReactApplicationContext,
credentialIssuer: String,
cNonce: String?,
proofSigningAlgorithmsSupported: List<String>,
) {
val params =
Arguments.createMap().apply {
putString("accessToken", accessToken)
if (cNonce != null) putString("cNonce", cNonce)
if (issuerMetadata != null) {
val json = Gson().toJson(issuerMetadata)
putString("issuerMetadata", json)
}
if (credentialConfigurationId != null) {
putString("credentialConfigurationId", credentialConfigurationId)
}
}
Arguments.createMap().apply {
putString("credentialIssuer", credentialIssuer)
if (cNonce != null) putString("cNonce", cNonce)
val json = Gson().toJson(proofSigningAlgorithmsSupported)
putString("proofSigningAlgorithmsSupported", json)
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onRequestProof", params)
.emit("onRequestProof", params)
}
fun emitRequestAuthCode(context: ReactApplicationContext, authorizationEndpoint: String) {
val params =
Arguments.createMap().apply {
putString("authorizationEndpoint", authorizationEndpoint)
}
Arguments.createMap().apply {
putString("authorizationUrl", authorizationEndpoint)
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onRequestAuthCode", params)
.emit("onRequestAuthCode", params)
}
fun emitTokenRequest(
context: ReactApplicationContext,
payload: Map<String, Any?>
) {
val params =
Arguments.createMap().apply {
putString("tokenRequest", Gson().toJson(payload))
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onRequestTokenResponse", params)
}
fun emitRequestTxCode(
context: ReactApplicationContext,
inputMode: String?,
description: String?,
length: Int?
context: ReactApplicationContext,
inputMode: String?,
description: String?,
length: Int?
) {
val params =
Arguments.createMap().apply {
putString("inputMode", inputMode)
putString("description", description)
if( length != null)
Arguments.createMap().apply {
putString("inputMode", inputMode)
putString("description", description)
if (length != null)
putInt("length", length)
}
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onRequestTxCode", params)
.emit("onRequestTxCode", params)
}
fun emitRequestIssuerTrust(context: ReactApplicationContext, issuerMetadata: Map<String, *>) {
fun emitRequestIssuerTrust(context: ReactApplicationContext, credentialIssuer: String, issuerDisplay: List<Map<String, Any>>) {
val params =
Arguments.createMap().apply {
putString("issuerMetadata", Gson().toJson(issuerMetadata))
}
Arguments.createMap().apply {
//TODO: Convert Gson construction to singleton pattern
putString("credentialIssuer", credentialIssuer)
putString("issuerDisplay", Gson().toJson(issuerDisplay))
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onCheckIssuerTrust", params)
.emit("onCheckIssuerTrust", params)
}
@JvmStatic
@@ -95,16 +111,25 @@ object VCIClientCallbackBridge {
deferredProof?.complete(jwt)
deferredProof = null
}
@JvmStatic
fun completeAuthCode(code: String) {
deferredAuthCode?.complete(code)
deferredAuthCode = null
}
@JvmStatic
fun completeTxCode(code: String) {
deferredTxCode?.complete(code)
deferredTxCode = null
}
@JvmStatic
fun completeTokenResponse(tokenResponse: TokenResponse) {
deferredTokenResponse?.complete(tokenResponse)
deferredTokenResponse = null
}
@JvmStatic
fun completeIssuerTrustResponse(trusted: Boolean) {
deferredIssuerTrustResponse?.complete(trusted)
@@ -117,7 +142,12 @@ object VCIClientCallbackBridge {
suspend fun awaitAuthCode(): String {
return deferredAuthCode?.await()
?: throw IllegalStateException("No auth code callback was set")
?: throw IllegalStateException("No auth code callback was set")
}
suspend fun awaitTokenResponse(): TokenResponse {
return deferredTokenResponse?.await()
?: throw IllegalStateException("No TokenResponse callback was set")
}
suspend fun awaitTxCode(): String {
@@ -126,6 +156,6 @@ object VCIClientCallbackBridge {
suspend fun awaitIssuerTrustResponse(): Boolean {
return deferredIssuerTrustResponse?.await()
?: throw IllegalStateException("No issuer trust response callback was set")
?: throw IllegalStateException("No issuer trust response callback was set")
}
}

View File

@@ -9,22 +9,14 @@ import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.google.gson.Gson;
import java.util.Objects;
import io.mosip.residentapp.VCIClientCallbackBridge;
import io.mosip.residentapp.VCIClientBridge;
import java.util.Map;
import io.mosip.vciclient.VCIClient;
import io.mosip.vciclient.constants.CredentialFormat;
import io.mosip.vciclient.credentialOffer.CredentialOffer;
import io.mosip.vciclient.credentialOffer.CredentialOfferService;
import io.mosip.vciclient.credentialResponse.CredentialResponse;
import io.mosip.vciclient.proof.jwt.JWTProof;
import io.mosip.vciclient.proof.Proof;
import io.mosip.vciclient.issuerMetadata.IssuerMetadata;
import io.mosip.vciclient.clientMetadata.ClientMetadata;
import io.mosip.vciclient.authorizationCodeFlow.clientMetadata.ClientMetadata;
import io.mosip.vciclient.credential.response.CredentialResponse;
import io.mosip.vciclient.token.TokenResponse;
public class InjiVciClientModule extends ReactContextBaseJavaModule {
private VCIClient vciClient;
@@ -69,6 +61,29 @@ public class InjiVciClientModule extends ReactContextBaseJavaModule {
VCIClientCallbackBridge.completeIssuerTrustResponse(trusted);
}
@ReactMethod
public void sendTokenResponseFromJS(String tokenResponseJson) {
TokenResponse tokenResponse = new Gson().fromJson(tokenResponseJson, TokenResponse.class);
VCIClientCallbackBridge.completeTokenResponse(tokenResponse);
}
@ReactMethod
public void getIssuerMetadata(String credentialIssuer, Promise promise) {
new Thread(() -> {
try {
Map<String, Object> issuerMetadata = vciClient.getIssuerMetadata(credentialIssuer);
reactContext.runOnUiQueueThread(() -> {
String json = new Gson().toJson(issuerMetadata, Map.class);
promise.resolve(json);
});
} catch (Exception e) {
reactContext.runOnUiQueueThread(() -> {
promise.reject("GET_ISSUER_METADATA_FAILED", e.getMessage(), e);
});
}
}).start();
}
@ReactMethod
public void requestCredentialByOffer(String credentialOffer,String clientMetadataJson, Promise promise) {
new Thread(() -> {
@@ -88,15 +103,13 @@ public class InjiVciClientModule extends ReactContextBaseJavaModule {
}
@ReactMethod
public void requestCredentialFromTrustedIssuer(String resolvedIssuerMetaJson, String clientMetadataJson, Promise promise) {
public void requestCredentialFromTrustedIssuer(String credentialIssuer, String credentialConfigurationId, String clientMetadataJson, Promise promise) {
new Thread(() -> {
try {
IssuerMetadata issuerMetaData = new Gson().fromJson(
resolvedIssuerMetaJson, IssuerMetadata.class);
ClientMetadata clientMetadata= new Gson().fromJson(
clientMetadataJson, ClientMetadata.class);
CredentialResponse response = VCIClientBridge.requestCredentialFromTrustedIssuerSync(vciClient, issuerMetaData,clientMetadata);
CredentialResponse response = VCIClientBridge.requestCredentialFromTrustedIssuerSync(vciClient, credentialIssuer, credentialConfigurationId,clientMetadata);
reactContext.runOnUiQueueThread(() -> {
promise.resolve(response != null ? response.toJsonString() : null);

View File

@@ -2,10 +2,12 @@ package io.mosip.residentapp
import com.facebook.react.bridge.ReactApplicationContext
import io.mosip.vciclient.VCIClient
import io.mosip.vciclient.clientMetadata.ClientMetadata
import io.mosip.vciclient.credentialResponse.CredentialResponse
import io.mosip.vciclient.issuerMetadata.IssuerMetadata
import io.mosip.residentapp.VCIClientBridge
import io.mosip.vciclient.authorizationCodeFlow.clientMetadata.ClientMetadata
import io.mosip.vciclient.credential.response.CredentialResponse
import io.mosip.vciclient.token.TokenRequest
import io.mosip.vciclient.token.TokenResponse
import io.mosip.vciclient.constants.AuthorizeUserCallback
import io.mosip.vciclient.constants.ProofJwtCallback
import kotlinx.coroutines.runBlocking
object VCIClientBridge {
@@ -18,62 +20,94 @@ object VCIClientBridge {
client: VCIClient,
offer: String,
clientMetaData: ClientMetadata
): CredentialResponse? = runBlocking {
): CredentialResponse = runBlocking {
client.requestCredentialByCredentialOffer(
credentialOffer = offer,
clientMetadata = clientMetaData,
getTxCode = { inputMode, description, length ->
VCIClientCallbackBridge.createTxCodeDeferred()
VCIClientCallbackBridge.emitRequestTxCode(VCIClientBridge.reactContext,
inputMode,
description,
length
)
VCIClientCallbackBridge.awaitTxCode()
},
getProofJwt = { accessToken, cNonce, issuerMetadata, credentialConfigurationId ->
VCIClientCallbackBridge.createProofDeferred()
VCIClientCallbackBridge.emitRequestProof(
VCIClientBridge.reactContext,
accessToken,
cNonce,
issuerMetadata,
credentialConfigurationId
)
VCIClientCallbackBridge.awaitProof()
},
getAuthCode = { endpoint ->
VCIClientCallbackBridge.createAuthCodeDeferred()
VCIClientCallbackBridge.emitRequestAuthCode(VCIClientBridge.reactContext, endpoint)
VCIClientCallbackBridge.awaitAuthCode()
},
onCheckIssuerTrust = { issuerMetadata ->
VCIClientCallbackBridge.createIsuerTrustResponseDeferred()
VCIClientCallbackBridge.emitRequestIssuerTrust(reactContext, issuerMetadata)
VCIClientCallbackBridge.awaitIssuerTrustResponse()
}
getTxCode = getTxCodeCallback(),
authorizeUser = authorizeUserCallback(),
getTokenResponse = getTokenResponseCallback(),
getProofJwt = getProofJwtCallback(),
onCheckIssuerTrust = onCheckIssuerTrustCallback()
)
}
@JvmStatic
fun requestCredentialFromTrustedIssuerSync(
client: VCIClient,
resolvedIssuerMetaData: IssuerMetadata,
credentialIssuer: String,
credentialConfigurationId: String,
clientMetaData: ClientMetadata
): CredentialResponse? = runBlocking {
): CredentialResponse = runBlocking {
client.requestCredentialFromTrustedIssuer(
issuerMetadata = resolvedIssuerMetaData,
clientMetadata = clientMetaData,
getProofJwt = { accessToken, cNonce, _, _ ->
VCIClientCallbackBridge.createProofDeferred()
VCIClientCallbackBridge.emitRequestProof(reactContext, accessToken, cNonce)
VCIClientCallbackBridge.awaitProof()
},
getAuthCode = { authorizationEndpoint ->
VCIClientCallbackBridge.createAuthCodeDeferred()
VCIClientCallbackBridge.emitRequestAuthCode(reactContext, authorizationEndpoint)
VCIClientCallbackBridge.awaitAuthCode()
}
credentialIssuer,
credentialConfigurationId,
clientMetaData,
authorizeUser = authorizeUserCallback(),
getTokenResponse = getTokenResponseCallback(),
getProofJwt = getProofJwtCallback(),
)
}
private fun authorizeUserCallback(): AuthorizeUserCallback = { endpoint ->
VCIClientCallbackBridge.createAuthCodeDeferred()
VCIClientCallbackBridge.emitRequestAuthCode(reactContext, endpoint)
VCIClientCallbackBridge.awaitAuthCode()
}
private fun getProofJwtCallback(): ProofJwtCallback =
{
credentialIssuer: String,
cNonce: String?,
proofSigningAlgorithmsSupported: List<String> ->
VCIClientCallbackBridge.createProofDeferred()
VCIClientCallbackBridge.emitRequestProof(
reactContext,
credentialIssuer,
cNonce,
proofSigningAlgorithmsSupported
)
VCIClientCallbackBridge.awaitProof()
}
private fun getTokenResponseCallback(): suspend (tokenRequest: TokenRequest) -> TokenResponse =
{ tokenRequest ->
val payload: Map<String, Any?> =
mapOf(
"grantType" to tokenRequest.grantType.value,
"tokenEndpoint" to tokenRequest.tokenEndpoint,
"authCode" to tokenRequest.authCode,
"preAuthCode" to tokenRequest.preAuthCode,
"txCode" to tokenRequest.txCode,
"clientId" to tokenRequest.clientId,
"redirectUri" to tokenRequest.redirectUri,
"codeVerifier" to tokenRequest.codeVerifier
)
VCIClientCallbackBridge.createTokenResponseDeferred()
VCIClientCallbackBridge.emitTokenRequest(reactContext, payload)
VCIClientCallbackBridge.awaitTokenResponse()
}
private fun getTxCodeCallback(): suspend (String?, String?, Int?) -> String =
{ inputMode, description, length ->
VCIClientCallbackBridge.createTxCodeDeferred()
VCIClientCallbackBridge.emitRequestTxCode(
reactContext,
inputMode,
description,
length
)
VCIClientCallbackBridge.awaitTxCode()
}
private fun onCheckIssuerTrustCallback(): suspend (String, List<Map<String, Any>>) -> Boolean =
{ credentialIssuer, issuerDisplay ->
VCIClientCallbackBridge.createIssuerTrustResponseDeferred()
VCIClientCallbackBridge.emitRequestIssuerTrust(
reactContext,
credentialIssuer,
issuerDisplay
)
VCIClientCallbackBridge.awaitIssuerTrustResponse()
}
}

View File

@@ -19,11 +19,9 @@ import {VCProcessor} from '../common/VCProcessor';
export const VCCardView: React.FC<VCItemProps> = ({
vcMetadata,
margin,
selectable,
selected,
onPress,
onShow,
isDownloading,
isPinned,
flow,
@@ -58,7 +56,7 @@ export const VCCardView: React.FC<VCItemProps> = ({
setVc(processedData);
}
}
loadVc();
}, [isDownloading, controller.credential]);

View File

@@ -7,6 +7,7 @@ This document provides a comprehensive overview of the process for downloading a
- vc+sd-jwt/dc+sd-jwt credential format for SD-JWT. All non-normative examples are referred for `vc+sd-jwt` format.
- Cryptographic Key Binding - JWK is being used for cryptographic key binding in SD-JWT VC.
### Actors involved
1. Inji Wallet
2. Issuing authority
@@ -124,6 +125,7 @@ Note:
- getProofJwt is a callback function to create the proof JWT for the credential request.
````
##### 6. Authorize user for credential request and get authorization code and access token
_inji-vci-client_ uses `authorizeUser` callback function to authorize the user for the credential request.
This typically involves redirecting the user to an authorization server where they can log in and grant permission
@@ -169,8 +171,8 @@ Once the response is received in _inji-vci-client_, it is returned to the Wallet
"credentialConfigurationId": "SD_JWT_VC_example_in_OpenID4VCI"
}
````
##### 11. Perform vc verification
##### 11. Perform vc verification
After obtaining the credential from the issuing authority through the _inji-vci-client_ library, a verification process ensures that the issued Verifiable Credential (VC) remains unaltered through _vc-verifier_ library.
_vc-verifier_ will use `CredentialVerifierFactory` and create `SdJwtVerifiableCredential` to perform validation and verification of the credential.

View File

@@ -32,10 +32,10 @@
9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */; };
9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */; };
9C7CDF432C7CC13500243A9A /* RNSecureKeystoreModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */; };
9CAE74EE2E2E38F800C2532C /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 9CAE74ED2E2E38F800C2532C /* pixelpass */; };
9CCCA19E2CF87A8400D5A461 /* securekeystore in Frameworks */ = {isa = PBXBuildFile; productRef = 9CCCA19D2CF87A8400D5A461 /* securekeystore */; };
9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 9CD470CD2DFFF86600C207F9 /* OpenID4VP */; };
9CD470D12DFFF89C00C207F9 /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = 9CD470D02DFFF89C00C207F9 /* VCIClient */; };
9CFB37492DDDC9A000C199A8 /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 9CFB37482DDDC9A000C199A8 /* pixelpass */; };
9CDFD3102E28CA7B00505CEF /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = 9CDFD30F2E28CA7B00505CEF /* VCIClient */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
E86208152C0335C5007C3E24 /* RNVCIClientModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86208142C0335C5007C3E24 /* RNVCIClientModule.swift */; };
@@ -123,10 +123,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9CD470D12DFFF89C00C207F9 /* VCIClient in Frameworks */,
9CDFD3102E28CA7B00505CEF /* VCIClient in Frameworks */,
9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */,
9CAE74EE2E2E38F800C2532C /* pixelpass in Frameworks */,
9CCCA19E2CF87A8400D5A461 /* securekeystore in Frameworks */,
9CFB37492DDDC9A000C199A8 /* pixelpass in Frameworks */,
9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */,
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */,
);
@@ -305,6 +305,7 @@
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
D11A8C363B4A5B625DB10379 /* [CP] Embed Pods Frameworks */,
9CDFD3112E2A2F7900505CEF /* ShellScript */,
);
buildRules = (
);
@@ -314,9 +315,9 @@
packageProductDependencies = (
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */,
9CCCA19D2CF87A8400D5A461 /* securekeystore */,
9CFB37482DDDC9A000C199A8 /* pixelpass */,
9CD470CD2DFFF86600C207F9 /* OpenID4VP */,
9CD470D02DFFF89C00C207F9 /* VCIClient */,
9CDFD30F2E28CA7B00505CEF /* VCIClient */,
9CAE74ED2E2E38F800C2532C /* pixelpass */,
);
productName = Inji;
productReference = 13B07F961A680F5B00A75B9A /* Inji.app */;
@@ -347,9 +348,9 @@
packageReferences = (
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */,
9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */,
9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
9CD470CC2DFFF86600C207F9 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */,
9CD470CF2DFFF89C00C207F9 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
9CDFD30E2E28CA7B00505CEF /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
9CAE74EC2E2E38F800C2532C /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
);
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
@@ -641,11 +642,11 @@
CODE_SIGN_ENTITLEMENTS = Inji/InjiRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = V2ABX7953Z;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = V2ABX7953Z;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = Inji/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -663,7 +664,7 @@
PRODUCT_BUNDLE_IDENTIFIER = io.mosip.inji.wallet.mobileid;
PRODUCT_NAME = Inji;
PROVISIONING_PROFILE_SPECIFIER = "match AppStore io.mosip.inji.wallet.mobileid";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore io.mosip.inji.wallet.mobileid";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore io.mosip.inji.wallet.mobileid";
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -701,7 +702,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CXX = "";
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -833,6 +834,14 @@
version = 0.5.0;
};
};
9CAE74EC2E2E38F800C2532C /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift/";
requirement = {
branch = "release-0.6.x";
kind = branch;
};
};
9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/secure-keystore-ios-swift";
@@ -845,23 +854,15 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/inji-openid4vp-ios-swift";
requirement = {
branch = "release-0.3.x";
branch = develop;
kind = branch;
};
};
9CD470CF2DFFF89C00C207F9 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = {
9CDFD30E2E28CA7B00505CEF /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/inji-vci-client-ios-swift/";
requirement = {
branch = develop;
kind = branch;
};
};
9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift/";
requirement = {
branch = develop;
branch = "release-0.4.x";
kind = branch;
};
};
@@ -873,6 +874,11 @@
package = 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */;
productName = "ios-tuvali-library";
};
9CAE74ED2E2E38F800C2532C /* pixelpass */ = {
isa = XCSwiftPackageProductDependency;
package = 9CAE74EC2E2E38F800C2532C /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
productName = pixelpass;
};
9CCCA19D2CF87A8400D5A461 /* securekeystore */ = {
isa = XCSwiftPackageProductDependency;
package = 9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */;
@@ -883,16 +889,11 @@
package = 9CD470CC2DFFF86600C207F9 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */;
productName = OpenID4VP;
};
9CD470D02DFFF89C00C207F9 /* VCIClient */ = {
9CDFD30F2E28CA7B00505CEF /* VCIClient */ = {
isa = XCSwiftPackageProductDependency;
package = 9CD470CF2DFFF89C00C207F9 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */;
package = 9CDFD30E2E28CA7B00505CEF /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */;
productName = VCIClient;
};
9CFB37482DDDC9A000C199A8 /* pixelpass */ = {
isa = XCSwiftPackageProductDependency;
package = 9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
productName = pixelpass;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;

View File

@@ -19,6 +19,24 @@
"version" : "1.1.0"
}
},
{
"identity" : "base58swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/keefertaylor/Base58Swift.git",
"state" : {
"revision" : "ce8ba8aa893f7e209e9c99c35ee61d6f1235881d",
"version" : "2.1.7"
}
},
{
"identity" : "bigint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/attaswift/BigInt.git",
"state" : {
"revision" : "018a5925f60f9e0523edd261de394a0898fe95b7",
"version" : "3.1.0"
}
},
{
"identity" : "crcswift",
"kind" : "remoteSourceControl",
@@ -51,8 +69,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/inji-openid4vp-ios-swift",
"state" : {
"branch" : "release-0.3.x",
"revision" : "3e963ec60e4beb77f020eb1230df019c45c74f56"
"branch" : "develop",
"revision" : "b93f78c94cd7632f2a80e869520788abd1378621"
}
},
{
@@ -60,8 +78,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/inji-vci-client-ios-swift/",
"state" : {
"branch" : "develop",
"revision" : "8a39bbf7805af4c615904090a027fa472e5f4534"
"branch" : "release-0.4.x",
"revision" : "56359149fbfa61e4fd334df21bf2116e05b4d02d"
}
},
{
@@ -78,8 +96,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/pixelpass-ios-swift/",
"state" : {
"branch" : "develop",
"revision" : "78a8b507f5bd0046e273e3ba962696b44aea0e33"
"branch" : "release-0.6.x",
"revision" : "78faea2ff48626ef9a6d198123bcf7b299acefd1"
}
},
{
@@ -100,12 +118,21 @@
"version" : "0.3.0"
}
},
{
"identity" : "siphash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/attaswift/SipHash",
"state" : {
"revision" : "e325083424688055363bbfcb7f1a440d7d7a1bae",
"version" : "1.2.2"
}
},
{
"identity" : "swiftcbor",
"kind" : "remoteSourceControl",
"location" : "https://github.com/valpackett/SwiftCBOR",
"location" : "https://github.com/abhip2565/SwiftCBOR",
"state" : {
"revision" : "04ccff117f6549121d5721ec84fdf0162122b90e",
"revision" : "cc4e195a0ea2dce88f2fd6c6d73b2504e3c884fd",
"version" : "0.5.0"
}
},
@@ -128,5 +155,5 @@
}
}
],
"version" : 2
"version" : 3
}

View File

@@ -3,11 +3,13 @@
@interface RCT_EXTERN_MODULE(InjiOpenID4VP, NSObject)
RCT_EXTERN_METHOD(init:(NSString *)appId)
RCT_EXTERN_METHOD(initSdk:(NSString *)appId
walletMetadata:(id)walletMetadata
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(authenticateVerifier:(NSString *)urlEncodedAuthorizationRequest
trustedVerifierJSON:(id)trustedVerifierJSON
walletMetadata:(id)walletMetadata
shouldValidateClient:(BOOL)shouldValidateClient
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
@@ -23,6 +25,7 @@ RCT_EXTERN_METHOD(shareVerifiablePresentation:(id)vpResponseMetadata
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(sendErrorToVerifier:(NSString *)error
:(NSString *)errorCode
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)

View File

@@ -7,19 +7,25 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
private var openID4VP: OpenID4VP?
static func moduleName() -> String {
return "InjiOpenID4VP"
}
@objc
func `init`(_ appId: String) {
openID4VP = OpenID4VP(traceabilityId: appId)
func `initSdk`(_ appId: String, walletMetadata: AnyObject?,resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
do {
let walletMetadataObject = try getWalletMetadataFromDict(walletMetadata, reject: reject)
openID4VP = OpenID4VP(traceabilityId: appId, walletMetadata: walletMetadataObject)
resolve(true)
} catch {
reject("OPENID4VP", error.localizedDescription, error)
}
}
@objc
func authenticateVerifier(_ urlEncodedAuthorizationRequest: String,
trustedVerifierJSON: AnyObject,
walletMetadata: AnyObject?,
shouldValidateClient: Bool,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
@@ -38,18 +44,17 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
return Verifier(clientId: clientId, responseUris: responseUris)
}
let walletMetadataObject = try getWalletMetadataFromDict(walletMetadata as Any, reject: reject)
let authenticationResponse: AuthorizationRequest = try await openID4VP!.authenticateVerifier(
urlEncodedAuthorizationRequest: urlEncodedAuthorizationRequest,
trustedVerifierJSON: trustedVerifiersList,
shouldValidateClient: shouldValidateClient, walletMetadata: walletMetadataObject
shouldValidateClient: shouldValidateClient
)
let response = try toJsonString(jsonObject: authenticationResponse)
resolve(response)
} catch {
reject("OPENID4VP", error.localizedDescription, error)
rejectWithOpenID4VPError(error, reject: reject)
}
}
}
@@ -97,7 +102,7 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
reject("ERROR", "Failed to serialize JSON", nil)
}
} catch {
reject("OPENID4VP", error.localizedDescription, error)
rejectWithOpenID4VPError(error, reject: reject)
}
}
}
@@ -140,7 +145,8 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
formattedVPTokenSigningResults[.mso_mdoc] = MdocVPTokenSigningResult(docTypeToDeviceAuthentication: docTypeToDeviceAuthentication)
default:
reject("OPENID4VP", "Credential format not supported", nil)
let error = NSError(domain: "Credential format '\(credentialFormat)' is not supported", code: 0)
rejectWithOpenID4VPError(error, reject: reject)
return
}
}
@@ -148,23 +154,32 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
let response = try await openID4VP?.shareVerifiablePresentation(vpTokenSigningResults: formattedVPTokenSigningResults)
resolve(response)
} catch {
reject("OPENID4VP", error.localizedDescription, error)
rejectWithOpenID4VPError(error, reject: reject)
}
}
}
@objc
func sendErrorToVerifier(_ error: String, _ errorCode: String,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
@objc
func sendErrorToVerifier(_ error: String, _ errorCode: String,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
Task {
enum VerifierError: Error {
case customError(String)
}
await openID4VP?.sendErrorToVerifier(error: VerifierError.customError(error))
resolve(true)
let exception: OpenID4VPException = {
switch errorCode {
case OpenID4VPErrorCodes.accessDenied:
return AccessDenied(message: error, className: Self.moduleName())
case OpenID4VPErrorCodes.invalidTransactionData:
return InvalidTransactionData(message: error, className: Self.moduleName())
default:
return GenericFailure(message: error, className: Self.moduleName())
}
}()
await openID4VP?.sendErrorToVerifier(error: exception)
resolve(true)
}
}
}
func toJsonString(jsonObject: AuthorizationRequest) throws -> String {
let encoder = JSONEncoder()
@@ -180,6 +195,17 @@ class RNOpenId4VpModule: NSObject, RCTBridgeModule {
static func requiresMainQueueSetup() -> Bool {
return true
}
func rejectWithOpenID4VPError(_ error: Error, reject: RCTPromiseRejectBlock) {
if let openidError = error as? OpenID4VPException {
reject(openidError.errorCode, openidError.message, openidError)
} else {
let nsError = NSError(domain: error.localizedDescription, code: 0)
reject("ERR_UNKNOWN", nsError.localizedDescription, nsError)
}
}
}
struct EncodableWrapper: Encodable {
@@ -205,22 +231,42 @@ func getWalletMetadataFromDict(_ walletMetadata: Any,
throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid Wallet Metadata"])
}
var vpFormatsSupported: [String: VPFormatSupported] = [:]
var vpFormatsSupported: [FormatType: VPFormatSupported] = [:]
if let vpFormatsSupportedDict = metadata["vp_formats_supported"] as? [String: Any],
let ldpVcDict = vpFormatsSupportedDict["ldp_vc"] as? [String: Any] {
let algValuesSupported = ldpVcDict["alg_values_supported"] as? [String]
vpFormatsSupported["ldp_vc"] = VPFormatSupported(algValuesSupported: algValuesSupported)
vpFormatsSupported[.ldp_vc] = VPFormatSupported(algValuesSupported: algValuesSupported)
if let mdocDict = vpFormatsSupportedDict["mso_mdoc"] as? [String: Any] {
let mdocAlgValuesSupported = mdocDict["alg_values_supported"] as? [String]
vpFormatsSupported[.mso_mdoc] = VPFormatSupported(algValuesSupported: mdocAlgValuesSupported)
}
} else {
vpFormatsSupported["ldp_vc"] = VPFormatSupported(algValuesSupported: nil)
vpFormatsSupported[.ldp_vc] = VPFormatSupported(algValuesSupported: nil)
}
let walletMetadataObject = try WalletMetadata(
presentationDefinitionURISupported: metadata["presentation_definition_uri_supported"] as? Bool,
vpFormatsSupported: vpFormatsSupported,
clientIdSchemesSupported: metadata["client_id_schemes_supported"] as? [String],
requestObjectSigningAlgValuesSupported: metadata["request_object_signing_alg_values_supported"] as? [String],
authorizationEncryptionAlgValuesSupported: metadata["authorization_encryption_alg_values_supported"] as? [String],
authorizationEncryptionEncValuesSupported: metadata["authorization_encryption_enc_values_supported"] as? [String]
clientIdSchemesSupported: mapStringsToEnum(metadata["client_id_schemes_supported"] as? [String] ?? [], using: ClientIdScheme.fromValue),
requestObjectSigningAlgValuesSupported: mapStringsToEnum(metadata["request_object_signing_alg_values_supported"] as? [String] ?? [], using: RequestSigningAlgorithm.fromValue),
authorizationEncryptionAlgValuesSupported: mapStringsToEnum(metadata["authorization_encryption_alg_values_supported"] as? [String] ?? [], using: KeyManagementAlgorithm.fromValue),
authorizationEncryptionEncValuesSupported: mapStringsToEnum(metadata["authorization_encryption_enc_values_supported"] as? [String] ?? [], using: ContentEncryptionAlgorithm.fromValue)
)
return walletMetadataObject
}
func mapStringsToEnum<T: RawRepresentable>(
_ input: [String],
using fromValue: (String) -> T?
) throws -> [T] where T.RawValue == String {
return try input.map { str in
guard let match = fromValue(str) else {
throw NSError(
domain: "EnumMappingError",
code: 1001,
userInfo: [NSLocalizedDescriptionKey: "Invalid value '\(str)' for enum \(T.self)"]
)
}
return match
}
}

View File

@@ -12,12 +12,18 @@ RCT_EXTERN_METHOD(requestCredentialByOffer:(NSString *)credentialOffer
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Requests a credential from a trusted issuer using issuer metadata and client metadata (both as JSON strings)
RCT_EXTERN_METHOD(requestCredentialFromTrustedIssuer:(NSString *)issuerMetadata
// Requests a credential from a trusted issuer using issuer URI, configuration ID, and client metadata (all as strings)
RCT_EXTERN_METHOD(requestCredentialFromTrustedIssuer:(NSString *)credentialIssuer
credentialConfigurationId:(NSString *)credentialConfigurationId
clientMetadata:(NSString *)clientMetadata
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Gets issuer metadata (discovery)
RCT_EXTERN_METHOD(getIssuerMetadata:(NSString *)credentialIssuer
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Sends proof JWT back to native side (in response to onRequestProof)
RCT_EXTERN_METHOD(sendProofFromJS:(NSString *)jwtProof)
@@ -30,6 +36,9 @@ RCT_EXTERN_METHOD(sendTxCodeFromJS:(NSString *)txCode)
// Sends issuer trust decision (true/false) back to native side (in response to onCheckIssuerTrust)
RCT_EXTERN_METHOD(sendIssuerTrustResponseFromJS:(BOOL)isTrusted)
// Sends token response JSON back to native side (in response to onRequestTokenResponse)
RCT_EXTERN_METHOD(sendTokenResponseFromJS:(NSString *)tokenResponseJson)
// Required by React Native
RCT_EXTERN_METHOD(requiresMainQueueSetup:(BOOL)isRequired)

View File

@@ -4,10 +4,13 @@ import VCIClient
@objc(InjiVciClient)
class RNVCIClientModule: NSObject, RCTBridgeModule {
private var vciClient: VCIClient?
private var pendingProofContinuation: ((String) -> Void)?
private var pendingAuthCodeContinuation: ((String) -> Void)?
private var pendingTxCodeContinuation: ((String) -> Void)?
private var pendingTokenResponseContinuation: ((String) -> Void)?
private var pendingIssuerTrustDecision: ((Bool) -> Void)?
static func moduleName() -> String {
@@ -19,6 +22,8 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
vciClient = VCIClient(traceabilityId: traceabilityId)
}
// MARK: - Public API
@objc
func requestCredentialByOffer(
_ credentialOffer: String,
@@ -45,19 +50,24 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
length: length
)
},
getProofJwt: { accessToken, cNonce, issuerMetadata, credentialConfigId in
try await self.getProofContinuationHook(
accessToken: accessToken,
cNonce: cNonce,
issuerMetadata: issuerMetadata,
credentialConfigId: credentialConfigId
)
},
getAuthCode: { authUrl in
authorizeUser: { authUrl in
try await self.getAuthCodeContinuationHook(authUrl: authUrl)
},
onCheckIssuerTrust: { issuerMetadata in
try await self.getIssuerTrustDecisionHook(issuerMetadata: issuerMetadata)
getTokenResponse: { tokenRequest in
try await self.getTokenResponseHook(tokenRequest: tokenRequest)
},
getProofJwt: { credentialIssuer, cNonce, algos in
try await self.getProofContinuationHook(
credentialIssuer: credentialIssuer,
cNonce: cNonce,
proofSigningAlgorithmsSupported: algos
)
},
onCheckIssuerTrust: { credentialIssuer, issuerDisplay in
try await self.getIssuerTrustDecisionHook(
credentialIssuer: credentialIssuer,
issuerDisplay: issuerDisplay
)
}
)
@@ -70,7 +80,8 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
@objc
func requestCredentialFromTrustedIssuer(
_ issuerMetadata: String,
_ credentialIssuer: String,
credentialConfigurationId: String,
clientMetadata: String,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock
@@ -82,22 +93,24 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
return
}
let issuer = try parseIssuerMeta(from: issuerMetadata)
let clientMeta = try parseClientMetadata(from: clientMetadata)
let response = try await vciClient.requestCredentialFromTrustedIssuer(
issuerMetadata: issuer,
credentialIssuer: credentialIssuer,
credentialConfigurationId: credentialConfigurationId,
clientMetadata: clientMeta,
getProofJwt: { accessToken, cNonce, issuerMetadata, credentialConfigId in
try await self.getProofContinuationHook(
accessToken: accessToken,
cNonce: cNonce,
issuerMetadata: issuerMetadata,
credentialConfigId: credentialConfigId
)
},
getAuthCode: { authUrl in
authorizeUser: { authUrl in
try await self.getAuthCodeContinuationHook(authUrl: authUrl)
},
getTokenResponse: { tokenRequest in
try await self.getTokenResponseHook(tokenRequest: tokenRequest)
},
getProofJwt: { credentialIssuer, cNonce, algos in
try await self.getProofContinuationHook(
credentialIssuer: credentialIssuer,
cNonce: cNonce,
proofSigningAlgorithmsSupported: algos
)
}
)
@@ -107,54 +120,51 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
}
}
}
@objc
func getIssuerMetadata(
_ credentialIssuer: String,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock
) {
Task {
do {
guard let vciClient = vciClient else {
reject(nil, "VCIClient not initialized", nil)
return
}
private func getProofContinuationHook(
accessToken: String,
cNonce: String?,
issuerMetadata: [String: Any]?,
credentialConfigId: String?
) async throws -> String {
var issuerMetadataJson: String = ""
let metadata = try await vciClient.getIssuerMetadata(credentialIssuer: credentialIssuer)
if let issuerMetadata = issuerMetadata {
if let data = try? JSONSerialization.data(withJSONObject: issuerMetadata, options: []),
let jsonString = String(data: data, encoding: .utf8) {
issuerMetadataJson = jsonString
let data = try JSONSerialization.data(withJSONObject: metadata, options: [])
guard let jsonString = String(data: data, encoding: .utf8) else {
throw NSError(domain: "JSONEncodingError", code: 0)
}
resolve(jsonString)
} catch {
reject(nil, error.localizedDescription, nil)
}
}
if let bridge = RCTBridge.current() {
let payload: [String: Any] = [
"accessToken": accessToken,
"cNonce": cNonce ?? NSNull(),
"issuerMetadata": issuerMetadataJson,
"credentialConfigurationId": credentialConfigId ?? NSNull(),
]
bridge.eventDispatcher().sendAppEvent(withName: "onRequestProof", body: payload)
}
return try await withCheckedThrowingContinuation { continuation in
self.pendingProofContinuation = { jwt in continuation.resume(returning: jwt) }
}
}
private func getAuthCodeContinuationHook(authUrl: String) async throws -> String {
if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(withName: "onRequestAuthCode", body: ["authorizationEndpoint": authUrl])
}
return try await withCheckedThrowingContinuation { continuation in
self.pendingAuthCodeContinuation = { code in continuation.resume(returning: code) }
}
}
// MARK: - Callbacks to JS
private func getTxCodeHook(inputMode: String?, description: String?, length: Int? ) async throws -> String {
private func getTxCodeHook(
inputMode: String?,
description: String?,
length: Int?
) async throws -> String {
if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(withName: "onRequestTxCode", body: [
"inputMode": inputMode,
"description": description,
"length": length
])
bridge.eventDispatcher().sendAppEvent(
withName: "onRequestTxCode",
body: [
"inputMode": inputMode,
"description": description,
"length": length
]
)
}
return try await withCheckedThrowingContinuation { continuation in
@@ -162,15 +172,87 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
}
}
private func getIssuerTrustDecisionHook(issuerMetadata: [String: Any]) async throws -> Bool {
var metadataJson = ""
if let data = try? JSONSerialization.data(withJSONObject: issuerMetadata, options: []),
let string = String(data: data, encoding: .utf8) {
metadataJson = string
private func getAuthCodeContinuationHook(authUrl: String) async throws -> String {
if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(
withName: "onRequestAuthCode",
body: ["authorizationUrl": authUrl]
)
}
return try await withCheckedThrowingContinuation { continuation in
self.pendingAuthCodeContinuation = { code in continuation.resume(returning: code) }
}
}
private func getProofContinuationHook(
credentialIssuer: String,
cNonce: String?,
proofSigningAlgorithmsSupported: [String]
) async throws -> String {
let jsonData = try JSONSerialization.data(withJSONObject: proofSigningAlgorithmsSupported, options: [])
let jsonString = String(data: jsonData, encoding: .utf8) ?? "{}"
if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(withName: "onCheckIssuerTrust", body: ["issuerMetadata": metadataJson])
bridge.eventDispatcher().sendAppEvent(
withName: "onRequestProof",
body: [
"credentialIssuer": credentialIssuer,
"cNonce": cNonce,
"proofSigningAlgorithmsSupported": jsonString
]
)
}
return try await withCheckedThrowingContinuation { continuation in
self.pendingProofContinuation = { jwt in continuation.resume(returning: jwt) }
}
}
private func getTokenResponseHook(tokenRequest: TokenRequest) async throws -> TokenResponse {
if let bridge = RCTBridge.current() {
let tokenRequest: [String: Any] = [
"grantType": tokenRequest.grantType.rawValue,
"tokenEndpoint": tokenRequest.tokenEndpoint,
"authCode": tokenRequest.authCode ?? NSNull(),
"preAuthCode": tokenRequest.preAuthCode ?? NSNull(),
"txCode": tokenRequest.txCode ?? NSNull(),
"clientId": tokenRequest.clientId ?? NSNull(),
"redirectUri": tokenRequest.redirectUri ?? NSNull(),
"codeVerifier": tokenRequest.codeVerifier ?? NSNull()
]
bridge.eventDispatcher().sendAppEvent(
withName: "onRequestTokenResponse",
body: ["tokenRequest":tokenRequest]
)
}
let json = try await withCheckedThrowingContinuation { continuation in
self.pendingTokenResponseContinuation = { json in continuation.resume(returning: json) }
}
guard let data = json.data(using: .utf8) else {
throw NSError(domain: "Invalid JSON", code: 0)
}
return try JSONDecoder().decode(TokenResponse.self, from: data)
}
private func getIssuerTrustDecisionHook(
credentialIssuer: String,
issuerDisplay: [[String: Any]]
) async throws -> Bool {
// Convert issuerDisplay to JSON string
let jsonData = try JSONSerialization.data(withJSONObject: issuerDisplay, options: [])
let jsonString = String(data: jsonData, encoding: .utf8) ?? "[]"
if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(
withName: "onCheckIssuerTrust",
body: [
"credentialIssuer": credentialIssuer,
"issuerDisplay": jsonString
]
)
}
return try await withCheckedThrowingContinuation { continuation in
@@ -178,6 +260,8 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
}
}
// MARK: - Receivers from JS
@objc(sendProofFromJS:)
func sendProofFromJS(_ jwt: String) {
pendingProofContinuation?(jwt)
@@ -196,24 +280,25 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
pendingTxCodeContinuation = nil
}
@objc(sendTokenResponseFromJS:)
func sendTokenResponseFromJS(_ json: String) {
pendingTokenResponseContinuation?(json)
pendingTokenResponseContinuation = nil
}
@objc(sendIssuerTrustResponseFromJS:)
func sendIssuerTrustResponseFromJS(_ trusted: Bool) {
pendingIssuerTrustDecision?(trusted)
pendingIssuerTrustDecision = nil
}
private func parseClientMetadata(from jsonString: String) throws -> ClientMetaData {
// MARK: - JSON Parsing
private func parseClientMetadata(from jsonString: String) throws -> ClientMetadata {
guard let data = jsonString.data(using: .utf8) else {
throw NSError(domain: "Invalid JSON string for clientMetadata", code: 0)
}
return try JSONDecoder().decode(ClientMetaData.self, from: data)
}
private func parseIssuerMeta(from jsonString: String) throws -> IssuerMetadata {
guard let data = jsonString.data(using: .utf8) else {
throw NSError(domain: "Invalid JSON string for issuerMetadata", code: 0)
}
return try JSONDecoder().decode(IssuerMetadata.self, from: data)
return try JSONDecoder().decode(ClientMetadata.self, from: data)
}
@objc static func requiresMainQueueSetup() -> Bool {

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "تمت مشاركة العرض التقديمي لبيانات الاعتماد بنجاح.",
"SHARED_WITH_FACE_VERIFIACTION": "تم التحقق من الوجه بنجاح، وتمت مشاركة العرض التقديمي لبيانات الاعتماد بنجاح.",
"VERIFIER_AUTHENTICATION_FAILED": "فشلت مصادقة خدمة التحقق أثناء مشاركة VP.",
"INVALID_AUTH_REQUEST": "يحتوي رمز الاستجابة السريعة على بيانات غير صالحة أو مفقودة. تحقق مع خدمة التحقق.",
"USER_DECLINED_CONSENT": "رفض المستخدم الموافقة على مشاركة العرض التقديمي لبيانات الاعتماد.",
"USER_DECLINED_CONSENT": "رفض المستخدم منح الموافقة لمشاركة عرض بيانات الاعتماد.",
"SHARED_AFTER_RETRY": "تمت مشاركة العرض التقديمي لبيانات الاعتماد بنجاح بعد إعادة المحاولة.",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "تم التحقق من الوجه بنجاح، وتمت مشاركة العرض التقديمي لبيانات الاعتماد بنجاح بعد إعادة المحاولة.",
"SHARING_FAILED": "فشلت مشاركة العرض التقديمي لبيانات الاعتماد بسبب خطأ فني.",
@@ -31,7 +30,13 @@
"NO_SELECTED_VC_HAS_IMAGE": "فشلت مشاركة العرض التقديمي لبيانات الاعتماد لأن بيانات الاعتماد المحددة التي يمكن التحقق منها تفتقر إلى صورة الوجه.",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "حدث عدم تطابق في العرض التقديمي لبيانات الاعتماد أثناء المشاركة من القائمة، بناءً على المطالبات المطلوبة: {{info}}.",
"NO_CREDENTIAL_MATCHING_REQUEST": "لم يتم العثور على بيانات اعتماد تطابق طلب خدمة التحقق بناءً على المطالبات المقدمة: {{info}}.",
"TECHNICAL_ERROR": "تعذر متابعة مشاركة العرض التقديمي لبيانات الاعتماد بسبب خطأ فني."
"TECHNICAL_ERROR": "حدث خطأ أثناء معالجة طلبك.",
"INVALID_REQUEST_URI_METHOD": "لم تتم مشاركة الاعتماد — استخدم المُحقق طريقة غير مدعومة.",
"INVALID_AUTH_REQUEST": "فشل مشاركة الاعتماد — الطلب يحتوي على معلومات مفقودة أو غير صحيحة.",
"REQUEST_COULD_NOT_BE_PROCESSED": "لم تتم مشاركة الاعتماد — تعذر معالجة الطلب بسبب مشكلة فنية.",
"INVALID_PRESENTATION_DEFINITION_URI": "لم تتم مشاركة الاعتماد — تحتوي طلب المحقق على تنسيق بيانات غير صحيح.",
"SEND_VP_ERROR": "فشلت مشاركة الاعتماد بسبب مشكلة فنية."
}
},
"DeviceInfoList": {
@@ -216,6 +221,10 @@
"title": "لا يوجد اتصال بالإنترنت",
"message": "الرجاء التحقق من اتصالك وإعادة المحاولة"
},
"networkRequestFailed": {
"title": "فشل طلب الشبكة",
"message": "لا يمكننا معالجة طلبك في الوقت الحالي."
},
"biometricsCancelled": {
"title": "هل تريد إلغاء التنزيل؟",
"message": "مطلوب تأكيد البيومترية لمواصلة تنزيل البطاقة."
@@ -901,7 +910,7 @@
},
"sendVPError": {
"title": "حدث خطأ ما",
"message": "الرسالة: لم نتمكن من مشاركة بطاقتك بسبب مشكلة فنية. انقر فوق إعادة المحاولة للمحاولة مرة أخرى، أو انتقل إلى الصفحة الرئيسية للخروج."
"message": "حدثت مشكلة فنية أثناء مشاركة بطاقتك. انقر على \"إعادة المحاولة\" للمحاولة مرة أخرى أو ارجع إلى \"الرئيسية\"."
},
"noImage": {
"title": "حدث خطأ!",
@@ -1098,4 +1107,4 @@
"confirm": "نعم، أثق بهذا",
"cancel": "لا، أعدني"
}
}
}

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "Credential Presentation is shared successfully.",
"SHARED_WITH_FACE_VERIFIACTION": "Face verification is successful, and the Credential Presentation is shared successfully.",
"VERIFIER_AUTHENTICATION_FAILED": "Failed to authenticate the Verification Service during VP sharing.",
"INVALID_AUTH_REQUEST": "QR code has invalid or missing data. Check with the verification service.",
"USER_DECLINED_CONSENT": "User declined consent for Credential Presentation sharing.",
"USER_DECLINED_CONSENT": "User declined consent for credential presentation sharing.",
"SHARED_AFTER_RETRY": "Credential Presentation is shared successfully after a retry.",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "Face verification is successful, and the Credential Presentation is shared successfully after a retry.",
"SHARING_FAILED": "Credential Presentation sharing failed due to technical error.",
@@ -31,7 +30,12 @@
"NO_SELECTED_VC_HAS_IMAGE": "Credential Presentation sharing failed as the selected verifiable credentials lack a face image.",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "Credentials Presentation mismatch occurred during sharing from the menu, based on requested claims: {{info}}.",
"NO_CREDENTIAL_MATCHING_REQUEST": "No credentials were found that match the verification service's request based on the provided claims: {{info}}.",
"TECHNICAL_ERROR": "Credential Presentation sharing could not proceed due to technical error."
"TECHNICAL_ERROR": "Something went wrong while processing your request.",
"INVALID_REQUEST_URI_METHOD": "Credential not shared— The verifier used a method that isnt supported.",
"INVALID_AUTH_REQUEST" : "Credential sharing failed — The request had missing or incorrect information.",
"REQUEST_COULD_NOT_BE_PROCESSED" : "Credential not shared — Request couldnt be processed due to a technical issue.",
"INVALID_PRESENTATION_DEFINITION_URI": "Credential not shared — The verifiers request had incorrect data format.",
"SEND_VP_ERROR": "Credential sharing failed due to a technical issue."
}
},
"DeviceInfoList": {
@@ -217,6 +221,10 @@
"title": "No internet connection",
"message": "Please check your connection and retry"
},
"networkRequestFailed":{
"title": "Network request failed",
"message": "We are unable to precess your request at the moment."
},
"biometricsCancelled": {
"title": "Do you want to cancel download?",
"message": "Biometric confirmation is required to continue downloading the card."
@@ -914,7 +922,7 @@
},
"sendVPError": {
"title": "Something Went Wrong",
"message": "Message: We couldn't share your card due to a technical issue. Click Retry to try again, or go to Home to exit."
"message": "There was a technical issue while sharing your card. Tap \"Retry\" to try again or go back to \"Home\"."
},
"noImage": {
"title": "An Error Occured!",

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "Matagumpay na naibahagi ang Pagtatanghal ng Kredensyal.",
"SHARED_WITH_FACE_VERIFIACTION": "Matagumpay ang pag-verify sa mukha, at matagumpay na naibahagi ang Credential Presentation.",
"VERIFIER_AUTHENTICATION_FAILED": "Nabigong i-authenticate ang Serbisyo sa Pag-verify sa panahon ng pagbabahagi ng VP.",
"INVALID_AUTH_REQUEST": "Ang QR code ay may di-wasto o nawawalang data. Tingnan sa serbisyo ng pag-verify.",
"USER_DECLINED_CONSENT": "Tinanggihan ng user ang pahintulot para sa pagbabahagi ng Credential Presentation.",
"USER_DECLINED_CONSENT": "பயனர் சான்றுகள் பகிர்வுக்கு ஒப்புதலை நிராகரித்தார்.",
"SHARED_AFTER_RETRY": "Ang Pagtatanghal ng Kredensyal ay matagumpay na naibahagi pagkatapos ng muling pagsubok.",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "Matagumpay ang pag-verify sa mukha, at matagumpay na naibahagi ang Credential Presentation pagkatapos ng muling pagsubok.",
"SHARING_FAILED": "Nabigo ang pagbabahagi ng Credential Presentation dahil sa teknikal na error.",
@@ -31,7 +30,13 @@
"NO_SELECTED_VC_HAS_IMAGE": "Nabigo ang pagbabahagi ng Pagtatanghal ng Kredensyal dahil walang larawan ng mukha ang mga napiling nabe-verify na kredensyal.",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "Ang hindi pagkakatugma ng Pagtatanghal ng Mga Kredensyal ay naganap sa panahon ng pagbabahagi mula sa menu, batay sa mga hiniling na claim: {{info}}.",
"NO_CREDENTIAL_MATCHING_REQUEST": "Walang nakitang mga kredensyal na tumutugma sa kahilingan ng serbisyo sa pag-verify batay sa mga ibinigay na claim: {{info}}.",
"TECHNICAL_ERROR": "Hindi matuloy ang pagbabahagi ng Kredensyal na Presentasyon dahil sa teknikal na error."
"TECHNICAL_ERROR": "May nangyaring mali habang pinoproseso ang iyong kahilingan.",
"INVALID_REQUEST_URI_METHOD": "Hindi naibahagi ang kredensyal — Gumamit ang verifier ng pamamaraang hindi suportado.",
"INVALID_AUTH_REQUEST": "Nabigong maibahagi ang kredensyal — May kulang o maling impormasyon sa kahilingan.",
"REQUEST_COULD_NOT_BE_PROCESSED": "Hindi naibahagi ang kredensyal — Hindi naproseso ang kahilingan dahil sa teknikal na isyu.",
"INVALID_PRESENTATION_DEFINITION_URI": "Hindi naibahagi ang kredensyal — Mali ang format ng datos sa kahilingan ng verifier.",
"SEND_VP_ERROR": "Nabigong maibahagi ang kredensyal dahil sa teknikal na isyu."
}
},
"DeviceInfoList": {
@@ -216,6 +221,10 @@
"title": "Pakisuri ang iyong koneksyon at subukang muli",
"message": "Mangyaring kumonekta sa internet at subukang muli."
},
"networkRequestFailed": {
"title": "Nabigo ang kahilingan sa network",
"message": "Hindi namin maiproseso ang iyong kahilingan sa ngayon."
},
"biometricsCancelled": {
"title": "Gusto mo bang kanselahin ang pag-download?",
"message": "Kinakailangan ang biometric confirmation para magpatuloy sa pag-download ng card."
@@ -882,7 +891,7 @@
"title": "Oops! Isang Error ang Naganap.",
"message": "Nagkaroon ng teknikal na problema habang pinoproseso ang iyong kahilingan. Maaari mong subukang muli pagkatapos ng ilang sandali."
},
"vpFormatsNotSupported": {
"vpFormatsNotSupported": {
"title": "Hindi Suportadong Kahilingan",
"message": "Ang kahilingang ito ay hindi suportado. Pakiusap, sabihan ang tagapagsuri na subukan sa ibang paraan."
},
@@ -904,7 +913,7 @@
},
"sendVPError": {
"title": "May Nangyaring Mali",
"message": "Mensahe: Hindi namin naibahagi ang iyong card dahil sa isang teknikal na isyu. I-click ang Retry para subukang muli, o pumunta sa Home upang lumabas."
"message": "Nagkaroon ng teknikal na problema habang ibinabahagi ang iyong card. Pindutin ang \"Subukang Muli\" para subukang muli o bumalik sa \"Home\"."
},
"noImage": {
"title": "Isang Error ang Naganap!",
@@ -1101,4 +1110,4 @@
"confirm": "Oo, Pinagkakatiwalaan Ko Ito",
"cancel": "Hindi, Bumalik Tayo"
}
}
}

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "क्रेडेंशियल प्रेजेंटेशन सफलतापूर्वक साझा किया गया है.",
"SHARED_WITH_FACE_VERIFIACTION": "चेहरा सत्यापन सफल है, और क्रेडेंशियल प्रस्तुति सफलतापूर्वक साझा की गई है।",
"VERIFIER_AUTHENTICATION_FAILED": "वीपी साझाकरण के दौरान सत्यापन सेवा को प्रमाणित करने में विफल।",
"INVALID_AUTH_REQUEST": "QR कोड में अमान्य या अनुपलब्ध डेटा है. सत्यापन सेवा से जांचें.",
"USER_DECLINED_CONSENT": "उपयोगकर्ता ने क्रेडेंशियल प्रेजेंटेशन साझा करने के लिए सहमति देने से इनकार कर दिया।",
"USER_DECLINED_CONSENT": "उपयोगकर्ता ने क्रेडेंशियल प्रस्तुति साझा करने के लिए सहमति अस्वीकार कर दी।",
"SHARED_AFTER_RETRY": "पुन: प्रयास के बाद क्रेडेंशियल प्रस्तुति सफलतापूर्वक साझा की जाती है।",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "चेहरे का सत्यापन सफल है, और पुन: प्रयास के बाद क्रेडेंशियल प्रस्तुति सफलतापूर्वक साझा की जाती है।",
"SHARING_FAILED": "तकनीकी त्रुटि के कारण क्रेडेंशियल प्रेजेंटेशन साझा करना विफल रहा।",
@@ -31,7 +30,13 @@
"NO_SELECTED_VC_HAS_IMAGE": "क्रेडेंशियल प्रस्तुति साझाकरण विफल रहा क्योंकि चयनित सत्यापन योग्य क्रेडेंशियल में चेहरे की छवि का अभाव है।",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "अनुरोधित दावों के आधार पर, मेनू से साझा करने के दौरान क्रेडेंशियल प्रस्तुतिकरण बेमेल हुआ: {{info}}।",
"NO_CREDENTIAL_MATCHING_REQUEST": "प्रदान किए गए दावों के आधार पर सत्यापन सेवा के अनुरोध से मेल खाने वाला कोई क्रेडेंशियल नहीं मिला: {{info}}।",
"TECHNICAL_ERROR": "तकनीकी त्रुटि के कारण क्रेडेंशियल प्रेजेंटेशन साझाकरण आगे नहीं बढ़ सका।"
"TECHNICAL_ERROR": "आपके अनुरोध को प्रोसेस करते समय कुछ गलत हो गया।",
"INVALID_REQUEST_URI_METHOD": "क्रेडेंशियल साझा नहीं किया गया — सत्यापनकर्ता ने एक असमर्थित विधि का उपयोग किया।",
"INVALID_AUTH_REQUEST": "क्रेडेंशियल साझा विफल रहा — अनुरोध में जानकारी अधूरी या गलत थी।",
"REQUEST_COULD_NOT_BE_PROCESSED": "क्रेडेंशियल साझा नहीं किया गया — तकनीकी समस्या के कारण अनुरोध प्रोसेस नहीं हो सका।",
"INVALID_PRESENTATION_DEFINITION_URI": "क्रेडेंशियल साझा नहीं किया गया — सत्यापनकर्ता की अनुरोध में गलत डेटा प्रारूप था।",
"SEND_VP_ERROR": "तकनीकी समस्या के कारण क्रेडेंशियल साझा करना विफल रहा।"
}
},
"DeviceInfoList": {
@@ -217,6 +222,10 @@
"title": "कोई इंटरनेट कनेक्शन नहीं",
"message": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें"
},
"networkRequestFailed": {
"title": "नेटवर्क अनुरोध विफल हुआ",
"message": "हम इस समय आपके अनुरोध को संसाधित करने में असमर्थ हैं।"
},
"biometricsCancelled": {
"title": "क्या आप डाउनलोड रद्द करना चाहते हैं?",
"message": "कार्ड डाउनलोड करना जारी रखने के लिए बायोमेट्रिक पुष्टिकरण आवश्यक है।"
@@ -907,7 +916,7 @@
},
"sendVPError": {
"title": "कुछ गलत हो गया",
"message": "संदेश: तकनीकी समस्या के कारण हम आपका कार्ड साझा नहीं कर सके। पुनः प्रयास करने के लिए 'पुनः प्रयास करें' पर क्लिक करें, या बाहर निकलने के लिए 'मुखपृष्ठ' पर जाए।"
"message": "आपका कार्ड साझा करते समय एक तकनीकी समस्या हुई। फिर से प्रयास करने के लिए \"पुनः प्रयास करें\" टैप करें या \"होम\" पर वापस जाए।"
},
"noImage": {
"title": "एक त्रुटि हुई!",
@@ -1102,4 +1111,4 @@
"confirm": "हाँ, मैं इस पर भरोसा करता हूँ",
"cancel": "नहीं, मुझे वापस ले चलो"
}
}
}

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "ರುಜುವಾತು ಪ್ರಸ್ತುತಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ.",
"SHARED_WITH_FACE_VERIFIACTION": "ಮುಖ ಪರಿಶೀಲನೆ ಯಶಸ್ವಿಯಾಗಿದೆ ಮತ್ತು ರುಜುವಾತು ಪ್ರಸ್ತುತಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ.",
"VERIFIER_AUTHENTICATION_FAILED": "VP ಹಂಚಿಕೆಯ ಸಮಯದಲ್ಲಿ ಪರಿಶೀಲನೆ ಸೇವೆಯನ್ನು ದೃಢೀಕರಿಸಲು ವಿಫಲವಾಗಿದೆ.",
"INVALID_AUTH_REQUEST": "QR ಕೋಡ್ ಅಮಾನ್ಯ ಅಥವಾ ಕಾಣೆಯಾದ ಡೇಟಾವನ್ನು ಹೊಂದಿದೆ. ಪರಿಶೀಲನಾ ಸೇವೆಯೊಂದಿಗೆ ಪರಿಶೀಲಿಸಿ.",
"USER_DECLINED_CONSENT": "ರುಜುವಾತು ಪ್ರಸ್ತುತಿ ಹಂಚಿಕೆಗೆ ಬಳಕೆದಾರರು ಒಪ್ಪಿಗೆ ನಿರಾಕರಿಸಿದ್ದಾರೆ.",
"USER_DECLINED_CONSENT": "ಬಳಕೆದಾರರು ರುಜುವಾತು ಪ್ರಸ್ತುತಿ ಹಂಚಿಕೆಗೆ ಒಪ್ಪಿಗೆ ನೀಡಲಿಲ್ಲ.",
"SHARED_AFTER_RETRY": "ಮರುಪ್ರಯತ್ನದ ನಂತರ ರುಜುವಾತು ಪ್ರಸ್ತುತಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ.",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "ಮುಖ ಪರಿಶೀಲನೆ ಯಶಸ್ವಿಯಾಗಿದೆ ಮತ್ತು ಮರುಪ್ರಯತ್ನದ ನಂತರ ರುಜುವಾತು ಪ್ರಸ್ತುತಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ.",
"SHARING_FAILED": "ತಾಂತ್ರಿಕ ದೋಷದಿಂದಾಗಿ ರುಜುವಾತು ಪ್ರಸ್ತುತಿ ಹಂಚಿಕೆ ವಿಫಲವಾಗಿದೆ.",
@@ -31,7 +30,12 @@
"NO_SELECTED_VC_HAS_IMAGE": "ಆಯ್ಕೆಮಾಡಿದ ಪರಿಶೀಲಿಸಬಹುದಾದ ರುಜುವಾತುಗಳಲ್ಲಿ ಮುಖದ ಚಿತ್ರದ ಕೊರತೆಯಿಂದಾಗಿ ರುಜುವಾತು ಪ್ರಸ್ತುತಿ ಹಂಚಿಕೆ ವಿಫಲವಾಗಿದೆ.",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "ವಿನಂತಿಸಿದ ಹಕ್ಕುಗಳ ಆಧಾರದ ಮೇಲೆ ಮೆನುವಿನಿಂದ ಹಂಚಿಕೊಳ್ಳುವಾಗ ರುಜುವಾತುಗಳ ಪ್ರಸ್ತುತಿ ಹೊಂದಿಕೆಯಾಗುತ್ತಿಲ್ಲ: {{info}}.",
"NO_CREDENTIAL_MATCHING_REQUEST": "ಒದಗಿಸಿದ ಹಕ್ಕುಗಳ ಆಧಾರದ ಮೇಲೆ ಪರಿಶೀಲನೆ ಸೇವೆಯ ವಿನಂತಿಗೆ ಹೊಂದಿಕೆಯಾಗುವ ಯಾವುದೇ ರುಜುವಾತುಗಳು ಕಂಡುಬಂದಿಲ್ಲ: {{info}}.",
"TECHNICAL_ERROR": "ತಾಂತ್ರಿಕ ದೋಷದಿಂದಾಗಿ ರುಜುವಾತು ಪ್ರಸ್ತುತಿ ಹಂಚಿಕೆಯನ್ನು ಮುಂದುವರಿಸಲಾಗಲಿಲ್ಲ."
"TECHNICAL_ERROR": "ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಾಗ ಏನೋ ತಪ್ಪು ಸಂಭವಿಸಿದೆ.",
"INVALID_REQUEST_URI_METHOD": "ಅಧికారಪತ್ರವನ್ನು ಹಂಚಲಾಗಿಲ್ಲ — ಪರಿಶೀಲಕರೊಬ್ಬರು ಬೆಂಬಲವಿಲ್ಲದ ವಿಧಾನವನ್ನು ಬಳಸಿದರು.",
"INVALID_AUTH_REQUEST": "ಅಧికారಪತ್ರ ಹಂಚಿಕೆಯಲ್ಲಿ ವಿಫಲವಾಯಿತು — ವಿನಂತಿಯಲ್ಲಿ ತಪ್ಪು ಅಥವಾ ಕೊರತೆಯ ಮಾಹಿತಿ ಇದೆ.",
"REQUEST_COULD_NOT_BE_PROCESSED": "ಅಧಕಾರಪತ್ರವನ್ನು ಹಂಚಲಾಗಿಲ್ಲ — ತಾಂತ್ರಿಕ ತೊಂದರೆಯಿಂದ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.",
"INVALID_PRESENTATION_DEFINITION_URI": "ಅಧಕಾರಪತ್ರವನ್ನು ಹಂಚಲಾಗಿಲ್ಲ — ಪರಿಶೀಲಕರ ವಿನಂತಿಯಲ್ಲಿ ತಪ್ಪಾದ ಡೇಟಾ ಸ್ವರೂಪವಿದೆ.",
"SEND_VP_ERROR": "ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆಯ ಕಾರಣದಿಂದ ಅಧಿಕಾರಪತ್ರ ಹಂಚಿಕೆಯಲ್ಲಿ ವಿಫಲವಾಯಿತು."
}
},
"DeviceInfoList": {
@@ -216,6 +220,10 @@
"title": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ",
"message": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ"
},
"networkRequestFailed": {
"title": "ನೆಟ್‌ವರ್ಕ್ ವಿನಂತಿ ವಿಫಲವಾಗಿದೆ",
"message": "ಈ ಕ್ಷಣದಲ್ಲಿ ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
},
"biometricsCancelled": {
"title": "ನೀವು ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲು ಬಯಸುವಿರಾ?",
"message": "ಕಾರ್ಡ್ ಡೌನ್‌ಲೋಡ್ ಮಾಡುವುದನ್ನು ಮುಂದುವರಿಸಲು ಬಯೋಮೆಟ್ರಿಕ್ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ."
@@ -883,7 +891,7 @@
"title": "ಓಹ್! ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ.",
"message": "ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಾಗ ತಾಂತ್ರಿಕ ತೊಂದರೆ ಸಂಭವಿಸಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
},
"vpFormatsNotSupported": {
"vpFormatsNotSupported": {
"title": "ಬೆಂಬಲಿಸಲ್ಪಟ್ಟ ವಿನಂತಿಯಲ್ಲ",
"message": "ಈ ವಿನಂತಿಯನ್ನು ಬೆಂಬಲಿಸಲಾಗದು. ದಯವಿಟ್ಟು ಪರಿಶೀಲಕರನ್ನು ಬೇರೆ ಮಾರ್ಗದಿಂದ ಪ್ರಯತ್ನಿಸಲು ಕೇಳಿ."
},
@@ -905,7 +913,7 @@
},
"sendVPError": {
"title": "ಏನೋ ತಪ್ಪು ಸಂಭವಿಸಿದೆ",
"message": "ಸಂದೇಶ: ತಾಂತ್ರಿಕ ತೊಂದರೆಯಿಂದಾಗಿ ನಿಮ್ಮ ಕಾರ್ಡ್ ಅನ್ನು ಹಂಚಲಾಗಲಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು 'ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ' ಕ್ಲಿಕ್ ಮಾಡಿ ಅಥವಾ ನಿರ್ಗಮಿಸಲು 'ಮುಖಪುಟ' ಗೆ ಹಗಿ."
"message": "ನಿಮ್ಮ ಕಾರ್ಡ್ ಹಂಚಿಕೊಳ್ಳುವಾಗ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆ ಸಂಭವಿಸಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು \"ಮರುಪ್ರಯತ್ನಿಸಿ\" ಒತ್ತಿರಿ ಅಥವಾ \"ಮುಖಪುಟ\" ಗೆ ಹಿಂದಿರುಗಿ."
},
"noImage": {
"title": "ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ!",
@@ -1102,4 +1110,4 @@
"confirm": "ಹೌದು, ನಾನು ನಂಬುತ್ತೇನೆ",
"cancel": "ಇಲ್ಲ, ನನನ್ನು ಹಿಂದಕ್ಕೆ ಕರೆ"
}
}
}

View File

@@ -19,8 +19,7 @@
"SHARED_SUCCESSFULLY": "நற்சான்றிதழ் வழங்கல் வெற்றிகரமாகப் பகிரப்பட்டது.",
"SHARED_WITH_FACE_VERIFIACTION": "முக சரிபார்ப்பு வெற்றிகரமாக உள்ளது, மேலும் நற்சான்றிதழ் விளக்கக்காட்சி வெற்றிகரமாக பகிரப்பட்டது.",
"VERIFIER_AUTHENTICATION_FAILED": "VP பகிர்வின் போது சரிபார்ப்பு சேவையை அங்கீகரிப்பதில் தோல்வி.",
"INVALID_AUTH_REQUEST": "QR குறியீட்டில் தவறான அல்லது விடுபட்ட தரவு உள்ளது. சரிபார்ப்பு சேவையுடன் சரிபார்க்கவும்.",
"USER_DECLINED_CONSENT": "நற்சான்றிதழ் விளக்கக்காட்சி பகிர்வுக்கான ஒப்புதலை பயனர் நிராகரித்தார்.",
"USER_DECLINED_CONSENT": "பயனர் சான்றுகள் பகிர்வுக்கு ஒப்புதலை நிராகரித்தார்.",
"SHARED_AFTER_RETRY": "நற்சான்றிதழ் விளக்கக்காட்சி மீண்டும் முயற்சித்த பிறகு வெற்றிகரமாகப் பகிரப்பட்டது.",
"SHARED_WITH_FACE_VERIFICATION_AFTER_RETRY": "முகச் சரிபார்ப்பு வெற்றிகரமாக உள்ளது, மீண்டும் முயற்சித்த பிறகு நற்சான்றிதழ் விளக்கக்காட்சி வெற்றிகரமாகப் பகிரப்பட்டது.",
"SHARING_FAILED": "தொழில்நுட்பப் பிழை காரணமாக நற்சான்றிதழ் வழங்கல் பகிர்வு தோல்வியடைந்தது.",
@@ -31,7 +30,12 @@
"NO_SELECTED_VC_HAS_IMAGE": "தேர்ந்தெடுக்கப்பட்ட சரிபார்க்கக்கூடிய நற்சான்றிதழ்களில் முகப் படம் இல்லாததால் நற்சான்றிதழ் வழங்கல் பகிர்வு தோல்வியடைந்தது.",
"CREDENTIAL_MISMATCH_FROM_KEBAB": "கோரப்பட்ட உரிமைகோரல்களின் அடிப்படையில், மெனுவிலிருந்து பகிரும் போது நற்சான்றிதழ்கள் வழங்கல் பொருந்தவில்லை: {{info}}.",
"NO_CREDENTIAL_MATCHING_REQUEST": "வழங்கப்பட்ட உரிமைகோரல்களின் அடிப்படையில் சரிபார்ப்பு சேவையின் கோரிக்கையுடன் பொருந்தக்கூடிய நற்சான்றிதழ்கள் எதுவும் கண்டறியப்படவில்லை: {{info}}.",
"TECHNICAL_ERROR": "தொழில்நுட்ப பிழை காரணமாக நற்சான்றிதழ் வழங்கல் பகிர்வை தொடர முடியவில்லை."
"TECHNICAL_ERROR": "உங்கள் கோரிக்கையை செயலாக்கும் போது ஏதோ தவறு ஏற்பட்டது.",
"INVALID_REQUEST_URI_METHOD": "அங்கீகாரம் பகிரப்படவில்லை — சரிபார்ப்பவர் ஆதரிக்கப்படாத முறையை பயன்படுத்தினார்.",
"INVALID_AUTH_REQUEST": "அங்கீகாரம் பகிர்வு தோல்வியடைந்தது — கோரிக்கையில் தவறான அல்லது காணாமற்போன தகவல்கள் உள்ளன.",
"REQUEST_COULD_NOT_BE_PROCESSED": "அங்கீகாரம் பகிரப்படவில்லை — தொழில்நுட்ப பிரச்சனை காரணமாக கோரிக்கையை செயலாக்க முடியவில்லை.",
"INVALID_PRESENTATION_DEFINITION_URI": "அங்கீகாரம் பகிரப்படவில்லை — சரிபார்ப்பவரின் கோரிக்கையில் தவறான தரவுகள் உள்ளன.",
"SEND_VP_ERROR": "அங்கீகாரம் பகிர்வதில் தொழில்நுட்ப பிரச்சனை ஏற்பட்டது."
}
},
"DeviceInfoList": {
@@ -216,6 +220,10 @@
"title": "இணைய இணைப்பு இல்லை",
"message": "உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்"
},
"networkRequestFailed": {
"title": "நெட்வொர்க் கோரிக்கை தோல்வியடைந்தது",
"message": "தற்போது உங்கள் கோரிக்கையை செயலாக்க முடியவில்லை."
},
"biometricsCancelled": {
"title": "பதிவிறக்கத்தை ரத்து செய்ய வேண்டுமா?",
"message": "கார்டை தொடர்ந்து பதிவிறக்க பயோமெட்ரிக் உறுதிப்படுத்தல் தேவை."
@@ -905,7 +913,7 @@
},
"sendVPError": {
"title": "ஏதோ தவறு நடந்துவிட்டது",
"message": "தகவல்: தொழில்நுட்ப சிக்கலாலஉங்கள் அட்டையை பகிர முடியவில்லை. மீண்டும் முயற்சிக்க 'மீண்டும் முயற்சி' என்பதை கிளிக் செய்யவும், அல்லது வெளியேற 'முகப்பு' என்பதைக் கிளிக் செய்யவும்."
"message": "உங்கள் கார்டைப் பகிரும்போது தொழில்நுட்ப சிக்கல் ஏற்பட்டது. மீண்டும் முயற்சிக்க \"மீண்டும் முயற்சி செய்\" என்பதைத் தட்டவும் அல்லது \"முகப்பு\" பக்கம் திரும்பவும்."
},
"noImage": {
"title": "ஒரு பிழை ஏற்பட்டது!",
@@ -1102,4 +1110,4 @@
"confirm": "ஆம், நம்புகிறேன்",
"cancel": "இல்லை, என்னை திரும்ப அழைத்துச் செல்"
}
}
}

View File

@@ -8,6 +8,7 @@ import {
import {
EXPIRED_VC_ERROR_CODE,
MY_VCS_STORE_KEY,
NO_INTERNET,
REQUEST_TIMEOUT,
isIOS,
} from '../../shared/constants';
@@ -25,9 +26,8 @@ import {
} from '../../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
import {NativeModules} from 'react-native';
import {KeyTypes} from '../../shared/cryptoutil/KeyTypes';
import {VCActivityLog} from '../../components/ActivityLogEvent';
import {isNetworkError} from '../../shared/Utils';
import {isNetworkError, parseJSON} from '../../shared/Utils';
import {issuerType} from './IssuersMachine';
const {RNSecureKeystoreModule} = NativeModules;
@@ -52,9 +52,6 @@ export const IssuersActions = (model: any) => {
setIssuers: model.assign({
issuers: (_: any, event: any) => event.data as issuerType[],
}),
setNoInternet: model.assign({
errorMessage: () => ErrorMessage.NO_INTERNET,
}),
setLoadingReasonAsDisplayIssuers: model.assign({
loadingReason: 'displayIssuers',
}),
@@ -75,7 +72,7 @@ export const IssuersActions = (model: any) => {
return proofTypesSupported.jwt
.proof_signing_alg_values_supported as string[];
} else {
return [KeyTypes.RS256] as string[];
return [] as string[];
}
},
}),
@@ -85,17 +82,6 @@ export const IssuersActions = (model: any) => {
resetSelectedCredentialType: model.assign({
selectedCredentialType: {},
}),
setNetworkOrTechnicalError: model.assign({
errorMessage: (_: any, event: any) => {
console.error(
`Error occurred during ${event} flow`,
event.data.message,
);
return isNetworkError(event.data.message)
? ErrorMessage.NO_INTERNET
: ErrorMessage.TECHNICAL_DIFFICULTIES;
},
}),
setCredentialTypeListDownloadFailureError: model.assign({
errorMessage: (_: any, event: any) => {
if (isNetworkError(event.data.message)) {
@@ -109,9 +95,12 @@ export const IssuersActions = (model: any) => {
errorMessage: (_: any, event: any) => {
console.error(`Error occurred while ${event} -> `, event.data.message);
const error = event.data.message;
if (isNetworkError(error)) {
if (error.includes(NO_INTERNET)) {
return ErrorMessage.NO_INTERNET;
}
if (isNetworkError(error)) {
return ErrorMessage.NETWORK_REQUEST_FAILED;
}
if (error.includes(REQUEST_TIMEOUT)) {
return ErrorMessage.REQUEST_TIMEDOUT;
}
@@ -126,9 +115,6 @@ export const IssuersActions = (model: any) => {
return ErrorMessage.GENERIC;
},
}),
setOIDCConfigError: model.assign({
errorMessage: (_: any, event: any) => event.data.toString(),
}),
resetError: model.assign({
errorMessage: '',
}),
@@ -236,20 +222,24 @@ export const IssuersActions = (model: any) => {
return context.issuers.find(issuer => issuer.issuer_id === event.id);
},
}),
resetSelectedIssuer: model.assign({
selectedIssuer: () => ({} as issuerType),
}),
updateIssuerFromWellknown: model.assign({
selectedIssuer: (context: any, event: any) => ({
...context.selectedIssuer,
credential_audience: event.data.credential_issuer,
credential_endpoint: event.data.credential_endpoint,
credential_configurations_supported:
event.data.credential_configurations_supported,
display: event.data.display,
authorization_servers: event.data.authorization_servers,
}),
selectedIssuerWellknownResponse: (_: any, event: any) => {
return event.data;
},
}),
setCredential: model.assign({
credential: (_: any, event: any) => event.data,
credential: (_: any, event: any) => event.data.credential,
}),
setQrData: model.assign({
qrData: (_: any, event: any) => event.data,
@@ -261,7 +251,7 @@ export const IssuersActions = (model: any) => {
}),
setAccessToken: model.assign({
accessToken: (_: any, event: any) => {
return event.accessToken;
return event.data.access_token;
},
}),
setCNonce: model.assign({
@@ -269,20 +259,41 @@ export const IssuersActions = (model: any) => {
return event.cNonce;
},
}),
setOfferCredentialTypeContexts: model.assign({
selectedCredentialType: (context: any, event: any) => {
return event.credentialTypes[0];
},
supportedCredentialTypes: (context: any, event: any) => {
return event.credentialTypes;
},
accessToken: (context: any, event: any) => {
return event.accessToken;
},
cNonce: (context: any, event: any) => {
return event.cNonce;
setCredentialConfigurationId: model.assign({
credentialConfigurationId: (_: any, event: any) => {
return event.data.credentialConfigurationId;
},
}),
setCredentialOfferCredentialType: model.assign({
selectedCredentialType: (context: any, event: any) => {
let credentialTypes: Array<{id: string; [key: string]: any}> = [];
const credentialConfigurationId = context.credentialConfigurationId;
const issuerMetadata = context.selectedIssuerWellknownResponse;
if (
issuerMetadata.credential_configurations_supported[
credentialConfigurationId
]
) {
credentialTypes.push({
id: credentialConfigurationId,
...issuerMetadata.credential_configurations_supported[
credentialConfigurationId
],
});
return credentialTypes[0];
}
},
}),
supportedCredentialTypes: (context: any, event: any) => {
return event.credentialTypes;
},
accessToken: (context: any, event: any) => {
return event.accessToken;
},
cNonce: (context: any, event: any) => {
return event.cNonce;
},
setRequestTxCode: model.assign({
isTransactionCodeRequested: (_: any, event: any) => {
return true;
@@ -295,22 +306,32 @@ export const IssuersActions = (model: any) => {
},
}),
setCredentialOfferIssuerWellknownResponse: model.assign({
selectedIssuerWellknownResponse: (_: any, event: any) => {
return event.issuerMetadata;
selectedIssuer: (_: any, event: any) => {
return event.data;
},
wellknownKeyTypes: (_: any, event: any) => {
const credType = Object.entries(event.credentialTypes)[0][1];
const proofTypesSupported = credType.proof_types_supported;
if (proofTypesSupported?.jwt) {
return proofTypesSupported.jwt
.proof_signing_alg_values_supported as string[];
} else {
return [KeyTypes.RS256] as string[];
}
selectedIssuerWellknownResponse: (_: any, event: any) => {
return event.data;
},
}),
updateSelectedIssuerWellknownResponse: model.assign({
selectedIssuerWellknownResponse: (_: any, event: any) => event.data,
setWellknwonKeyTypes: model.assign({
wellknownKeyTypes: (_: any, event: any) => {
return event.proofSigningAlgosSupported;
},
}),
setSelectedCredentialIssuer: model.assign({
credentialOfferCredentialIssuer: (_: any, event: any) => {
return event.issuer;
},
}),
setTokenRequestObject: model.assign({
tokenRequestObject: (_: any, event: any) => {
return parseJSON(event.tokenRequest);
},
}),
setTokenResponseObject: model.assign({
tokenResponse: (_: any, event: any) => {
return event.data;
},
}),
setSelectedIssuerId: model.assign({
selectedIssuerId: (_: any, event: any) => event.id,
@@ -330,22 +351,17 @@ export const IssuersActions = (model: any) => {
txCodeDescription: (_: any, event: any) => event.description,
txCodeLength: (_: any, event: any) => event.length,
}),
setCredentialOfferIssuerMetadata: model.assign({
credentialOfferIssuerMetadata: (_: any, event: any) => {
return event.issuerMetadata;
},
}),
setIssuerDisplayDetails: model.assign({
issuerLogo: (context: any, _: any) => {
const displayArray = context.credentialOfferIssuerMetadata?.display;
issuerLogo: (_: any, event: any) => {
const displayArray = event.issuerDisplay;
const display = displayArray
? getDisplayObjectForCurrentLanguage(displayArray)
: undefined;
return display?.logo?.url ?? '';
},
issuerName: (context: any, _: any) => {
const displayArray = context.credentialOfferIssuerMetadata?.display;
issuerName: (_: any, event: any) => {
const displayArray = event.issuerDisplay;
const display = displayArray
? getDisplayObjectForCurrentLanguage(displayArray)
: undefined;
@@ -353,13 +369,13 @@ export const IssuersActions = (model: any) => {
},
}),
setFlowType: model.assign({
setCredentialOfferFlowType: model.assign({
isCredentialOfferFlow: (_: any, event: any) => {
return true;
},
}),
resetFlowType: model.assign({
resetCredentialOfferFlowType: model.assign({
isCredentialOfferFlow: (_: any, event: any) => {
return false;
},
@@ -402,7 +418,9 @@ export const IssuersActions = (model: any) => {
type: 'VC_DOWNLOADED',
timestamp: Date.now(),
deviceName: '',
issuer: context.selectedIssuerId,
issuer:
context.selectedIssuer.credential_issuer_host ??
context.credentialOfferCredentialIssuer,
credentialConfigurationId: context.selectedCredentialType.id,
}),
context.selectedIssuerWellknownResponse,
@@ -441,8 +459,9 @@ export const IssuersActions = (model: any) => {
},
updateVerificationErrorMessage: assign({
verificationErrorMessage: (_, event: any) =>
(event.data as Error).message,
verificationErrorMessage: (_, event: any) => {
return (event.data as Error).message;
},
}),
resetVerificationErrorMessage: model.assign({

View File

@@ -33,5 +33,6 @@ export const IssuersEvents = {
TX_CODE_REQUEST: () => ({}),
TX_CODE_RECEIVED: (txCode: string) => ({txCode}),
ON_CONSENT_GIVEN: () => ({}),
TRUST_ISSUER_CONSENT_REQUEST: (issuerMetadata: object) => ({issuerMetadata})
TRUST_ISSUER_CONSENT_REQUEST: (issuerMetadata: object) => ({issuerMetadata}),
TOKEN_REQUEST: (tokenRequest: object) => ({tokenRequest}),
};

View File

@@ -1,6 +1,5 @@
import {isSignedInResult} from '../../shared/CloudBackupAndRestoreUtils';
import {ErrorMessage, OIDCErrors} from '../../shared/openId4VCI/Utils';
import {isHardwareKeystoreExists} from '../../shared/cryptoutil/cryptoUtil';
import {BiometricCancellationError} from '../../shared/error/BiometricCancellationError';
import {VerificationErrorType} from '../../shared/vcjs/verifyCredential';
@@ -17,32 +16,6 @@ export const IssuersGuards = () => {
return context.keyType == '';
},
isInternetConnected: (_: any, event: any) => !!event.data.isConnected,
isOIDCflowCancelled: (_: any, event: any) => {
// iOS & Android have different error strings for user cancelled flow
const err = [
OIDCErrors.OIDC_FLOW_CANCELLED_ANDROID,
OIDCErrors.OIDC_FLOW_CANCELLED_IOS,
];
return (
!!event.data &&
typeof event.data.toString === 'function' &&
err.some(e => event.data.toString().includes(e))
);
},
isOIDCConfigError: (_: any, event: any) => {
return (
!!event.data &&
typeof event.data.toString === 'function' &&
event.data.toString().includes(OIDCErrors.OIDC_CONFIG_ERROR_PREFIX)
);
},
isGrantTypeNotSupportedError: (_: any, event: any) => {
return (
!!event.data &&
event.data.toString() ===
OIDCErrors.AUTHORIZATION_ENDPOINT_DISCOVERY.GRANT_TYPE_NOT_SUPPORTED
);
},
canSelectIssuerAgain: (context: any) => {
return (
context.errorMessage.includes(OIDCErrors.OIDC_CONFIG_ERROR_PREFIX) ||
@@ -50,18 +23,13 @@ export const IssuersGuards = () => {
);
},
shouldFetchIssuersAgain: (context: any) => context.issuers.length === 0,
isCustomSecureKeystore: () => isHardwareKeystoreExists,
hasUserCancelledBiometric: (_: any, event: any) =>
event.data instanceof BiometricCancellationError,
isGenericError: (_: any, event: any) => {
const errorMessage = event.data.message;
return errorMessage === ErrorMessage.GENERIC;
},
isCredentialOfferFlow: (context: any) => {
return context.isCredentialOfferFlow;
},
isIssuerIdInTrustedIssuers: (_: any,event:any) => {
isIssuerIdInTrustedIssuers: (_: any, event: any) => {
return event.data;
}
},
};
};

File diff suppressed because one or more lines are too long

View File

@@ -1,556 +0,0 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.credentialDownloadFromOffer:invocation[0]': {
type: 'done.invoke.issuersMachine.credentialDownloadFromOffer:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.displayIssuers:invocation[0]': {
type: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]': {
type: 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]': {
type: 'done.invoke.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]': {
type: 'done.invoke.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.downloadCredentials:invocation[0]': {
type: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]': {
type: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.getCredentialTypes:invocation[0]': {
type: 'done.invoke.issuersMachine.getCredentialTypes:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.proccessingCredential:invocation[0]': {
type: 'done.invoke.issuersMachine.proccessingCredential:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.storing:invocation[0]': {
type: 'done.invoke.issuersMachine.storing:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.issuersMachine.verifyingCredential:invocation[0]': {
type: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]': {
type: 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.displayIssuers:invocation[0]': {
type: 'error.platform.issuersMachine.displayIssuers:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]': {
type: 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]': {
type: 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]': {
type: 'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.downloadCredentials:invocation[0]': {
type: 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]': {
type: 'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.getCredentialTypes:invocation[0]': {
type: 'error.platform.issuersMachine.getCredentialTypes:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.verifyingCredential:invocation[0]': {
type: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
addIssuerToTrustedIssuers: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.addingIssuerToTrustedIssuers:invocation[0]';
checkIssuerIdInStoredTrustedIssuers:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList:invocation[0]';
constructProof: 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]';
constructProofForTrustedIssuers: 'done.invoke.issuersMachine.downloadCredentials.constructProof:invocation[0]';
downloadCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
downloadCredentialFromOffer: 'done.invoke.issuersMachine.credentialDownloadFromOffer:invocation[0]';
downloadIssuerWellknown: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
downloadIssuersList: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
generateKeyPair:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
getCredentialTypes: 'done.invoke.issuersMachine.getCredentialTypes:invocation[0]';
getKeyOrderList:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]';
getKeyPair:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
isUserSignedAlready: 'done.invoke.issuersMachine.storing:invocation[0]';
sendConsentGiven: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]';
sendConsentNotGiven: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentNotGiven:invocation[0]';
sendTxCode: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]';
updateCredential: 'done.invoke.issuersMachine.proccessingCredential:invocation[0]';
verifyCredential: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
};
missingImplementations: {
actions:
| 'downloadIssuerWellknown'
| 'loadKeyPair'
| 'logDownloaded'
| 'resetCredentialOfferIssuer'
| 'resetError'
| 'resetLoadingReason'
| 'resetQrData'
| 'resetRequestConsentToTrustIssuer'
| 'resetRequestTxCode'
| 'resetSelectedCredentialType'
| 'resetVerificationErrorMessage'
| 'resetVerificationResult'
| 'sendBackupEvent'
| 'sendDownloadingFailedToVcMeta'
| 'sendErrorEndEvent'
| 'sendImpressionEvent'
| 'sendSuccessEndEvent'
| 'setAccessToken'
| 'setCNonce'
| 'setCredential'
| 'setCredentialOfferIssuer'
| 'setCredentialOfferIssuerMetadata'
| 'setCredentialOfferIssuerWellknownResponse'
| 'setCredentialTypeListDownloadFailureError'
| 'setCredentialWrapper'
| 'setError'
| 'setIssuerDisplayDetails'
| 'setIssuers'
| 'setLoadingReasonAsDisplayIssuers'
| 'setLoadingReasonAsDownloadingCredentials'
| 'setLoadingReasonAsSettingUp'
| 'setMetadataInCredentialData'
| 'setNetworkOrTechnicalError'
| 'setOfferCredentialTypeContexts'
| 'setPrivateKey'
| 'setPublicKey'
| 'setQrData'
| 'setRequestConsentToTrustIssuer'
| 'setRequestTxCode'
| 'setSelectedCredentialType'
| 'setSelectedIssuerId'
| 'setSelectedIssuers'
| 'setSelectedKey'
| 'setSupportedCredentialTypes'
| 'setTxCode'
| 'setTxCodeDisplayDetails'
| 'setVCMetadata'
| 'setVerifiableCredential'
| 'setVerificationResult'
| 'storeKeyPair'
| 'storeVcMetaContext'
| 'storeVcsContext'
| 'storeVerifiableCredentialData'
| 'storeVerifiableCredentialMeta'
| 'updateIssuerFromWellknown'
| 'updateSelectedIssuerWellknownResponse'
| 'updateVerificationErrorMessage';
delays: never;
guards:
| 'canSelectIssuerAgain'
| 'hasUserCancelledBiometric'
| 'isCredentialOfferFlow'
| 'isGenericError'
| 'isIssuerIdInTrustedIssuers'
| 'isKeyTypeNotFound'
| 'isSignedIn'
| 'isVerificationPendingBecauseOfNetworkIssue'
| 'shouldFetchIssuersAgain';
services:
| 'addIssuerToTrustedIssuers'
| 'checkIssuerIdInStoredTrustedIssuers'
| 'constructProof'
| 'constructProofForTrustedIssuers'
| 'downloadCredential'
| 'downloadCredentialFromOffer'
| 'downloadIssuerWellknown'
| 'downloadIssuersList'
| 'generateKeyPair'
| 'getCredentialTypes'
| 'getKeyOrderList'
| 'getKeyPair'
| 'isUserSignedAlready'
| 'sendConsentGiven'
| 'sendConsentNotGiven'
| 'sendTxCode'
| 'updateCredential'
| 'verifyCredential';
};
eventsCausingActions: {
downloadIssuerWellknown: 'TRY_AGAIN';
loadKeyPair:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
logDownloaded:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
resetCredentialOfferIssuer:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]';
resetError: 'RESET_ERROR' | 'TRY_AGAIN';
resetLoadingReason:
| 'CANCEL'
| 'RESET_ERROR'
| 'done.invoke.issuersMachine.displayIssuers:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]'
| 'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]'
| 'error.platform.issuersMachine.getCredentialTypes:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
resetQrData:
| 'CANCEL'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]';
resetRequestConsentToTrustIssuer:
| 'CANCEL'
| 'ON_CONSENT_GIVEN'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]';
resetRequestTxCode:
| 'CANCEL'
| 'TX_CODE_RECEIVED'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.sendTxCode:invocation[0]';
resetSelectedCredentialType:
| 'CANCEL'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
resetVerificationErrorMessage: 'RESET_VERIFY_ERROR';
resetVerificationResult: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
sendBackupEvent: 'done.invoke.issuersMachine.storing:invocation[0]';
sendDownloadingFailedToVcMeta:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
sendErrorEndEvent: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
sendImpressionEvent: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
sendSuccessEndEvent:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
setAccessToken: 'PROOF_REQUEST';
setCNonce: 'PROOF_REQUEST';
setCredential: 'done.invoke.issuersMachine.credentialDownloadFromOffer:invocation[0]';
setCredentialOfferIssuer: 'PROOF_REQUEST';
setCredentialOfferIssuerMetadata: 'TRUST_ISSUER_CONSENT_REQUEST';
setCredentialOfferIssuerWellknownResponse: 'PROOF_REQUEST';
setCredentialTypeListDownloadFailureError: 'error.platform.issuersMachine.getCredentialTypes:invocation[0]';
setCredentialWrapper:
| 'done.invoke.issuersMachine.downloadCredentials:invocation[0]'
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]';
setError:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.constructProof:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]'
| 'error.platform.issuersMachine.displayIssuers:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
setIssuerDisplayDetails: 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]';
setIssuers: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
setLoadingReasonAsDisplayIssuers: 'TRY_AGAIN';
setLoadingReasonAsDownloadingCredentials:
| 'ON_CONSENT_GIVEN'
| 'QR_CODE_SCANNED'
| 'SELECTED_CREDENTIAL_TYPE'
| 'TRY_AGAIN'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
setLoadingReasonAsSettingUp: 'SELECTED_ISSUER' | 'TRY_AGAIN';
setMetadataInCredentialData:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
setNetworkOrTechnicalError: 'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]';
setOfferCredentialTypeContexts: 'PROOF_REQUEST';
setPrivateKey:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
setPublicKey:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
setQrData: 'ON_CONSENT_GIVEN' | 'QR_CODE_SCANNED';
setRequestConsentToTrustIssuer: 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]';
setRequestTxCode: 'TX_CODE_REQUEST';
setSelectedCredentialType: 'SELECTED_CREDENTIAL_TYPE';
setSelectedIssuerId: 'SELECTED_ISSUER';
setSelectedIssuers: 'SELECTED_ISSUER';
setSelectedKey:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]';
setSupportedCredentialTypes: 'done.invoke.issuersMachine.getCredentialTypes:invocation[0]';
setTxCode: 'TX_CODE_RECEIVED';
setTxCodeDisplayDetails: 'TX_CODE_REQUEST';
setVCMetadata:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
setVerifiableCredential:
| 'done.invoke.issuersMachine.downloadCredentials:invocation[0]'
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]';
setVerificationResult:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
storeKeyPair:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.generateKeyPair:invocation[0]';
storeVcMetaContext:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
storeVcsContext:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
storeVerifiableCredentialData:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
storeVerifiableCredentialMeta:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
updateIssuerFromWellknown: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
updateSelectedIssuerWellknownResponse: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
updateVerificationErrorMessage: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
};
eventsCausingDelays: {};
eventsCausingGuards: {
canSelectIssuerAgain: 'TRY_AGAIN';
hasUserCancelledBiometric:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.constructProof:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
isCredentialOfferFlow: 'TRY_AGAIN';
isGenericError:
| 'error.platform.issuersMachine.credentialDownloadFromOffer:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
isIssuerIdInTrustedIssuers:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList:invocation[0]';
isKeyTypeNotFound:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
isSignedIn: 'done.invoke.issuersMachine.storing:invocation[0]';
isVerificationPendingBecauseOfNetworkIssue: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
shouldFetchIssuersAgain: 'TRY_AGAIN';
};
eventsCausingServices: {
addIssuerToTrustedIssuers: 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList:invocation[0]';
checkIssuerIdInStoredTrustedIssuers:
| 'TRUST_ISSUER_CONSENT_REQUEST'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.sendConsentGiven:invocation[0]';
constructProof:
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.generateKeyPair:invocation[0]'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]';
constructProofForTrustedIssuers:
| 'TRY_AGAIN'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
downloadCredential: 'SELECTED_CREDENTIAL_TYPE';
downloadCredentialFromOffer: 'QR_CODE_SCANNED';
downloadIssuerWellknown: 'SELECTED_ISSUER' | 'TRY_AGAIN';
downloadIssuersList: 'CANCEL' | 'TRY_AGAIN' | 'xstate.init';
generateKeyPair:
| 'error.platform.issuersMachine.credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials.keyManagement.getKeyPairFromKeystore:invocation[0]';
getCredentialTypes: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
getKeyOrderList: 'PROOF_REQUEST';
getKeyPair:
| 'TRY_AGAIN'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.keyManagement.setSelectedKey:invocation[0]'
| 'done.invoke.issuersMachine.downloadCredentials.keyManagement.setSelectedKey:invocation[0]';
isUserSignedAlready:
| 'done.invoke.issuersMachine.proccessingCredential:invocation[0]'
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
sendConsentGiven:
| 'ON_CONSENT_GIVEN'
| 'done.invoke.issuersMachine.credentialDownloadFromOffer.checkingIssuerTrust:invocation[0]';
sendConsentNotGiven: 'CANCEL';
sendTxCode: 'TX_CODE_RECEIVED';
updateCredential: 'done.invoke.issuersMachine.credentialDownloadFromOffer:invocation[0]';
verifyCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
};
matchesStates:
| 'credentialDownloadFromOffer'
| 'credentialDownloadFromOffer.checkingIssuerTrust'
| 'credentialDownloadFromOffer.credentialOfferDownloadConsent'
| 'credentialDownloadFromOffer.idle'
| 'credentialDownloadFromOffer.keyManagement'
| 'credentialDownloadFromOffer.keyManagement.constructProof'
| 'credentialDownloadFromOffer.keyManagement.generateKeyPair'
| 'credentialDownloadFromOffer.keyManagement.getKeyPairFromKeystore'
| 'credentialDownloadFromOffer.keyManagement.setSelectedKey'
| 'credentialDownloadFromOffer.keyManagement.userCancelledBiometric'
| 'credentialDownloadFromOffer.sendConsentGiven'
| 'credentialDownloadFromOffer.sendConsentGiven.addingIssuerToTrustedIssuers'
| 'credentialDownloadFromOffer.sendConsentGiven.updatingTrustedIssuerList'
| 'credentialDownloadFromOffer.sendConsentNotGiven'
| 'credentialDownloadFromOffer.sendTxCode'
| 'credentialDownloadFromOffer.waitingForTxCode'
| 'displayIssuers'
| 'done'
| 'downloadCredentials'
| 'downloadCredentials.constructProof'
| 'downloadCredentials.idle'
| 'downloadCredentials.keyManagement'
| 'downloadCredentials.keyManagement.generateKeyPair'
| 'downloadCredentials.keyManagement.getKeyPairFromKeystore'
| 'downloadCredentials.keyManagement.setSelectedKey'
| 'downloadCredentials.keyManagement.userCancelledBiometric'
| 'downloadCredentials.userCancelledBiometric'
| 'downloadIssuerWellknown'
| 'error'
| 'getCredentialTypes'
| 'handleVCVerificationFailure'
| 'idle'
| 'proccessingCredential'
| 'selectingCredentialType'
| 'selectingIssuer'
| 'storing'
| 'verifyingCredential'
| 'waitingForQrScan'
| {
credentialDownloadFromOffer?:
| 'checkingIssuerTrust'
| 'credentialOfferDownloadConsent'
| 'idle'
| 'keyManagement'
| 'sendConsentGiven'
| 'sendConsentNotGiven'
| 'sendTxCode'
| 'waitingForTxCode'
| {
keyManagement?:
| 'constructProof'
| 'generateKeyPair'
| 'getKeyPairFromKeystore'
| 'setSelectedKey'
| 'userCancelledBiometric';
sendConsentGiven?:
| 'addingIssuerToTrustedIssuers'
| 'updatingTrustedIssuerList';
};
downloadCredentials?:
| 'constructProof'
| 'idle'
| 'keyManagement'
| 'userCancelledBiometric'
| {
keyManagement?:
| 'generateKeyPair'
| 'getKeyPairFromKeystore'
| 'setSelectedKey'
| 'userCancelledBiometric';
};
};
tags: never;
}

View File

@@ -1,15 +1,14 @@
import {createModel} from 'xstate/lib/model';
import {AuthorizeResult} from 'react-native-app-auth';
import { createModel } from 'xstate/lib/model';
import {
CredentialTypes,
CredentialWrapper,
IssuerWellknownResponse,
VerifiableCredential,
} from '../VerifiableCredential/VCMetaMachine/vc';
import {AppServices} from '../../shared/GlobalContext';
import {VCMetadata} from '../../shared/VCMetadata';
import {IssuersEvents} from './IssuersEvents';
import {issuerType} from './IssuersMachine';
import { AppServices } from '../../shared/GlobalContext';
import { VCMetadata } from '../../shared/VCMetadata';
import { IssuersEvents } from './IssuersEvents';
import { issuerType } from './IssuersMachine';
export const IssuersModel = createModel(
{
@@ -18,7 +17,7 @@ export const IssuersModel = createModel(
qrData: '' as string,
selectedIssuer: {} as issuerType,
selectedIssuerWellknownResponse: {} as IssuerWellknownResponse,
tokenResponse: {} as AuthorizeResult,
tokenResponse: {} as object,
errorMessage: '' as string,
loadingReason: 'displayIssuers' as string,
verifiableCredential: null as VerifiableCredential | null,
@@ -45,7 +44,9 @@ export const IssuersModel = createModel(
txCodeDescription: '' as string,
txCodeLength: null as number | null,
isCredentialOfferFlow: false as boolean,
credentialOfferIssuerMetadata: {} as object,
credentialOfferCredentialIssuer: {} as string,
tokenRequestObject: {} as object,
credentialConfigurationId: '' as string,
},
{
events: IssuersEvents,

View File

@@ -104,7 +104,3 @@ export function selectSupportedCredentialTypes(state: State) {
export function selectIsQrScanning(state: State) {
return state.matches('waitingForQrScan');
}
export function selectCredentialOfferData(state: State) {
return state.context.credentialOfferData;
}

View File

@@ -1,23 +1,23 @@
import NetInfo from '@react-native-community/netinfo';
import {NativeModules} from 'react-native';
import { NativeModules } from 'react-native';
import Cloud from '../../shared/CloudBackupAndRestoreUtils';
import {CACHED_API} from '../../shared/api';
import getAllConfigurations, { CACHED_API } from '../../shared/api';
import {
fetchKeyPair,
generateKeyPair,
} from '../../shared/cryptoutil/cryptoUtil';
import {
constructIssuerMetaData,
constructProofJWT,
hasKeyPair,
updateCredentialInformation,
verifyCredentialData,
} from '../../shared/openId4VCI/Utils';
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 { displayType, issuerType } from './IssuersMachine';
import { setItem } from '../store';
import { API_CACHED_STORAGE_KEYS } from '../../shared/constants';
import { createCacheObject } from '../../shared/Utils';
import { VerificationResult } from '../../shared/vcjs/verifyCredential';
export const IssuersService = () => {
return {
@@ -30,21 +30,26 @@ export const IssuersService = () => {
},
checkInternet: async () => await NetInfo.fetch(),
downloadIssuerWellknown: async (context: any) => {
const wellknownResponse = await CACHED_API.fetchIssuerWellknownConfig(
context.selectedIssuer.issuer_id,
context.selectedIssuer.credential_issuer_host
? context.selectedIssuer.credential_issuer_host
: context.selectedIssuer.credential_issuer,
const wellknownResponse = (await VciClient.getInstance().getIssuerMetadata(
context.selectedIssuer.credential_issuer_host,
)) as issuerType;
const wellknownCacheObject = createCacheObject(wellknownResponse);
await setItem(
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(
context.selectedIssuer.credential_issuer_host,
),
wellknownCacheObject,
'',
);
return wellknownResponse;
},
getCredentialTypes: async (context: any) => {
const credentialTypes = [];
const credentialTypes: Array<{id: string; [key: string]: any}> = [];
const selectedIssuer = context.selectedIssuer;
const keys =
selectedIssuer.credential_configuration_ids ??
Object.keys(selectedIssuer.credential_configurations_supported);
const keys = Object.keys(
selectedIssuer.credential_configurations_supported,
);
for (const key of keys) {
if (selectedIssuer.credential_configurations_supported[key]) {
@@ -70,26 +75,35 @@ export const IssuersService = () => {
authEndpoint: authorizationEndpoint,
});
};
const getProofJwt = async (accessToken: string, cNonce: string) => {
const getProofJwt = async (
credentialIssuer: string,
cNonce: string | null,
proofSigningAlgosSupported: string[] | null,
) => {
sendBack({
type: 'PROOF_REQUEST',
accessToken: accessToken,
credentialIssuer: credentialIssuer,
cNonce: cNonce,
proofSigningAlgosSupported: proofSigningAlgosSupported,
});
};
const credential =
const getTokenResponse = (tokenRequest: object) => {
sendBack({
type: 'TOKEN_REQUEST',
tokenRequest: tokenRequest,
});
};
const {credential} =
await VciClient.getInstance().requestCredentialFromTrustedIssuer(
constructIssuerMetaData(
context.selectedIssuer,
context.selectedCredentialType,
context.selectedCredentialType.scope,
),
context.selectedIssuer.credential_issuer_host,
context.selectedCredentialType.id,
{
clientId: context.selectedIssuer.client_id,
redirectUri: context.selectedIssuer.redirect_uri,
},
getProofJwt,
navigateToAuthView,
getTokenResponse,
);
return updateCredentialInformation(context, credential);
},
@@ -109,7 +123,7 @@ export const IssuersService = () => {
const {RNSecureKeystoreModule} = NativeModules;
try {
return await RNSecureKeystoreModule.hasAlias(
context.credentialOfferIssuerMetadata.credential_issuer,
context.credentialOfferCredentialIssuer,
);
} catch (error) {
console.error(
@@ -123,8 +137,8 @@ export const IssuersService = () => {
const {RNSecureKeystoreModule} = NativeModules;
try {
await RNSecureKeystoreModule.storeData(
context.credentialOfferIssuerMetadata.credential_issuer,
JSON.stringify(context.credentialOfferIssuerMetadata),
context.credentialOfferCredentialIssuer,
'trusted',
);
} catch {
console.error('Error updating issuer trust in keystore');
@@ -138,39 +152,16 @@ export const IssuersService = () => {
});
};
const getSignedProofJwt = async (
accessToken: string,
credentialIssuer: string,
cNonce: string | null,
issuerMetadata: object,
credentialConfigurationId: string,
proofSigningAlgosSupported: string[] | null,
) => {
let issuer = issuerMetadata as issuerType;
issuer.issuer_id = issuer.credential_issuer;
const wellknownCacheObject = createCacheObject(issuer);
await setItem(
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(issuer.issuer_id),
wellknownCacheObject,
'',
);
let credentialTypes: Array<{id: string; [key: string]: any}> = [];
if (
issuer.credential_configurations_supported[credentialConfigurationId]
) {
credentialTypes.push({
id: credentialConfigurationId,
...issuer.credential_configurations_supported[
credentialConfigurationId
],
});
sendBack({
type: 'PROOF_REQUEST',
accessToken: accessToken,
cNonce: cNonce,
issuerMetadata: issuerMetadata,
issuer: issuer,
credentialTypes: credentialTypes,
});
}
sendBack({
type: 'PROOF_REQUEST',
cNonce: cNonce,
issuer: credentialIssuer,
proofSigningAlgosSupported: proofSigningAlgosSupported,
});
};
const getTxCode = async (
@@ -186,24 +177,55 @@ export const IssuersService = () => {
});
};
const requesTrustIssuerConsent = async (issuerMetadata: object) => {
const issuerMetadataObject = issuerMetadata as issuerType;
const requesTrustIssuerConsent = async (
credentialIssuer: string,
issuerDisplay: object[],
) => {
const issuerDisplayObject = issuerDisplay as displayType[];
sendBack({
type: 'TRUST_ISSUER_CONSENT_REQUEST',
issuerMetadata: issuerMetadataObject,
issuerDisplay: issuerDisplayObject,
issuer: credentialIssuer,
});
};
const getTokenResponse = (tokenRequest: object) => {
sendBack({
type: 'TOKEN_REQUEST',
tokenRequest: tokenRequest,
});
};
const credential = await VciClient.getInstance().requestCredentialByOffer(
context.qrData,
getTxCode,
getSignedProofJwt,
navigateToAuthView,
requesTrustIssuerConsent,
);
return credential;
const credentialResponse =
await VciClient.getInstance().requestCredentialByOffer(
context.qrData,
getTxCode,
getSignedProofJwt,
navigateToAuthView,
getTokenResponse,
requesTrustIssuerConsent,
);
return credentialResponse;
},
sendTokenRequest: async (context: any) => {
const tokenRequestObject = context.tokenRequestObject;
return await sendTokenRequest(
tokenRequestObject,
context.selectedIssuer?.token_endpoint,
);
},
sendTokenResponse: async (context: any) => {
const tokenResponse = context.tokenResponse;
if (!tokenResponse) {
throw new Error(
'Could not send token response, tokenResponse is undefined or null',
);
}
return await VciClient.getInstance().sendTokenResponse(
JSON.stringify(tokenResponse),
);
},
updateCredential: async (context: any) => {
const credential = await updateCredentialInformation(
context,
@@ -211,13 +233,25 @@ export const IssuersService = () => {
);
return credential;
},
cacheIssuerWellknown: async (context: any) => {
const credentialIssuer = context.credentialOfferCredentialIssuer;
const issuerMetadata = (await VciClient.getInstance().getIssuerMetadata(
credentialIssuer,
)) as issuerType;
const wellknownCacheObject = createCacheObject(issuerMetadata);
await setItem(
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(credentialIssuer),
wellknownCacheObject,
'',
);
return issuerMetadata;
},
constructProof: async (context: any) => {
const issuerMeta = context.selectedIssuer;
const proofJWT = await constructProofJWT(
context.publicKey,
context.privateKey,
context.accessToken,
issuerMeta,
context.credentialOfferCredentialIssuer,
null,
context.keyType,
context.wellknownKeyTypes,
true,
@@ -226,13 +260,13 @@ export const IssuersService = () => {
await VciClient.getInstance().sendProof(proofJWT);
return proofJWT;
},
constructProofForTrustedIssuers: async (context: any) => {
constructAndSendProofForTrustedIssuers: async (context: any) => {
const issuerMeta = context.selectedIssuer;
const proofJWT = await constructProofJWT(
context.publicKey,
context.privateKey,
context.accessToken,
issuerMeta,
context.selectedIssuer.credential_issuer_host,
context.selectedIssuer.client_id,
context.keyType,
context.wellknownKeyTypes,
false,
@@ -267,16 +301,82 @@ export const IssuersService = () => {
return context.keyType;
},
verifyCredential: async (context: any) => {
verifyCredential: async (context: any): Promise<VerificationResult> => {
const { isCredentialOfferFlow, verifiableCredential, selectedCredentialType } = context;
if (isCredentialOfferFlow) {
const configurations = await getAllConfigurations();
if (configurations.disableCredentialOfferVcVerification) {
return {
isVerified: true,
verificationMessage: '',
verificationErrorCode: '',
};
}
}
const verificationResult = await verifyCredentialData(
context.verifiableCredential?.credential,
context.selectedCredentialType.format
verifiableCredential?.credential,
selectedCredentialType.format,
);
if (!verificationResult.isVerified) {
throw new Error(verificationResult.verificationErrorCode);
}
return verificationResult;
}
}
}
async function sendTokenRequest(
tokenRequestObject: any,
proxyTokenEndpoint: any = null,
) {
if (proxyTokenEndpoint) {
tokenRequestObject.tokenEndpoint = proxyTokenEndpoint;
}
if (!tokenRequestObject?.tokenEndpoint) {
console.error('tokenEndpoint is not provided in tokenRequestObject');
throw new Error('tokenEndpoint is required');
}
const formBody = new URLSearchParams();
formBody.append('grant_type', tokenRequestObject.grantType);
if (tokenRequestObject.authCode) {
formBody.append('code', tokenRequestObject.authCode);
}
if (tokenRequestObject.preAuthCode) {
formBody.append('pre-authorized_code', tokenRequestObject.preAuthCode);
}
if (tokenRequestObject.txCode) {
formBody.append('tx_code', tokenRequestObject.txCode);
}
if (tokenRequestObject.clientId) {
formBody.append('client_id', tokenRequestObject.clientId);
}
if (tokenRequestObject.redirectUri) {
formBody.append('redirect_uri', tokenRequestObject.redirectUri);
}
if (tokenRequestObject.codeVerifier) {
formBody.append('code_verifier', tokenRequestObject.codeVerifier);
}
const response = await fetch(tokenRequestObject.tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
};
};
body: formBody.toString(),
});
if (!response.ok) {
const errorText = await response.text();
console.error(
'Token request failed with status:',
response.status,
errorText,
);
throw new Error(`Token request failed: ${response.status} ${errorText}`);
}
const tokenResponse = await response.json();
return tokenResponse;
}

View File

@@ -157,7 +157,7 @@ export const activityLogMachine =
setAllWellknownConfigResponse: model.assign({
wellKnownIssuerMap: (_, event) => {
Object.entries(event.response).forEach(([key, value]) => {
event.response[key] = parseJSON(value);
event.response[key] = parseJSON(value).response;
});
return event.response as Record<string, Object>;

View File

@@ -1,120 +1,80 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.app.init.checkKeyPairs:invocation[0]': {
type: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.app.init.generateKeyPairs:invocation[0]': {
type: 'done.invoke.app.init.generateKeyPairs:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.app.ready.focus.active:invocation[0]': {
type: 'done.invoke.app.ready.focus.active:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.app.init.checkKeyPairs:invocation[0]': {
type: 'error.platform.app.init.checkKeyPairs:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
checkFocusState: 'done.invoke.app.ready.focus:invocation[0]';
checkKeyPairs: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
checkNetworkState: 'done.invoke.app.ready.network:invocation[0]';
generateKeyPairsAndStoreOrder: 'done.invoke.app.init.generateKeyPairs:invocation[0]';
getAppInfo: 'done.invoke.app.init.info:invocation[0]';
isQrLoginByDeepLink: 'done.invoke.app.ready.focus.active:invocation[0]';
resetQRLoginDeepLinkData: 'done.invoke.app.ready.focus.active:invocation[1]';
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
forwardToServices: 'ACTIVE' | 'INACTIVE' | 'OFFLINE' | 'ONLINE';
loadCredentialRegistryHostFromStorage: 'READY';
loadCredentialRegistryInConstants: 'STORE_RESPONSE';
loadEsignetHostFromConstants: 'STORE_RESPONSE';
loadEsignetHostFromStorage: 'READY';
logServiceEvents: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
logStoreEvents:
| 'BIOMETRIC_CANCELLED'
| 'KEY_INVALIDATE_ERROR'
| 'RESET_KEY_INVALIDATE_ERROR_DISMISS'
| 'xstate.init';
requestDeviceInfo: 'REQUEST_DEVICE_INFO';
resetKeyInvalidateError: 'READY' | 'RESET_KEY_INVALIDATE_ERROR_DISMISS';
resetLinkCode: 'RESET_LINKCODE';
setAppInfo: 'APP_INFO_RECEIVED';
setIsDecryptError: 'DECRYPT_ERROR';
setIsReadError: 'ERROR';
setLinkCode: 'done.invoke.app.ready.focus.active:invocation[0]';
spawnServiceActors: 'done.invoke.app.init.checkKeyPairs:invocation[0]';
spawnStoreActor:
| 'BIOMETRIC_CANCELLED'
| 'KEY_INVALIDATE_ERROR'
| 'RESET_KEY_INVALIDATE_ERROR_DISMISS'
| 'xstate.init';
unsetIsDecryptError: 'DECRYPT_ERROR_DISMISS' | 'READY';
unsetIsReadError: 'READY';
updateKeyInvalidateError: 'ERROR' | 'KEY_INVALIDATE_ERROR';
};
eventsCausingDelays: {};
eventsCausingGuards: {};
eventsCausingServices: {
checkFocusState: 'APP_INFO_RECEIVED';
checkKeyPairs:
| 'READY'
| 'done.invoke.app.init.generateKeyPairs:invocation[0]';
checkNetworkState: 'APP_INFO_RECEIVED';
generateKeyPairsAndStoreOrder: 'error.platform.app.init.checkKeyPairs:invocation[0]';
getAppInfo: 'STORE_RESPONSE';
isQrLoginByDeepLink: 'ACTIVE';
resetQRLoginDeepLinkData: 'ACTIVE';
};
matchesStates:
| 'init'
| 'init.checkKeyPairs'
| 'init.credentialRegistry'
| 'init.generateKeyPairs'
| 'init.info'
| 'init.services'
| 'init.store'
| 'ready'
| 'ready.focus'
| 'ready.focus.active'
| 'ready.focus.checking'
| 'ready.focus.inactive'
| 'ready.network'
| 'ready.network.checking'
| 'ready.network.offline'
| 'ready.network.online'
| 'waiting'
| {
init?:
| 'checkKeyPairs'
| 'credentialRegistry'
| 'generateKeyPairs'
| 'info'
| 'services'
| 'store';
ready?:
| 'focus'
| 'network'
| {
focus?: 'active' | 'checking' | 'inactive';
network?: 'checking' | 'offline' | 'online';
};
};
tags: never;
}
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"done.invoke.app.init.checkKeyPairs:invocation[0]": { type: "done.invoke.app.init.checkKeyPairs:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.app.init.fetchConfig:invocation[0]": { type: "done.invoke.app.init.fetchConfig:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.app.init.generateKeyPairs:invocation[0]": { type: "done.invoke.app.init.generateKeyPairs:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.app.ready.focus.active:invocation[0]": { type: "done.invoke.app.ready.focus.active:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.app.ready.focus.active:invocation[2]": { type: "done.invoke.app.ready.focus.active:invocation[2]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.app.init.checkKeyPairs:invocation[0]": { type: "error.platform.app.init.checkKeyPairs:invocation[0]"; data: unknown };
"error.platform.app.init.fetchConfig:invocation[0]": { type: "error.platform.app.init.fetchConfig:invocation[0]"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"checkFocusState": "done.invoke.app.ready.focus:invocation[0]";
"checkKeyPairs": "done.invoke.app.init.checkKeyPairs:invocation[0]";
"checkNetworkState": "done.invoke.app.ready.network:invocation[0]";
"fetchAndUpdateCacheTTLFromConfig": "done.invoke.app.init.fetchConfig:invocation[0]";
"generateKeyPairsAndStoreOrder": "done.invoke.app.init.generateKeyPairs:invocation[0]";
"getAppInfo": "done.invoke.app.init.info:invocation[0]";
"getOVPDeepLinkIntent": "done.invoke.app.ready.focus.active:invocation[2]";
"getQrLoginDeepLinkIntent": "done.invoke.app.ready.focus.active:invocation[0]";
"resetOVPDeepLinkIntent": "done.invoke.app.ready.focus.active:invocation[3]";
"resetQrLoginDeepLinkIntent": "done.invoke.app.ready.focus.active:invocation[1]";
};
missingImplementations: {
actions: "forwardToServices";
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
"forwardToServices": "ACTIVE" | "INACTIVE" | "OFFLINE" | "ONLINE";
"loadCredentialRegistryHostFromStorage": "READY";
"loadCredentialRegistryInConstants": "STORE_RESPONSE";
"loadEsignetHostFromConstants": "STORE_RESPONSE";
"loadEsignetHostFromStorage": "READY";
"logServiceEvents": "done.invoke.app.init.checkKeyPairs:invocation[0]";
"logStoreEvents": "BIOMETRIC_CANCELLED" | "KEY_INVALIDATE_ERROR" | "RESET_KEY_INVALIDATE_ERROR_DISMISS" | "xstate.init";
"requestDeviceInfo": "REQUEST_DEVICE_INFO";
"resetAuthorizationRequest": "RESET_AUTHORIZATION_REQUEST";
"resetKeyInvalidateError": "READY" | "RESET_KEY_INVALIDATE_ERROR_DISMISS";
"resetLinkCode": "RESET_LINKCODE";
"setAppInfo": "APP_INFO_RECEIVED";
"setAuthorizationRequest": "done.invoke.app.ready.focus.active:invocation[2]";
"setIsDecryptError": "DECRYPT_ERROR";
"setIsReadError": "ERROR";
"setLinkCode": "done.invoke.app.ready.focus.active:invocation[0]";
"spawnServiceActors": "done.invoke.app.init.checkKeyPairs:invocation[0]";
"spawnStoreActor": "BIOMETRIC_CANCELLED" | "KEY_INVALIDATE_ERROR" | "RESET_KEY_INVALIDATE_ERROR_DISMISS" | "xstate.init";
"unsetIsDecryptError": "DECRYPT_ERROR_DISMISS" | "READY";
"unsetIsReadError": "READY";
"updateKeyInvalidateError": "ERROR" | "KEY_INVALIDATE_ERROR";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
};
eventsCausingServices: {
"checkFocusState": "APP_INFO_RECEIVED";
"checkKeyPairs": "READY" | "done.invoke.app.init.fetchConfig:invocation[0]" | "error.platform.app.init.fetchConfig:invocation[0]";
"checkNetworkState": "APP_INFO_RECEIVED";
"fetchAndUpdateCacheTTLFromConfig": "done.invoke.app.init.generateKeyPairs:invocation[0]";
"generateKeyPairsAndStoreOrder": "error.platform.app.init.checkKeyPairs:invocation[0]";
"getAppInfo": "STORE_RESPONSE";
"getOVPDeepLinkIntent": "ACTIVE";
"getQrLoginDeepLinkIntent": "ACTIVE";
"resetOVPDeepLinkIntent": "ACTIVE";
"resetQrLoginDeepLinkIntent": "ACTIVE";
};
matchesStates: "init" | "init.checkKeyPairs" | "init.credentialRegistry" | "init.fetchConfig" | "init.generateKeyPairs" | "init.info" | "init.services" | "init.store" | "ready" | "ready.focus" | "ready.focus.active" | "ready.focus.checking" | "ready.focus.inactive" | "ready.network" | "ready.network.checking" | "ready.network.offline" | "ready.network.online" | "waiting" | { "init"?: "checkKeyPairs" | "credentialRegistry" | "fetchConfig" | "generateKeyPairs" | "info" | "services" | "store";
"ready"?: "focus" | "network" | { "focus"?: "active" | "checking" | "inactive";
"network"?: "checking" | "offline" | "online"; }; };
tags: never;
}

View File

@@ -1,156 +1,63 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.restore.restoreBackup.checkInternet:invocation[0]': {
type: 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]': {
type: 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]': {
type: 'done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]': {
type: 'done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.restore.restoreBackup.checkInternet:invocation[0]': {
type: 'error.platform.restore.restoreBackup.checkInternet:invocation[0]';
data: unknown;
};
'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]': {
type: 'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]';
data: unknown;
};
'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]': {
type: 'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
checkInternet: 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]';
checkStorageAvailability: 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]';
downloadLatestBackup: 'done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]';
readBackupFile: 'done.invoke.restore.restoreBackup.readBackupFile:invocation[0]';
unzipBackupFile: 'done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]';
};
missingImplementations: {
actions:
| 'cleanupFiles'
| 'downloadUnsyncedBackupFiles'
| 'loadDataToMemory'
| 'refreshVCs'
| 'sendDataRestoreErrorEvent'
| 'sendDataRestoreFailureEvent'
| 'sendDataRestoreStartEvent'
| 'sendDataRestoreSuccessEvent'
| 'setBackupFileName'
| 'setDataFromBackupFile'
| 'setRestoreErrorReason'
| 'setRestoreErrorReasonAsNetworkError'
| 'setRestoreTechnicalError'
| 'setShowRestoreInProgress'
| 'unsetShowRestoreInProgress';
delays: never;
guards:
| 'isInternetConnected'
| 'isMinimumStorageRequiredForBackupRestorationReached';
services:
| 'checkInternet'
| 'checkStorageAvailability'
| 'downloadLatestBackup'
| 'readBackupFile'
| 'unzipBackupFile';
};
eventsCausingActions: {
cleanupFiles:
| 'STORE_ERROR'
| 'STORE_RESPONSE'
| 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]'
| 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]'
| 'error.platform.restore.restoreBackup.checkInternet:invocation[0]'
| 'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]'
| 'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]';
downloadUnsyncedBackupFiles: 'DOWNLOAD_UNSYNCED_BACKUP_FILES';
loadDataToMemory: 'DATA_FROM_FILE';
refreshVCs: 'STORE_RESPONSE';
sendDataRestoreErrorEvent:
| 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]'
| 'error.platform.restore.restoreBackup.checkInternet:invocation[0]';
sendDataRestoreFailureEvent:
| 'STORE_ERROR'
| 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]'
| 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]'
| 'error.platform.restore.restoreBackup.checkInternet:invocation[0]'
| 'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]'
| 'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]';
sendDataRestoreStartEvent: 'BACKUP_RESTORE';
sendDataRestoreSuccessEvent: 'STORE_RESPONSE';
setBackupFileName: 'done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]';
setDataFromBackupFile: 'DATA_FROM_FILE';
setRestoreErrorReason:
| 'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]'
| 'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]';
setRestoreErrorReasonAsNetworkError:
| 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]'
| 'error.platform.restore.restoreBackup.checkInternet:invocation[0]';
setRestoreTechnicalError:
| 'STORE_ERROR'
| 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]';
setShowRestoreInProgress: 'BACKUP_RESTORE';
unsetShowRestoreInProgress:
| 'DISMISS_SHOW_RESTORE_IN_PROGRESS'
| 'STORE_ERROR'
| 'STORE_RESPONSE'
| 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]'
| 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]'
| 'error.platform.restore.restoreBackup.checkInternet:invocation[0]'
| 'error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]'
| 'error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]';
};
eventsCausingDelays: {};
eventsCausingGuards: {
isInternetConnected: 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]';
isMinimumStorageRequiredForBackupRestorationReached: 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]';
};
eventsCausingServices: {
checkInternet: 'BACKUP_RESTORE';
checkStorageAvailability: 'done.invoke.restore.restoreBackup.checkInternet:invocation[0]';
downloadLatestBackup: 'done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]';
readBackupFile: 'done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]';
unzipBackupFile: 'done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]';
};
matchesStates:
| 'init'
| 'restoreBackup'
| 'restoreBackup.checkInternet'
| 'restoreBackup.checkStorageAvailability'
| 'restoreBackup.downloadBackupFileFromCloud'
| 'restoreBackup.failure'
| 'restoreBackup.loadDataToMemory'
| 'restoreBackup.readBackupFile'
| 'restoreBackup.success'
| 'restoreBackup.unzipBackupFile'
| {
restoreBackup?:
| 'checkInternet'
| 'checkStorageAvailability'
| 'downloadBackupFileFromCloud'
| 'failure'
| 'loadDataToMemory'
| 'readBackupFile'
| 'success'
| 'unzipBackupFile';
};
tags: never;
}
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"done.invoke.restore.restoreBackup.checkInternet:invocation[0]": { type: "done.invoke.restore.restoreBackup.checkInternet:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]": { type: "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]": { type: "done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]": { type: "done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.restore.restoreBackup.checkInternet:invocation[0]": { type: "error.platform.restore.restoreBackup.checkInternet:invocation[0]"; data: unknown };
"error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]": { type: "error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]"; data: unknown };
"error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]": { type: "error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"checkInternet": "done.invoke.restore.restoreBackup.checkInternet:invocation[0]";
"checkStorageAvailability": "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]";
"downloadLatestBackup": "done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]";
"readBackupFile": "done.invoke.restore.restoreBackup.readBackupFile:invocation[0]";
"unzipBackupFile": "done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]";
};
missingImplementations: {
actions: "cleanupFiles" | "downloadUnsyncedBackupFiles" | "loadDataToMemory" | "refreshVCs" | "sendDataRestoreErrorEvent" | "sendDataRestoreFailureEvent" | "sendDataRestoreStartEvent" | "sendDataRestoreSuccessEvent" | "setBackupFileName" | "setDataFromBackupFile" | "setRestoreErrorReason" | "setRestoreErrorReasonAsNetworkError" | "setRestoreTechnicalError" | "setShowRestoreInProgress" | "unsetShowRestoreInProgress";
delays: never;
guards: "isInternetConnected" | "isMinimumStorageRequiredForBackupRestorationReached";
services: "checkInternet" | "checkStorageAvailability" | "downloadLatestBackup" | "readBackupFile" | "unzipBackupFile";
};
eventsCausingActions: {
"cleanupFiles": "STORE_ERROR" | "STORE_RESPONSE" | "done.invoke.restore.restoreBackup.checkInternet:invocation[0]" | "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]" | "error.platform.restore.restoreBackup.checkInternet:invocation[0]" | "error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]" | "error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]";
"downloadUnsyncedBackupFiles": "DOWNLOAD_UNSYNCED_BACKUP_FILES";
"loadDataToMemory": "DATA_FROM_FILE";
"refreshVCs": "STORE_RESPONSE";
"sendDataRestoreErrorEvent": "done.invoke.restore.restoreBackup.checkInternet:invocation[0]" | "error.platform.restore.restoreBackup.checkInternet:invocation[0]";
"sendDataRestoreFailureEvent": "STORE_ERROR" | "done.invoke.restore.restoreBackup.checkInternet:invocation[0]" | "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]" | "error.platform.restore.restoreBackup.checkInternet:invocation[0]" | "error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]" | "error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]";
"sendDataRestoreStartEvent": "BACKUP_RESTORE";
"sendDataRestoreSuccessEvent": "STORE_RESPONSE";
"setBackupFileName": "done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]";
"setDataFromBackupFile": "DATA_FROM_FILE";
"setRestoreErrorReason": "error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]" | "error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]";
"setRestoreErrorReasonAsNetworkError": "done.invoke.restore.restoreBackup.checkInternet:invocation[0]" | "error.platform.restore.restoreBackup.checkInternet:invocation[0]";
"setRestoreTechnicalError": "STORE_ERROR" | "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]";
"setShowRestoreInProgress": "BACKUP_RESTORE";
"unsetShowRestoreInProgress": "DISMISS_SHOW_RESTORE_IN_PROGRESS" | "STORE_ERROR" | "STORE_RESPONSE" | "done.invoke.restore.restoreBackup.checkInternet:invocation[0]" | "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]" | "error.platform.restore.restoreBackup.checkInternet:invocation[0]" | "error.platform.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]" | "error.platform.restore.restoreBackup.unzipBackupFile:invocation[0]";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
"isInternetConnected": "done.invoke.restore.restoreBackup.checkInternet:invocation[0]";
"isMinimumStorageRequiredForBackupRestorationReached": "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]";
};
eventsCausingServices: {
"checkInternet": "BACKUP_RESTORE";
"checkStorageAvailability": "done.invoke.restore.restoreBackup.checkInternet:invocation[0]";
"downloadLatestBackup": "done.invoke.restore.restoreBackup.checkStorageAvailability:invocation[0]";
"readBackupFile": "done.invoke.restore.restoreBackup.unzipBackupFile:invocation[0]";
"unzipBackupFile": "done.invoke.restore.restoreBackup.downloadBackupFileFromCloud:invocation[0]";
};
matchesStates: "init" | "restoreBackup" | "restoreBackup.checkInternet" | "restoreBackup.checkStorageAvailability" | "restoreBackup.downloadBackupFileFromCloud" | "restoreBackup.failure" | "restoreBackup.loadDataToMemory" | "restoreBackup.readBackupFile" | "restoreBackup.success" | "restoreBackup.unzipBackupFile" | { "restoreBackup"?: "checkInternet" | "checkStorageAvailability" | "downloadBackupFileFromCloud" | "failure" | "loadDataToMemory" | "readBackupFile" | "success" | "unzipBackupFile"; };
tags: never;
}

View File

@@ -218,7 +218,7 @@ export const openID4VPActions = (model: any) => {
),
shareDeclineStatus: () => {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
@@ -308,7 +308,7 @@ function getVcsMatchingAuthRequest(context, event) {
}
if (Object.keys(matchingVCs).length === 0) {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.NO_MATCHING_VCS,
OVP_ERROR_CODE.NO_MATCHING_VCS,
);

View File

@@ -29,11 +29,10 @@ export const openID4VPServices = () => {
},
getAuthenticationResponse: (context: any) => async () => {
const serviceRes = await OpenID4VP.getInstance().authenticateVerifier(
return await OpenID4VP.authenticateVerifier(
context.urlEncodedAuthorizationRequest,
context.trustedVerifiers,
);
return serviceRes;
},
getKeyPair: async (context: any) => {
@@ -47,12 +46,10 @@ 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 openid.constructUnsignedVPToken(
const unSignedVpTokens = await OpenID4VP.constructUnsignedVPToken(
context.selectedVCs,
holderId,
signatureSuite,
@@ -137,7 +134,9 @@ export const openID4VPServices = () => {
vpTokenSigningResultMap[formatType] = signedData;
}
}
return await openid.shareVerifiablePresentation(vpTokenSigningResultMap);
return await OpenID4VP.shareVerifiablePresentation(
vpTokenSigningResultMap,
);
},
};
};

View File

@@ -1,54 +0,0 @@
// 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.checkFreshInstall:invocation[0]": { type: "done.invoke.store.checkFreshInstall:invocation[0]"; 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: {
"checkFreshInstall": "done.invoke.store.checkFreshInstall:invocation[0]";
"checkStorageInitialisedOrNot": "done.invoke.store.checkStorageInitialisation:invocation[0]";
"clear": "done.invoke.store.resettingStorage:invocation[0]";
"clearKeys": "done.invoke.store.clearIosKeys:invocation[0]";
"generateEncryptionKey": "done.invoke.store.generatingEncryptionKey:invocation[0]";
"getEncryptionKey": "done.invoke.store.gettingEncryptionKey:invocation[0]";
"hasEncryptionKey": "done.invoke.store.checkEncryptionKey:invocation[0]";
"store": "done.invoke._store";
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
"forwardStoreRequest": "APPEND" | "CLEAR" | "EXPORT" | "FETCH_ALL_WELLKNOWN_CONFIG" | "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: {
};
eventsCausingGuards: {
"hasData": "done.invoke.store.checkFreshInstall:invocation[0]";
"isCustomSecureKeystore": "KEY_RECEIVED";
};
eventsCausingServices: {
"checkFreshInstall": "BIOMETRIC_CANCELLED" | "xstate.init";
"checkStorageInitialisedOrNot": "ERROR";
"clear": "KEY_RECEIVED";
"clearKeys": "done.invoke.store.checkFreshInstall:invocation[0]";
"generateEncryptionKey": "ERROR" | "IGNORE" | "READY";
"getEncryptionKey": "TRY_AGAIN";
"hasEncryptionKey": never;
"store": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
};
matchesStates: "checkEncryptionKey" | "checkFreshInstall" | "checkStorageInitialisation" | "clearIosKeys" | "failedReadingKey" | "generatingEncryptionKey" | "gettingEncryptionKey" | "ready" | "resettingStorage";
tags: never;
}

View File

@@ -12,17 +12,14 @@ import {
selectSelectedIssuer,
selectSelectingCredentialType,
selectStoring,
selectVerificationErrorMessage,
selectIsNonGenericError,
selectIsQrScanning,
selectCredentialOfferData,
selectVerificationErrorMessage, selectIsQrScanning,
selectAuthWebViewStatus,
selectAuthEndPoint,
selectIsTxCodeRequested,
selectIsConsentRequested,
selectIssuerLogo,
selectIssuerName,
selectTxCodeDisplayDetails,
selectTxCodeDisplayDetails
} from '../../machines/Issuers/IssuersSelectors';
import { ActorRefFrom } from 'xstate';
import { BOTTOM_TAB_ROUTES } from '../../routes/routesConstants';
@@ -40,10 +37,10 @@ export function useIssuerScreenController({ route, navigation }) {
return {
issuers: useSelector(service, selectIssuers),
issuerLogo: useSelector(service,selectIssuerLogo),
issuerName: useSelector(service,selectIssuerName),
isTxCodeRequested: useSelector(service,selectIsTxCodeRequested),
txCodeDisplayDetails: useSelector(service,selectTxCodeDisplayDetails),
issuerLogo: useSelector(service, selectIssuerLogo),
issuerName: useSelector(service, selectIssuerName),
isTxCodeRequested: useSelector(service, selectIsTxCodeRequested),
txCodeDisplayDetails: useSelector(service, selectTxCodeDisplayDetails),
authEndpount: useSelector(service, selectAuthEndPoint),
selectedIssuer: useSelector(service, selectSelectedIssuer),
errorMessageType: useSelector(service, selectErrorMessageType),
@@ -51,7 +48,6 @@ export function useIssuerScreenController({ route, navigation }) {
isBiometricsCancelled: useSelector(service, selectIsBiometricCancelled),
isDone: useSelector(service, selectIsDone),
isIdle: useSelector(service, selectIsIdle),
isNonGenericError: useSelector(service, selectIsNonGenericError),
loadingReason: useSelector(service, selectLoadingReason),
isStoring: useSelector(service, selectStoring),
isQrScanning: useSelector(service, selectIsQrScanning),
@@ -61,9 +57,8 @@ export function useIssuerScreenController({ route, navigation }) {
selectSelectingCredentialType,
),
isConsentRequested: useSelector(
service,selectIsConsentRequested
service, selectIsConsentRequested
),
credentialOfferData: useSelector(service, selectCredentialOfferData),
supportedCredentialTypes: useSelector(
service,
selectSupportedCredentialTypes,

View File

@@ -38,6 +38,7 @@ import { IssuersModel } from '../../machines/Issuers/IssuersModel';
import { AUTH_ROUTES } from '../../routes/routesConstants';
import { TransactionCodeModal } from './TransactionCodeScreen';
import { TrustIssuerModal } from './TrustIssuerModal';
import i18next from 'i18next';
export const IssuersScreen: React.FC<
HomeRouteProps | RootRouteProps
> = props => {
@@ -49,7 +50,7 @@ export const IssuersScreen: React.FC<
const [search, setSearch] = useState('');
const [tapToSearch, setTapToSearch] = useState(false);
const [clearSearchIcon, setClearSearchIcon] = useState(false);
const showFullScreenError = controller.isError && controller.errorMessageType;
const showFullScreenError = controller.isError
const isVerificationFailed = controller.verificationErrorMessage !== '';
@@ -57,7 +58,8 @@ export const IssuersScreen: React.FC<
const verificationErrorMessage = isTranslationKeyFound(translationKey, t)
? t(translationKey)
: t(`errors.verificationFailed.ERR_GENERIC`);
: t('errors.verificationFailed.ERR_GENERIC');
useLayoutEffect(() => {
if (controller.loadingReason || showFullScreenError) {
@@ -124,7 +126,8 @@ export const IssuersScreen: React.FC<
controller.errorMessageType ===
ErrorMessage.CREDENTIAL_TYPE_DOWNLOAD_FAILURE ||
controller.errorMessageType ===
ErrorMessage.AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED
ErrorMessage.AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED ||
controller.errorMessageType === ErrorMessage.NETWORK_REQUEST_FAILED
);
}

View File

@@ -113,7 +113,7 @@ export const TransactionCodeModal: React.FC<ExtendedModalProps> = props => {
}`,
)}
</Text>
{textLineCount >= maxLines && <Text
{textLineCount > maxLines && <Text
onPress={() => setShowFullDescription(prev => !prev)}
style={Theme.TransactionCodeScreenStyle.showMoreButton}
>

View File

@@ -29,16 +29,27 @@ import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
import {VCShareFlowType} from '../../shared/Utils';
import {APP_EVENTS} from '../../machines/app';
import {GlobalContext} from '../../shared/GlobalContext';
import {useOvpErrorModal} from '../../shared/hooks/useOvpErrorModal';
export const ScanScreen: React.FC = () => {
const {t} = useTranslation('ScanScreen');
const scanScreenController = useScanScreen();
const sendVcScreenController = useSendVcScreen();
const sendVPScreenController = useSendVPScreen();
const [errorModal] = useOvpErrorModal({
error: sendVPScreenController.error,
noCredentialsMatchingVPRequest:
sendVPScreenController.noCredentialsMatchingVPRequest,
requestedClaimsByVerifier: sendVPScreenController.requestedClaimsByVerifier,
getAdditionalMessage: sendVPScreenController.getAdditionalMessage,
generateAndStoreLogMessage:
sendVPScreenController.generateAndStoreLogMessage,
t,
});
const [isBluetoothOn, setIsBluetoothOn] = useState(false);
const showErrorModal =
sendVPScreenController.scanScreenError ||
(sendVPScreenController.errorModal.show &&
(errorModal.show &&
(sendVPScreenController.flowType ===
VCShareFlowType.MINI_VIEW_SHARE_OPENID4VP ||
sendVPScreenController.flowType ===
@@ -277,7 +288,7 @@ export const ScanScreen: React.FC = () => {
const getPrimaryButtonText = () => {
if (
sendVPScreenController.errorModal.showRetryButton &&
errorModal.showRetryButton &&
sendVPScreenController.openID4VPRetryCount < 3
) {
return t('ScanScreen:status.retry');
@@ -366,8 +377,8 @@ export const ScanScreen: React.FC = () => {
alignActionsOnEnd
showClose={false}
isVisible={showErrorModal}
title={sendVPScreenController.errorModal.title}
message={sendVPScreenController.errorModal.message}
title={errorModal.title}
message={errorModal.message}
image={SvgImage.PermissionDenied()}
primaryButtonTestID={'retry'}
primaryButtonText={getPrimaryButtonText()}

View File

@@ -32,22 +32,32 @@ import OpenID4VP from '../../shared/openID4VP/OpenID4VP';
import {GlobalContext} from '../../shared/GlobalContext';
import {APP_EVENTS} from '../../machines/app';
import {useScanScreen} from './ScanScreenController';
import {useOvpErrorModal} from '../../shared/hooks/useOvpErrorModal';
export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
const {t} = useTranslation('SendVPScreen');
const controller = useSendVPScreen();
const scanScreenController = useScanScreen();
const [errorModal, resetErrorModal] = useOvpErrorModal({
error: controller.error,
noCredentialsMatchingVPRequest: controller.noCredentialsMatchingVPRequest,
requestedClaimsByVerifier: controller.requestedClaimsByVerifier,
getAdditionalMessage: controller.getAdditionalMessage,
generateAndStoreLogMessage: controller.generateAndStoreLogMessage,
t,
});
const vcsMatchingAuthRequest = controller.vcsMatchingAuthRequest;
const {appService} = useContext(GlobalContext);
const [triggerExitFlow, setTriggerExitFlow] = useState(false);
useEffect(() => {
if (controller.errorModal.show && controller.isOVPViaDeepLink) {
if (errorModal.show && controller.isOVPViaDeepLink) {
const timeout = setTimeout(
() => {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.NO_MATCHING_VCS,
OVP_ERROR_CODE.NO_MATCHING_VCS,
);
@@ -58,11 +68,11 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
return () => clearTimeout(timeout);
}
}, [controller.errorModal.show, controller.isOVPViaDeepLink]);
}, [errorModal.show, controller.isOVPViaDeepLink]);
useEffect(() => {
if (triggerExitFlow) {
controller.RESET_LOGGED_ERROR();
RESET_LOGGED_ERROR();
controller.GO_TO_HOME();
controller.RESET_RETRY_COUNT();
appService.send(APP_EVENTS.RESET_AUTHORIZATION_REQUEST());
@@ -106,11 +116,16 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
}
});
const RESET_LOGGED_ERROR = () => {
resetErrorModal();
};
const handleDismiss = () => {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
controller.generateAndStoreLogMessage('USER_DECLINED_CONSENT');
if (controller.isOVPViaDeepLink) {
controller.GO_TO_HOME();
BackHandler.exitApp();
@@ -120,10 +135,11 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
};
const handleRejectButtonEvent = () => {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
controller.generateAndStoreLogMessage('USER_DECLINED_CONSENT');
if (controller.isOVPViaDeepLink) {
controller.GO_TO_HOME();
BackHandler.exitApp();
@@ -135,12 +151,9 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
const getAdditionalMessage = () => {
if (
controller.isOVPViaDeepLink &&
!(
controller.errorModal.showRetryButton &&
controller.openID4VPRetryCount < 3
)
!(errorModal.showRetryButton && controller.openID4VPRetryCount < 3)
) {
return controller.errorModal.additionalMessage;
return errorModal.additionalMessage;
}
return undefined;
};
@@ -209,7 +222,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
const getPrimaryButtonEvent = () => {
if (controller.showConfirmationPopup && controller.isOVPViaDeepLink) {
return () => {
OpenID4VP.getInstance().sendErrorToVerifier(
OpenID4VP.sendErrorToVerifier(
OVP_ERROR_MESSAGES.DECLINED,
OVP_ERROR_CODE.DECLINED,
);
@@ -221,8 +234,7 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
};
const getPrimaryButtonText = () => {
return controller.errorModal.showRetryButton &&
controller.openID4VPRetryCount < 3
return errorModal.showRetryButton && controller.openID4VPRetryCount < 3
? t('ScanScreen:status.retry')
: undefined;
};
@@ -417,14 +429,14 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
/>
</>
)}
{controller.errorModal.show && (
{errorModal.show && (
<Error
isModal
alignActionsOnEnd
showClose={false}
isVisible={controller.errorModal.show}
title={controller.errorModal.title}
message={controller.errorModal.message}
isVisible={errorModal.show}
title={errorModal.title}
message={errorModal.message}
additionalMessage={getAdditionalMessage()}
image={SvgImage.PermissionDenied()}
primaryButtonTestID={'retry'}

View File

@@ -1,6 +1,6 @@
import {NavigationProp, useNavigation} from '@react-navigation/native';
import {useSelector} from '@xstate/react';
import {useContext, useEffect, useRef, useState} from 'react';
import {useCallback, useContext, useRef, useState} from 'react';
import {useTranslation} from 'react-i18next';
import {ActorRefFrom} from 'xstate';
import {Theme} from '../../components/ui/styleUtils';
@@ -151,208 +151,30 @@ export function useSendVPScreen() {
selectIsOVPViaDeeplink,
);
const getAdditionalMessage = () => {
const getAdditionalMessage = useCallback(() => {
return isOVPViaDeepLink && isIOS() ? t('errors.additionalMessage') : '';
};
}, [isOVPViaDeepLink, t]);
const generateAndStoreLogMessage = useCallback(
(logType: string, errorInfo?: string) => {
activityLogService.send(
ActivityLogEvents.LOG_ACTIVITY(
VPShareActivityLog.getLogFromObject({
timestamp: Date.now(),
type: logType,
info: errorInfo,
}),
),
);
},
[activityLogService],
);
function generateAndStoreLogMessage(logType: string, errorInfo?: string) {
activityLogService.send(
ActivityLogEvents.LOG_ACTIVITY(
VPShareActivityLog.getLogFromObject({
timestamp: Date.now(),
type: logType,
info: errorInfo,
}),
),
);
}
const requestedClaimsByVerifier = useSelector(
openID4VPService,
selectRequestedClaimsByVerifier,
);
const [errorModal, setErrorModalData] = useState({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
const isClaimsEmpty =
!requestedClaimsByVerifier || requestedClaimsByVerifier.trim() === '';
if (noCredentialsMatchingVPRequest) {
errorModal.title = isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.title')
: t('errors.noMatchingCredentials.title');
errorModal.message = isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.message')
: t('errors.noMatchingCredentials.message', { claims: requestedClaimsByVerifier });
generateAndStoreLogMessage(
'NO_CREDENTIAL_MATCHING_REQUEST',
requestedClaimsByVerifier,
);
} else if (
error.includes('Verifier authentication was unsuccessful') ||
error.startsWith('api error')
) {
errorModal.title = t('errors.invalidVerifier.title');
errorModal.message = t('errors.invalidVerifier.message');
generateAndStoreLogMessage('VERIFIER_AUTHENTICATION_FAILED');
} else if (error.includes('credential mismatch detected')) {
errorModal.title = t('errors.credentialsMismatch.title');
errorModal.message = t('errors.credentialsMismatch.message', {
claims: requestedClaimsByVerifier,
});
generateAndStoreLogMessage(
'CREDENTIAL_MISMATCH_FROM_KEBAB',
requestedClaimsByVerifier,
);
} else if (error.includes('none of the selected VC has image')) {
errorModal.title = t('errors.noImage.title');
errorModal.message = t('errors.noImage.message');
generateAndStoreLogMessage('NO_SELECTED_VC_HAS_IMAGE');
} else if (error.includes('invalid_request_uri_method')) {
errorModal.title = t('errors.invalidRequestURI.title');
errorModal.message = t('errors.invalidRequestURI.message');
generateAndStoreLogMessage('INVALID_REQUEST_URI_METHOD');
} else if (error.includes('invalid_request')) {
errorModal.title = t('errors.invalidQrCode.title');
errorModal.message = t('errors.invalidQrCode.message');
generateAndStoreLogMessage('INVALID_AUTH_REQUEST');
} else if (error.includes('vp_formats_not_supported')) {
errorModal.title = t('errors.vpFormatsNotSupported.title');
errorModal.message = t('errors.vpFormatsNotSupported.message');
generateAndStoreLogMessage('VP_FORMATS_NOT_SUPPORTED');
} else if (error.includes('invalid_presentation_definition_uri')) {
errorModal.title = t('errors.invalidPresentationDefinitionURI.title');
errorModal.message = t('errors.invalidPresentationDefinitionURI.message');
generateAndStoreLogMessage('INVALID_PRESENTATION_DEFINITION_URI');
} else if (error.includes('invalid_presentation_definition_reference')) {
errorModal.title = t('errors.invalidPresentationDefinitionRef.title');
errorModal.message = t('errors.invalidPresentationDefinitionRef.message');
generateAndStoreLogMessage('INVALID_PRESENTATION_DEFINITION_REFERENCE');
} else if (error.startsWith('send vp')) {
errorModal.title = t('errors.sendVPError.title');
errorModal.message = t('errors.sendVPError.message');
errorModal.showRetryButton = true;
} else if (error !== '') {
errorModal.title = t('errors.genericError.title');
errorModal.message = t('errors.genericError.message');
generateAndStoreLogMessage('TECHNICAL_ERROR');
}
useEffect(() => {
if (noCredentialsMatchingVPRequest && !hasLoggedErrorRef.current) {
const isClaimsEmpty =
!requestedClaimsByVerifier || requestedClaimsByVerifier.trim() === '';
setErrorModalData({
show: true,
title: isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.title')
: t('errors.noMatchingCredentials.title'),
message: isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.message')
: t('errors.noMatchingCredentials.message', {
claims: requestedClaimsByVerifier,
}),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage(
'NO_CREDENTIAL_MATCHING_REQUEST',
requestedClaimsByVerifier,
);
hasLoggedErrorRef.current = true;
} else if (
(error.includes('Verifier authentication was unsuccessful') ||
error.startsWith('api error')) &&
!hasLoggedErrorRef.current
) {
setErrorModalData({
show: true,
title: t('errors.invalidVerifier.title'),
message: t('errors.invalidVerifier.message'),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage('VERIFIER_AUTHENTICATION_FAILED');
hasLoggedErrorRef.current = true;
} else if (
error.includes('credential mismatch detected') &&
!hasLoggedErrorRef.current
) {
setErrorModalData({
show: true,
title: t('errors.credentialsMismatch.title'),
message: t('errors.credentialsMismatch.message', {
claims: requestedClaimsByVerifier,
}),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage(
'CREDENTIAL_MISMATCH_FROM_KEBAB',
requestedClaimsByVerifier,
);
hasLoggedErrorRef.current = true;
} else if (
error.includes('none of the selected VC has image') &&
!hasLoggedErrorRef.current
) {
setErrorModalData({
show: true,
title: t('errors.noImage.title'),
message: t('errors.noImage.message'),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage('NO_SELECTED_VC_HAS_IMAGE');
hasLoggedErrorRef.current = true;
} else if (
error.startsWith('vc validation') &&
!hasLoggedErrorRef.current
) {
setErrorModalData({
show: true,
title: t('errors.invalidQrCode.title'),
message: t('errors.invalidQrCode.message'),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage('INVALID_AUTH_REQUEST');
hasLoggedErrorRef.current = true;
} else if (error.startsWith('send vp') && !hasLoggedErrorRef.current) {
setErrorModalData({
show: true,
title: t('errors.genericError.title'),
message: t('errors.genericError.message'),
additionalMessage: getAdditionalMessage(),
showRetryButton: true,
});
hasLoggedErrorRef.current = true;
} else if (error !== '' && !hasLoggedErrorRef.current) {
setErrorModalData({
show: true,
title: t('errors.genericError.title'),
message: t('errors.genericError.message'),
additionalMessage: getAdditionalMessage(),
showRetryButton: false,
});
generateAndStoreLogMessage('TECHNICAL_ERROR');
hasLoggedErrorRef.current = true;
} else if (error === '') {
setErrorModalData({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
hasLoggedErrorRef.current = false;
}
}, [error, noCredentialsMatchingVPRequest]);
let overlayDetails: Omit<VPShareOverlayProps, 'isVisible'> | null = null;
let vpVerifierName = useSelector(
openID4VPService,
@@ -401,18 +223,12 @@ export function useSendVPScreen() {
checkIfAnyVCHasImage,
checkIfAllVCsHasImage,
getSelectedVCs,
errorModal,
error,
noCredentialsMatchingVPRequest,
requestedClaimsByVerifier,
getAdditionalMessage,
overlayDetails,
RESET_LOGGED_ERROR: () => {
hasLoggedErrorRef.current = false;
setErrorModalData({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
},
generateAndStoreLogMessage,
scanScreenError: useSelector(scanService, selectIsSendingVPError),
vcsMatchingAuthRequest,
userSelectedVCs: useSelector(openID4VPService, selectSelectedVCs),

View File

@@ -19,5 +19,6 @@ export const INITIAL_CONFIG = {
faceSdkModelUrl: '',
openId4VCIDownloadVCTimeout: '30000',
cacheTTLInMilliSeconds: '3600000',
disableCredentialOfferVcVerification: false
},
};

View File

@@ -129,7 +129,7 @@ export function parseMetadatas(metadataStrings: object[]) {
}
export const getVCMetadata = (context: object, keyType: string) => {
const issuer = context.selectedIssuer.credential_issuer;
const issuer = context.selectedIssuer.credential_issuer_host ?? context.selectedIssuer.credential_issuer;
const credentialId = `${UUID.generate()}_${issuer}`;
return VCMetadata.fromVC({
@@ -147,7 +147,7 @@ export const getVCMetadata = (context: object, keyType: string) => {
format: context['credentialWrapper'].format,
downloadKeyType: keyType,
credentialType: getCredentialType(context.selectedCredentialType),
issuerHost: context.selectedIssuer.credential_issuer_host ?? context.selectedIssuer.credential_issuer,
issuerHost: issuer
});
};

View File

@@ -155,6 +155,7 @@ export type IndividualId = {
export const TECHNICAL_ERROR = 'Technical error';
export const NETWORK_REQUEST_FAILED = 'Network request failed';
export const NO_INTERNET = 'No internet connection';
export const IOS_SIGNIN_FAILED = 'iCloud not available';
export const REQUEST_TIMEOUT = 'request timeout';
export const BIOMETRIC_CANCELLED = 'User has cancelled biometric';

View File

@@ -0,0 +1,193 @@
import {useEffect, useState} from 'react';
interface OvpErrorModal {
show: boolean;
title: string;
message: string;
additionalMessage: string;
showRetryButton: boolean;
}
interface UseOvpErrorModalProps {
error: string;
noCredentialsMatchingVPRequest: boolean;
requestedClaimsByVerifier: string;
getAdditionalMessage: () => string;
generateAndStoreLogMessage: (logType: string, errorInfo?: string) => void;
t: (key: string, options?: any) => string;
}
export function useOvpErrorModal({
error,
noCredentialsMatchingVPRequest,
requestedClaimsByVerifier,
getAdditionalMessage,
generateAndStoreLogMessage,
t,
}: UseOvpErrorModalProps): [OvpErrorModal, () => void] {
const [errorModal, setErrorModal] = useState<OvpErrorModal>({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
useEffect(() => {
const isClaimsEmpty =
!requestedClaimsByVerifier || requestedClaimsByVerifier.trim() === '';
const additionalMessage = getAdditionalMessage();
if (noCredentialsMatchingVPRequest) {
setErrorModal({
show: true,
title: isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.title')
: t('errors.noMatchingCredentials.title'),
message: isClaimsEmpty
? t('errors.noMatchingCredentialsWithMissingClaims.message')
: t('errors.noMatchingCredentials.message', {
claims: requestedClaimsByVerifier,
}),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage(
'NO_CREDENTIAL_MATCHING_REQUEST',
requestedClaimsByVerifier,
);
} else if (
error.includes('Verifier authentication was unsuccessful') ||
error.startsWith('api error')
) {
setErrorModal({
show: true,
title: t('errors.invalidVerifier.title'),
message: t('errors.invalidVerifier.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('VERIFIER_AUTHENTICATION_FAILED');
} else if (error.includes('credential mismatch detected')) {
setErrorModal({
show: true,
title: t('errors.credentialsMismatch.title'),
message: t('errors.credentialsMismatch.message', {
claims: requestedClaimsByVerifier,
}),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage(
'CREDENTIAL_MISMATCH_FROM_KEBAB',
requestedClaimsByVerifier,
);
} else if (error.includes('none of the selected VC has image')) {
setErrorModal({
show: true,
title: t('errors.noImage.title'),
message: t('errors.noImage.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('NO_SELECTED_VC_HAS_IMAGE');
} else if (error.includes('invalid_request_uri_method')) {
setErrorModal({
show: true,
title: t('errors.invalidRequestURI.title'),
message: t('errors.invalidRequestURI.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('INVALID_REQUEST_URI_METHOD');
} else if (error.includes('invalid_request')) {
setErrorModal({
show: true,
title: t('errors.invalidQrCode.title'),
message: t('errors.invalidQrCode.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('INVALID_AUTH_REQUEST');
} else if (error.includes('vp_formats_not_supported')) {
setErrorModal({
show: true,
title: t('errors.vpFormatsNotSupported.title'),
message: t('errors.vpFormatsNotSupported.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('REQUEST_COULD_NOT_BE_PROCESSED');
} else if (error.includes('invalid_presentation_definition_uri')) {
setErrorModal({
show: true,
title: t('errors.invalidPresentationDefinitionURI.title'),
message: t('errors.invalidPresentationDefinitionURI.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('INVALID_PRESENTATION_DEFINITION_URI');
} else if (error.includes('invalid_presentation_definition_reference')) {
setErrorModal({
show: true,
title: t('errors.invalidPresentationDefinitionRef.title'),
message: t('errors.invalidPresentationDefinitionRef.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('REQUEST_COULD_NOT_BE_PROCESSED');
} else if (error.includes('invalid_client')) {
setErrorModal({
show: true,
title: t('errors.invalidQrCode.title'),
message: t('errors.invalidQrCode.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('REQUEST_COULD_NOT_BE_PROCESSED');
} else if (error.startsWith('send vp')) {
setErrorModal({
show: true,
title: t('errors.sendVPError.title'),
message: t('errors.sendVPError.message'),
additionalMessage,
showRetryButton: true,
});
generateAndStoreLogMessage('SEND_VP_ERROR');
} else if (error !== '') {
setErrorModal({
show: true,
title: t('errors.genericError.title'),
message: t('errors.genericError.message'),
additionalMessage,
showRetryButton: false,
});
generateAndStoreLogMessage('TECHNICAL_ERROR');
} else {
setErrorModal({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
}
}, [
error,
noCredentialsMatchingVPRequest,
requestedClaimsByVerifier,
getAdditionalMessage,
t,
]);
const resetErrorModal = () => {
setErrorModal({
show: false,
title: '',
message: '',
additionalMessage: '',
showRetryButton: false,
});
};
return [errorModal, resetErrorModal];
}

View File

@@ -4,7 +4,7 @@ import {
SelectedCredentialsForVPSharing,
VC,
} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
import {walletMetadata} from './walletMetadata';
import {fallbackWalletMetadata} from './fallbackWalletMetadata';
import {getWalletMetadata, isClientValidationRequired} from './OpenID4VPHelper';
import {parseJSON} from '../Utils';
@@ -14,58 +14,66 @@ class OpenID4VP {
private static instance: OpenID4VP;
private InjiOpenID4VP = NativeModules.InjiOpenID4VP;
private constructor() {
this.InjiOpenID4VP.init(__AppId.getValue());
private constructor(walletMetadata: any) {
this.InjiOpenID4VP.initSdk(__AppId.getValue(), walletMetadata);
}
public static getInstance(): OpenID4VP {
private static async getInstance(): Promise<OpenID4VP> {
if (!OpenID4VP.instance) {
OpenID4VP.instance = new OpenID4VP();
const walletMetadata =
(await getWalletMetadata()) || fallbackWalletMetadata;
OpenID4VP.instance = new OpenID4VP(walletMetadata);
}
return OpenID4VP.instance;
}
async authenticateVerifier(
static async authenticateVerifier(
urlEncodedAuthorizationRequest: string,
trustedVerifiersList: any,
) {
const shouldValidateClient = await isClientValidationRequired();
const metadata = (await getWalletMetadata()) || walletMetadata;
const openID4VP = await OpenID4VP.getInstance();
const authenticationResponse =
await this.InjiOpenID4VP.authenticateVerifier(
await openID4VP.InjiOpenID4VP.authenticateVerifier(
urlEncodedAuthorizationRequest,
trustedVerifiersList,
metadata,
shouldValidateClient,
);
return JSON.parse(authenticationResponse);
}
async constructUnsignedVPToken(
static async constructUnsignedVPToken(
selectedVCs: Record<string, VC[]>,
holderId: string,
signatureAlgorithm: string,
) {
const updatedSelectedVCs = this.processSelectedVCs(selectedVCs);
const unSignedVpTokens = await this.InjiOpenID4VP.constructUnsignedVPToken(
updatedSelectedVCs,
holderId,
signatureAlgorithm,
);
const openID4VP = await OpenID4VP.getInstance();
const updatedSelectedVCs = openID4VP.processSelectedVCs(selectedVCs);
const unSignedVpTokens =
await openID4VP.InjiOpenID4VP.constructUnsignedVPToken(
updatedSelectedVCs,
holderId,
signatureAlgorithm,
);
return parseJSON(unSignedVpTokens);
}
async shareVerifiablePresentation(
static async shareVerifiablePresentation(
vpTokenSigningResultMap: Record<string, any>,
) {
return await this.InjiOpenID4VP.shareVerifiablePresentation(
const openID4VP = await OpenID4VP.getInstance();
return await openID4VP.InjiOpenID4VP.shareVerifiablePresentation(
vpTokenSigningResultMap,
);
}
sendErrorToVerifier(errorMessage: string, errorCode: string) {
this.InjiOpenID4VP.sendErrorToVerifier(errorMessage, errorCode);
static sendErrorToVerifier(errorMessage: string, errorCode: string) {
OpenID4VP.getInstance().then(openID4VP => {
openID4VP.InjiOpenID4VP.sendErrorToVerifier(errorMessage, errorCode);
});
}
private processSelectedVCs(selectedVCs: Record<string, VC[]>) {

View File

@@ -1,5 +1,5 @@
export const walletMetadata = {
presentation_definition_uri_supported: true,
export const fallbackWalletMetadata = {
presentation_definition_uri_supported: false,
vp_formats_supported: {
ldp_vc: {
alg_values_supported: [

View File

@@ -1,6 +1,5 @@
import base64url from 'base64url';
import i18next from 'i18next';
import jwtDecode from 'jwt-decode';
import jose from 'node-jose';
import {NativeModules} from 'react-native';
import {vcVerificationBannerDetails} from '../../components/BannerNotificationContainer';
@@ -10,11 +9,9 @@ import {
DETAIL_VIEW_ADD_ON_FIELDS,
getCredentialTypeFromWellKnown,
} from '../../components/VC/common/VCUtils';
import i18n from '../../i18n';
import {displayType, issuerType} from '../../machines/Issuers/IssuersMachine';
import {displayType} from '../../machines/Issuers/IssuersMachine';
import {
Credential,
CredentialTypes,
CredentialWrapper,
VerifiableCredential,
} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
@@ -22,16 +19,11 @@ import getAllConfigurations, {CACHED_API} from '../api';
import {
ED25519_PROOF_SIGNING_ALGO,
isAndroid,
isIOS,
JWT_ALG_TO_KEY_TYPE,
KEY_TYPE_TO_JWT_ALG,
} from '../constants';
import {getJWT} from '../cryptoutil/cryptoUtil';
import {
VerificationErrorMessage,
VerificationErrorType,
verifyCredential,
} from '../vcjs/verifyCredential';
import {verifyCredential} from '../vcjs/verifyCredential';
import {getVerifiableCredential} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors';
import {getErrorEventData, sendErrorEvent} from '../telemetry/TelemetryUtils';
import {TelemetryConstants} from '../telemetry/TelemetryConstants';
@@ -268,22 +260,21 @@ export enum ErrorMessage {
TECHNICAL_DIFFICULTIES = 'technicalDifficulty',
CREDENTIAL_TYPE_DOWNLOAD_FAILURE = 'credentialTypeListDownloadFailure',
AUTHORIZATION_GRANT_TYPE_NOT_SUPPORTED = 'authorizationGrantTypeNotSupportedByWallet',
NETWORK_REQUEST_FAILED = 'networkRequestFailed',
}
export async function constructProofJWT(
publicKey: any,
privateKey: any,
accessToken: string,
selectedIssuer: issuerType,
selectedIssuer: string,
client_id: string | null,
keyType: string,
proofSigningAlgosSupported: string[] = [],
isCredentialOfferFlow: boolean,
cNonce?: string,
): Promise<string> {
const jwk = await getJWK(publicKey, keyType);
const decodedToken = jwtDecode(accessToken);
const nonce = cNonce ?? decodedToken?.c_nonce;
const nonce = cNonce;
const alg =
keyType === KeyTypes.ED25519
? resolveEd25519Alg(proofSigningAlgosSupported)
@@ -302,9 +293,9 @@ export async function constructProofJWT(
};
const jwtPayload = {
iss: selectedIssuer.client_id,
...(client_id ? {iss: client_id} : {}),
nonce,
aud: selectedIssuer.credential_audience ?? selectedIssuer.credential_issuer,
aud: selectedIssuer,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 18000,
};
@@ -410,38 +401,9 @@ export function selectCredentialRequestKey(
return keyType;
}
}
return KeyTypes.ED25519;
return keyOrder[0];
}
export const constructIssuerMetaData = (
selectedIssuer: issuerType,
selectedCredentialType: CredentialTypes,
scope: string,
): Object => {
const issuerMeta: Object = {
credentialAudience: selectedIssuer.credential_audience,
credentialEndpoint: selectedIssuer.credential_endpoint,
credentialFormat: isIOS()
? selectedCredentialType.format
: selectedCredentialType.format.toUpperCase(),
authorizationServers: selectedIssuer['authorization_servers'],
tokenEndpoint: selectedIssuer.token_endpoint,
scope: scope,
};
if (selectedCredentialType.format === VCFormat.ldp_vc) {
issuerMeta['credentialType'] = selectedCredentialType?.credential_definition
?.type ?? ['VerifiableCredential'];
if (selectedCredentialType?.credential_definition['@context'])
issuerMeta['context'] =
selectedCredentialType?.credential_definition['@context'];
} else if (selectedCredentialType.format === VCFormat.mso_mdoc) {
issuerMeta['doctype'] = selectedCredentialType.doctype;
issuerMeta['claims'] = selectedCredentialType.claims;
}
return issuerMeta;
};
export function getMatchingCredentialIssuerMetadata(
wellknown: any,
credentialConfigurationId: string,
@@ -470,11 +432,11 @@ export async function verifyCredentialData(
credential: Credential,
credentialFormat: string,
) {
const verificationResult = await verifyCredential(
credential,
credentialFormat,
);
return verificationResult;
const verificationResult = await verifyCredential(
credential,
credentialFormat,
);
return verificationResult;
}
function resolveEd25519Alg(proofSigningAlgosSupported: string[]) {
return proofSigningAlgosSupported.includes(

View File

@@ -4,6 +4,8 @@ import {
} from '../machines/VerifiableCredential/VCMetaMachine/vc';
import {__AppId} from './GlobalVariables';
import {MIMOTO_BASE_URL, REQUEST_TIMEOUT} from './constants';
import NetInfo from '@react-native-community/netinfo';
import { ErrorMessage } from './openId4VCI/Utils';
export type HTTP_METHOD = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
@@ -14,6 +16,14 @@ export class BackendResponseError extends Error {
}
}
async function assertInternetConnection() {
const net = await NetInfo.fetch();
if (!net.isConnected || net.isInternetReachable === false) {
console.info('No internet');
throw new Error('No internet connection');
}
}
export async function request(
method: HTTP_METHOD,
path: `/${string}` | string,
@@ -22,66 +32,81 @@ export async function request(
headers: Record<string, string> = {
'Content-Type': 'application/json',
},
timeoutMillis?: undefined | number,
timeoutMillis?: number | undefined,
) {
if (path.includes('v1/mimoto')) headers['X-AppId'] = __AppId.getValue();
if (path.includes('v1/mimoto')) {
headers['X-AppId'] = __AppId.getValue();
}
const requestUrl = path.startsWith('https://') ? path : host + path;
let response;
const requestUrl = path.indexOf('https://') != -1 ? path : host + path;
if (timeoutMillis === undefined) {
response = await fetch(requestUrl, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
} else {
console.log(`making a web request to ${requestUrl}`);
let controller = new AbortController();
setTimeout(() => {
controller.abort();
}, timeoutMillis);
try {
try {
if (timeoutMillis === undefined) {
response = await fetch(requestUrl, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
} catch (error) {
console.error(
`Error occurred while making request: ${host + path}: ${error}`,
);
if (error.name === 'AbortError') {
throw new Error(REQUEST_TIMEOUT);
} else {
console.info(`Making a web request to ${requestUrl}`);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMillis);
try {
response = await fetch(requestUrl, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
} catch (error: any) {
clearTimeout(timeoutId);
console.error(`Request failed: ${requestUrl}:`, error);
if (error.name === 'AbortError') {
throw new Error(REQUEST_TIMEOUT);
}
await assertInternetConnection();
throw error;
}
throw error;
}
} catch (error: any) {
console.error(`Failed to fetch from ${requestUrl}:`, error);
await assertInternetConnection();
throw error;
}
let jsonResponse;
try {
jsonResponse = await response.json();
} catch (jsonError) {
console.warn(`Failed to parse JSON from ${requestUrl}`, jsonError);
throw new Error(ErrorMessage.NETWORK_REQUEST_FAILED+' Invalid JSON response');
}
const jsonResponse = await response.json();
if (response.status >= 400) {
let backendUrl = host + path;
let errorMessage =
const backendUrl = host + path;
const errorMessage =
jsonResponse.message ||
(typeof jsonResponse.error === 'object'
? JSON.stringify(jsonResponse.error)
: jsonResponse.error);
console.error(
`The backend API ${backendUrl} returned error code ${response.status} with message --> ${errorMessage}`,
);
throw new Error(errorMessage);
}
if (jsonResponse.errors && jsonResponse.errors.length) {
let backendUrl = host + path;
const {errorCode, errorMessage} = jsonResponse.errors.shift();
const { errorCode, errorMessage } = jsonResponse.errors.shift();
console.error(
'The backend API ' +
backendUrl +
' returned error response --> error code is : ' +
errorCode +
' error message is : ' +
errorMessage,
`The backend API ${requestUrl} returned structured error --> error code: ${errorCode}, message: ${errorMessage}`,
);
throw new BackendResponseError(errorCode, errorMessage);
}

View File

@@ -35,6 +35,15 @@ class VciClient {
this.InjiVciClient.sendIssuerTrustResponseFromJS(consent);
}
async sendTokenResponse(json: string) {
this.InjiVciClient.sendTokenResponseFromJS(json);
}
async getIssuerMetadata(issuerUri: string): Promise<object> {
const response = await this.InjiVciClient.getIssuerMetadata(issuerUri);
return JSON.parse(response);
}
async requestCredentialByOffer(
credentialOffer: string,
getTxCode: (
@@ -43,30 +52,29 @@ class VciClient {
length: number | undefined,
) => void,
getProofJwt: (
accessToken: string,
credentialIssuer: string,
cNonce: string | null,
issuerMetadata: object,
credentialConfigurationId: string,
proofSigningAlgosSupported: string[] | null,
) => void,
navigateToAuthView: (authorizationEndpoint: string) => void,
requestTrustIssuerConsent: (issuerMetadata: object) => void,
): Promise<VerifiableCredential> {
requestTokenResponse: (tokenRequest: object) => void,
requestTrustIssuerConsent: (
credentialIssuer: string,
issuerDisplay: object[],
) => void,
): Promise<any> {
const proofListener = emitter.addListener(
'onRequestProof',
({accessToken, cNonce, issuerMetadata, credentialConfigurationId}) => {
getProofJwt(
accessToken,
cNonce,
JSON.parse(issuerMetadata),
credentialConfigurationId,
);
({credentialIssuer, cNonce, proofSigningAlgorithmsSupported}) => {
getProofJwt(credentialIssuer, cNonce, JSON.parse(proofSigningAlgorithmsSupported));
},
);
const authListener = emitter.addListener(
'onRequestAuthCode',
({authorizationEndpoint}) => {
navigateToAuthView(authorizationEndpoint);
({authorizationUrl}) => {
navigateToAuthView(authorizationUrl);
},
);
@@ -77,21 +85,29 @@ class VciClient {
},
);
const tokenResponseListener = emitter.addListener(
'onRequestTokenResponse',
({tokenRequest}) => {
requestTokenResponse(tokenRequest);
},
);
const trustIssuerListener = emitter.addListener(
'onCheckIssuerTrust',
({issuerMetadata}) => {
requestTrustIssuerConsent(JSON.parse(issuerMetadata));
({credentialIssuer, issuerDisplay}) => {
requestTrustIssuerConsent(credentialIssuer, JSON.parse(issuerDisplay));
},
);
let response = '';
try {
const clientMetadata = {
clientId: 'wallet',
redirectUri: 'io.mosip.residentapp.inji://oauthredirect',
};
response = await this.InjiVciClient.requestCredentialByOffer(
credentialOffer,
JSON.stringify({
clientId: 'wallet',
redirectUri: 'io.mosip.residentapp.inji://oauthredirect',
}),
JSON.stringify(clientMetadata),
);
} catch (error) {
console.error('Error requesting credential by offer:', error);
@@ -100,37 +116,59 @@ class VciClient {
proofListener.remove();
authListener.remove();
txCodeListener.remove();
tokenResponseListener.remove();
trustIssuerListener.remove();
}
return JSON.parse(response) as VerifiableCredential;
const parsedResponse = JSON.parse(response);
return {
credential: {
credential: parsedResponse.credential,
} as VerifiableCredential,
credentialConfigurationId:
parsedResponse.credentialConfigurationId ?? {},
credentialIssuer: parsedResponse.credentialIssuer ?? '',
};
}
async requestCredentialFromTrustedIssuer(
resolvedIssuerMetaData: object,
credentialIssuerUri: string,
credentialConfigurationId: string,
clientMetadata: object,
getProofJwt: (accessToken: string, cNonce: string) => void,
getProofJwt: (
credentialIssuer: string,
cNonce: string | null,
proofSigningAlgosSupported: string[] | null,
) => void,
navigateToAuthView: (authorizationEndpoint: string) => void,
): Promise<VerifiableCredential> {
requestTokenResponse: (tokenRequest: object) => void,
): Promise<any> {
const proofListener = emitter.addListener(
'onRequestProof',
({accessToken, cNonce}) => {
getProofJwt(accessToken, cNonce);
proofListener.remove();
({credentialIssuer, cNonce, proofSigningAlgorithmsSupported}) => {
getProofJwt(credentialIssuer, cNonce, JSON.parse(proofSigningAlgorithmsSupported));
},
);
const authListener = emitter.addListener(
'onRequestAuthCode',
({authorizationEndpoint}) => {
navigateToAuthView(authorizationEndpoint);
({authorizationUrl}) => {
navigateToAuthView(authorizationUrl);
},
);
const tokenResponseListener = emitter.addListener(
'onRequestTokenResponse',
({tokenRequest}) => {
requestTokenResponse(tokenRequest);
},
);
let response = '';
try {
response = await this.InjiVciClient.requestCredentialFromTrustedIssuer(
JSON.stringify(resolvedIssuerMetaData),
credentialIssuerUri,
credentialConfigurationId,
JSON.stringify(clientMetadata),
);
} catch (error) {
@@ -139,9 +177,18 @@ class VciClient {
} finally {
proofListener.remove();
authListener.remove();
tokenResponseListener.remove();
}
return JSON.parse(response) as VerifiableCredential;
const parsedResponse = JSON.parse(response);
return {
credential: {
credential: parsedResponse.credential,
} as VerifiableCredential,
credentialConfigurationId:
parsedResponse.credentialConfigurationId ?? {},
credentialIssuer: parsedResponse.credentialIssuer ?? '',
};
}
}