[INJIMOB-3392] add token request logic in wallet for vci flow (#2014)

* [INJIMOB-3392] add token request logic in wallet for vci flow

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

* [INJIMOB-3392] chore: update integration of VCIClient native module

Changes are updated as per new changes in the library

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-3390] refactor: event structure of token request

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-3392] fix tokenEndpoint method and refactorings

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

* [INJIMOB-3392] cnonce decode from accesstoken and credential response destructuring fix

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

* [INJIMOB-3390] add: getIssuerMetadata in kotlin NativeModule

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-3393] fix: auth callback in android

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-3390] fix: proofJwt issue in download flow

Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>

* [INJIMOB-3392] fix credentialofferflow

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

* [INJIMOB-3392]fix format issues in bridge layer

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

* [INJIMOB-3392]fix activity log texts on application reopen

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

* [INJIMOB-3392]cache issuer metadata by key: issuerhost

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

* [INJIMOB-3392] fix error scenarios and cleanup issuermachine

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

* [INJIMOB-3392] refactor request method to handle missing error scenarios

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

* [INJIMOB-3392] fix max lines for txcode description to 2

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

* [INJIMOB-3392] rename credentialissueruri to credentialissuer

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

* [INJIMOB-3392] take cnonce from outside accesstoken

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

* [INJIMOB-3392] declare random-values at entry file

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

* [INJIMOB-3392] set fallback keytype to user priority first

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

* [INJIMOB-3392] add locales for network request failed error

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

* [INJIMOB-3392] remove console log

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

* [INJIMOB-3392] refactor and clean up code

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

---------

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>
Signed-off-by: KiruthikaJeyashankar <kiruthikavjshankar@gmail.com>
Co-authored-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>
This commit is contained in:
KiruthikaJeyashankar
2025-07-24 11:42:00 +05:30
committed by GitHub
parent 0fe6915bb8
commit 5305e7d7ea
37 changed files with 1159 additions and 1519 deletions

View File

@@ -1,3 +1,4 @@
import 'react-native-get-random-values';
import React, {useContext, useEffect, useState} from 'react'; import React, {useContext, useEffect, useState} from 'react';
import {AppLayout} from './screens/AppLayout'; import {AppLayout} from './screens/AppLayout';
import {useFont} from './shared/hooks/useFont'; 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:pixelpass-aar:0.7.0-SNAPSHOT")
implementation("io.mosip:secure-keystore:0.3.0") implementation("io.mosip:secure-keystore:0.3.0")
implementation("io.mosip:tuvali:0.5.2") 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("com.google.code.gson:gson:2.10.1")
implementation("io.mosip:vcverifier-aar:1.3.0-SNAPSHOT") { implementation("io.mosip:vcverifier-aar:1.3.0-SNAPSHOT") {
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on' exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'

View File

@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.modules.core.DeviceEventManagerModule import com.facebook.react.modules.core.DeviceEventManagerModule
import com.google.gson.Gson import com.google.gson.Gson
import io.mosip.vciclient.token.TokenResponse
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
object VCIClientCallbackBridge { object VCIClientCallbackBridge {
@@ -11,6 +12,7 @@ object VCIClientCallbackBridge {
private var deferredAuthCode: CompletableDeferred<String>? = null private var deferredAuthCode: CompletableDeferred<String>? = null
private var deferredTxCode: CompletableDeferred<String>? = null private var deferredTxCode: CompletableDeferred<String>? = null
private var deferredIssuerTrustResponse: CompletableDeferred<Boolean>? = null private var deferredIssuerTrustResponse: CompletableDeferred<Boolean>? = null
private var deferredTokenResponse: CompletableDeferred<TokenResponse>? = null
fun createProofDeferred(): CompletableDeferred<String> { fun createProofDeferred(): CompletableDeferred<String> {
deferredProof = CompletableDeferred() deferredProof = CompletableDeferred()
@@ -22,72 +24,86 @@ object VCIClientCallbackBridge {
return deferredAuthCode!! return deferredAuthCode!!
} }
fun createTokenResponseDeferred(): CompletableDeferred<TokenResponse> {
deferredTokenResponse = CompletableDeferred()
return deferredTokenResponse!!
}
fun createTxCodeDeferred(): CompletableDeferred<String> { fun createTxCodeDeferred(): CompletableDeferred<String> {
deferredTxCode = CompletableDeferred() deferredTxCode = CompletableDeferred()
return deferredTxCode!! return deferredTxCode!!
} }
fun createIsuerTrustResponseDeferred(): CompletableDeferred<Boolean> { fun createIssuerTrustResponseDeferred(): CompletableDeferred<Boolean> {
deferredIssuerTrustResponse = CompletableDeferred() deferredIssuerTrustResponse = CompletableDeferred()
return deferredIssuerTrustResponse!! return deferredIssuerTrustResponse!!
} }
fun emitRequestProof( fun emitRequestProof(
context: ReactApplicationContext, context: ReactApplicationContext,
accessToken: String, credentialIssuer: String,
cNonce: String?, cNonce: String?,
issuerMetadata: Map<String, *>? = null, proofSigningAlgorithmsSupported: List<String>,
credentialConfigurationId: String? = null
) { ) {
val params = val params =
Arguments.createMap().apply { Arguments.createMap().apply {
putString("accessToken", accessToken) putString("credentialIssuer", credentialIssuer)
if (cNonce != null) putString("cNonce", cNonce) if (cNonce != null) putString("cNonce", cNonce)
if (issuerMetadata != null) { val json = Gson().toJson(proofSigningAlgorithmsSupported)
val json = Gson().toJson(issuerMetadata) putString("proofSigningAlgorithmsSupported", json)
putString("issuerMetadata", json) }
}
if (credentialConfigurationId != null) {
putString("credentialConfigurationId", credentialConfigurationId)
}
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onRequestProof", params) .emit("onRequestProof", params)
} }
fun emitRequestAuthCode(context: ReactApplicationContext, authorizationEndpoint: String) { fun emitRequestAuthCode(context: ReactApplicationContext, authorizationEndpoint: String) {
val params = val params =
Arguments.createMap().apply { Arguments.createMap().apply {
putString("authorizationEndpoint", authorizationEndpoint) putString("authorizationUrl", authorizationEndpoint)
} }
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) 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( fun emitRequestTxCode(
context: ReactApplicationContext, context: ReactApplicationContext,
inputMode: String?, inputMode: String?,
description: String?, description: String?,
length: Int? length: Int?
) { ) {
val params = val params =
Arguments.createMap().apply { Arguments.createMap().apply {
putString("inputMode", inputMode) putString("inputMode", inputMode)
putString("description", description) putString("description", description)
if( length != null) if (length != null)
putInt("length", length) putInt("length", length)
} }
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) 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 = val params =
Arguments.createMap().apply { Arguments.createMap().apply {
putString("issuerMetadata", Gson().toJson(issuerMetadata)) //TODO: Convert Gson construction to singleton pattern
} putString("credentialIssuer", credentialIssuer)
putString("issuerDisplay", Gson().toJson(issuerDisplay))
}
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onCheckIssuerTrust", params) .emit("onCheckIssuerTrust", params)
} }
@JvmStatic @JvmStatic
@@ -95,16 +111,25 @@ object VCIClientCallbackBridge {
deferredProof?.complete(jwt) deferredProof?.complete(jwt)
deferredProof = null deferredProof = null
} }
@JvmStatic @JvmStatic
fun completeAuthCode(code: String) { fun completeAuthCode(code: String) {
deferredAuthCode?.complete(code) deferredAuthCode?.complete(code)
deferredAuthCode = null deferredAuthCode = null
} }
@JvmStatic @JvmStatic
fun completeTxCode(code: String) { fun completeTxCode(code: String) {
deferredTxCode?.complete(code) deferredTxCode?.complete(code)
deferredTxCode = null deferredTxCode = null
} }
@JvmStatic
fun completeTokenResponse(tokenResponse: TokenResponse) {
deferredTokenResponse?.complete(tokenResponse)
deferredTokenResponse = null
}
@JvmStatic @JvmStatic
fun completeIssuerTrustResponse(trusted: Boolean) { fun completeIssuerTrustResponse(trusted: Boolean) {
deferredIssuerTrustResponse?.complete(trusted) deferredIssuerTrustResponse?.complete(trusted)
@@ -117,7 +142,12 @@ object VCIClientCallbackBridge {
suspend fun awaitAuthCode(): String { suspend fun awaitAuthCode(): String {
return deferredAuthCode?.await() 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 { suspend fun awaitTxCode(): String {
@@ -126,6 +156,6 @@ object VCIClientCallbackBridge {
suspend fun awaitIssuerTrustResponse(): Boolean { suspend fun awaitIssuerTrustResponse(): Boolean {
return deferredIssuerTrustResponse?.await() 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.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.util.Objects;
import io.mosip.residentapp.VCIClientCallbackBridge; import java.util.Map;
import io.mosip.residentapp.VCIClientBridge;
import io.mosip.vciclient.VCIClient; import io.mosip.vciclient.VCIClient;
import io.mosip.vciclient.constants.CredentialFormat; import io.mosip.vciclient.authorizationCodeFlow.clientMetadata.ClientMetadata;
import io.mosip.vciclient.credentialOffer.CredentialOffer; import io.mosip.vciclient.credential.response.CredentialResponse;
import io.mosip.vciclient.credentialOffer.CredentialOfferService; import io.mosip.vciclient.token.TokenResponse;
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;
public class InjiVciClientModule extends ReactContextBaseJavaModule { public class InjiVciClientModule extends ReactContextBaseJavaModule {
private VCIClient vciClient; private VCIClient vciClient;
@@ -69,6 +61,29 @@ public class InjiVciClientModule extends ReactContextBaseJavaModule {
VCIClientCallbackBridge.completeIssuerTrustResponse(trusted); 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 @ReactMethod
public void requestCredentialByOffer(String credentialOffer,String clientMetadataJson, Promise promise) { public void requestCredentialByOffer(String credentialOffer,String clientMetadataJson, Promise promise) {
new Thread(() -> { new Thread(() -> {
@@ -88,15 +103,13 @@ public class InjiVciClientModule extends ReactContextBaseJavaModule {
} }
@ReactMethod @ReactMethod
public void requestCredentialFromTrustedIssuer(String resolvedIssuerMetaJson, String clientMetadataJson, Promise promise) { public void requestCredentialFromTrustedIssuer(String credentialIssuer, String credentialConfigurationId, String clientMetadataJson, Promise promise) {
new Thread(() -> { new Thread(() -> {
try { try {
IssuerMetadata issuerMetaData = new Gson().fromJson(
resolvedIssuerMetaJson, IssuerMetadata.class);
ClientMetadata clientMetadata= new Gson().fromJson( ClientMetadata clientMetadata= new Gson().fromJson(
clientMetadataJson, ClientMetadata.class); clientMetadataJson, ClientMetadata.class);
CredentialResponse response = VCIClientBridge.requestCredentialFromTrustedIssuerSync(vciClient, issuerMetaData,clientMetadata); CredentialResponse response = VCIClientBridge.requestCredentialFromTrustedIssuerSync(vciClient, credentialIssuer, credentialConfigurationId,clientMetadata);
reactContext.runOnUiQueueThread(() -> { reactContext.runOnUiQueueThread(() -> {
promise.resolve(response != null ? response.toJsonString() : null); promise.resolve(response != null ? response.toJsonString() : null);

View File

@@ -2,10 +2,12 @@ package io.mosip.residentapp
import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactApplicationContext
import io.mosip.vciclient.VCIClient import io.mosip.vciclient.VCIClient
import io.mosip.vciclient.clientMetadata.ClientMetadata import io.mosip.vciclient.authorizationCodeFlow.clientMetadata.ClientMetadata
import io.mosip.vciclient.credentialResponse.CredentialResponse import io.mosip.vciclient.credential.response.CredentialResponse
import io.mosip.vciclient.issuerMetadata.IssuerMetadata import io.mosip.vciclient.token.TokenRequest
import io.mosip.residentapp.VCIClientBridge import io.mosip.vciclient.token.TokenResponse
import io.mosip.vciclient.constants.AuthorizeUserCallback
import io.mosip.vciclient.constants.ProofJwtCallback
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
object VCIClientBridge { object VCIClientBridge {
@@ -18,62 +20,94 @@ object VCIClientBridge {
client: VCIClient, client: VCIClient,
offer: String, offer: String,
clientMetaData: ClientMetadata clientMetaData: ClientMetadata
): CredentialResponse? = runBlocking { ): CredentialResponse = runBlocking {
client.requestCredentialByCredentialOffer( client.requestCredentialByCredentialOffer(
credentialOffer = offer, credentialOffer = offer,
clientMetadata = clientMetaData, clientMetadata = clientMetaData,
getTxCode = { inputMode, description, length -> getTxCode = getTxCodeCallback(),
VCIClientCallbackBridge.createTxCodeDeferred() authorizeUser = authorizeUserCallback(),
VCIClientCallbackBridge.emitRequestTxCode(VCIClientBridge.reactContext, getTokenResponse = getTokenResponseCallback(),
inputMode, getProofJwt = getProofJwtCallback(),
description, onCheckIssuerTrust = onCheckIssuerTrustCallback()
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()
}
) )
} }
@JvmStatic @JvmStatic
fun requestCredentialFromTrustedIssuerSync( fun requestCredentialFromTrustedIssuerSync(
client: VCIClient, client: VCIClient,
resolvedIssuerMetaData: IssuerMetadata, credentialIssuer: String,
credentialConfigurationId: String,
clientMetaData: ClientMetadata clientMetaData: ClientMetadata
): CredentialResponse? = runBlocking { ): CredentialResponse = runBlocking {
client.requestCredentialFromTrustedIssuer( client.requestCredentialFromTrustedIssuer(
issuerMetadata = resolvedIssuerMetaData, credentialIssuer,
clientMetadata = clientMetaData, credentialConfigurationId,
getProofJwt = { accessToken, cNonce, _, _ -> clientMetaData,
VCIClientCallbackBridge.createProofDeferred() authorizeUser = authorizeUserCallback(),
VCIClientCallbackBridge.emitRequestProof(reactContext, accessToken, cNonce) getTokenResponse = getTokenResponseCallback(),
VCIClientCallbackBridge.awaitProof() getProofJwt = getProofJwtCallback(),
},
getAuthCode = { authorizationEndpoint ->
VCIClientCallbackBridge.createAuthCodeDeferred()
VCIClientCallbackBridge.emitRequestAuthCode(reactContext, authorizationEndpoint)
VCIClientCallbackBridge.awaitAuthCode()
}
) )
} }
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> = ({ export const VCCardView: React.FC<VCItemProps> = ({
vcMetadata, vcMetadata,
margin,
selectable, selectable,
selected, selected,
onPress, onPress,
onShow,
isDownloading, isDownloading,
isPinned, isPinned,
flow, flow,
@@ -58,7 +56,7 @@ export const VCCardView: React.FC<VCItemProps> = ({
setVc(processedData); setVc(processedData);
} }
} }
loadVc(); loadVc();
}, [isDownloading, controller.credential]); }, [isDownloading, controller.credential]);

View File

@@ -32,10 +32,10 @@
9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */; }; 9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */; };
9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */; }; 9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */; };
9C7CDF432C7CC13500243A9A /* RNSecureKeystoreModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */; }; 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 */; }; 9CCCA19E2CF87A8400D5A461 /* securekeystore in Frameworks */ = {isa = PBXBuildFile; productRef = 9CCCA19D2CF87A8400D5A461 /* securekeystore */; };
9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 9CD470CD2DFFF86600C207F9 /* OpenID4VP */; }; 9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 9CD470CD2DFFF86600C207F9 /* OpenID4VP */; };
9CD470D12DFFF89C00C207F9 /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = 9CD470D02DFFF89C00C207F9 /* VCIClient */; }; 9CDFD3102E28CA7B00505CEF /* VCIClient in Frameworks */ = {isa = PBXBuildFile; productRef = 9CDFD30F2E28CA7B00505CEF /* VCIClient */; };
9CFB37492DDDC9A000C199A8 /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 9CFB37482DDDC9A000C199A8 /* pixelpass */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
E86208152C0335C5007C3E24 /* RNVCIClientModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86208142C0335C5007C3E24 /* RNVCIClientModule.swift */; }; E86208152C0335C5007C3E24 /* RNVCIClientModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86208142C0335C5007C3E24 /* RNVCIClientModule.swift */; };
@@ -123,10 +123,10 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
9CD470D12DFFF89C00C207F9 /* VCIClient in Frameworks */, 9CDFD3102E28CA7B00505CEF /* VCIClient in Frameworks */,
9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */, 9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */,
9CAE74EE2E2E38F800C2532C /* pixelpass in Frameworks */,
9CCCA19E2CF87A8400D5A461 /* securekeystore in Frameworks */, 9CCCA19E2CF87A8400D5A461 /* securekeystore in Frameworks */,
9CFB37492DDDC9A000C199A8 /* pixelpass in Frameworks */,
9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */, 9CD470CE2DFFF86600C207F9 /* OpenID4VP in Frameworks */,
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */, 96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */,
); );
@@ -305,6 +305,7 @@
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
D11A8C363B4A5B625DB10379 /* [CP] Embed Pods Frameworks */, D11A8C363B4A5B625DB10379 /* [CP] Embed Pods Frameworks */,
9CDFD3112E2A2F7900505CEF /* ShellScript */,
); );
buildRules = ( buildRules = (
); );
@@ -314,9 +315,9 @@
packageProductDependencies = ( packageProductDependencies = (
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */, 9C4850422C3E5873002ECBD5 /* ios-tuvali-library */,
9CCCA19D2CF87A8400D5A461 /* securekeystore */, 9CCCA19D2CF87A8400D5A461 /* securekeystore */,
9CFB37482DDDC9A000C199A8 /* pixelpass */,
9CD470CD2DFFF86600C207F9 /* OpenID4VP */, 9CD470CD2DFFF86600C207F9 /* OpenID4VP */,
9CD470D02DFFF89C00C207F9 /* VCIClient */, 9CDFD30F2E28CA7B00505CEF /* VCIClient */,
9CAE74ED2E2E38F800C2532C /* pixelpass */,
); );
productName = Inji; productName = Inji;
productReference = 13B07F961A680F5B00A75B9A /* Inji.app */; productReference = 13B07F961A680F5B00A75B9A /* Inji.app */;
@@ -347,9 +348,9 @@
packageReferences = ( packageReferences = (
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */, 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */,
9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */, 9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */,
9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
9CD470CC2DFFF86600C207F9 /* XCRemoteSwiftPackageReference "inji-openid4vp-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 */; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = ""; projectDirPath = "";
@@ -833,6 +834,14 @@
version = 0.5.0; 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" */ = { 9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/secure-keystore-ios-swift"; repositoryURL = "https://github.com/mosip/secure-keystore-ios-swift";
@@ -849,19 +858,11 @@
kind = branch; kind = branch;
}; };
}; };
9CD470CF2DFFF89C00C207F9 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = { 9CDFD30E2E28CA7B00505CEF /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/inji-vci-client-ios-swift/"; repositoryURL = "https://github.com/mosip/inji-vci-client-ios-swift/";
requirement = { requirement = {
branch = develop; branch = "release-0.4.x";
kind = branch;
};
};
9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift/";
requirement = {
branch = develop;
kind = branch; kind = branch;
}; };
}; };
@@ -873,6 +874,11 @@
package = 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */; package = 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */;
productName = "ios-tuvali-library"; productName = "ios-tuvali-library";
}; };
9CAE74ED2E2E38F800C2532C /* pixelpass */ = {
isa = XCSwiftPackageProductDependency;
package = 9CAE74EC2E2E38F800C2532C /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
productName = pixelpass;
};
9CCCA19D2CF87A8400D5A461 /* securekeystore */ = { 9CCCA19D2CF87A8400D5A461 /* securekeystore */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */; package = 9CCCA19C2CF87A8400D5A461 /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */;
@@ -883,16 +889,11 @@
package = 9CD470CC2DFFF86600C207F9 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */; package = 9CD470CC2DFFF86600C207F9 /* XCRemoteSwiftPackageReference "inji-openid4vp-ios-swift" */;
productName = OpenID4VP; productName = OpenID4VP;
}; };
9CD470D02DFFF89C00C207F9 /* VCIClient */ = { 9CDFD30F2E28CA7B00505CEF /* VCIClient */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 9CD470CF2DFFF89C00C207F9 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */; package = 9CDFD30E2E28CA7B00505CEF /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */;
productName = VCIClient; productName = VCIClient;
}; };
9CFB37482DDDC9A000C199A8 /* pixelpass */ = {
isa = XCSwiftPackageProductDependency;
package = 9CFB37472DDDC99F00C199A8 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
productName = pixelpass;
};
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
}; };
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;

View File

@@ -78,8 +78,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/inji-vci-client-ios-swift/", "location" : "https://github.com/mosip/inji-vci-client-ios-swift/",
"state" : { "state" : {
"branch" : "develop", "branch" : "release-0.4.x",
"revision" : "8a39bbf7805af4c615904090a027fa472e5f4534" "revision" : "56359149fbfa61e4fd334df21bf2116e05b4d02d"
} }
}, },
{ {
@@ -96,8 +96,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/mosip/pixelpass-ios-swift/", "location" : "https://github.com/mosip/pixelpass-ios-swift/",
"state" : { "state" : {
"branch" : "develop", "branch" : "release-0.6.x",
"revision" : "78a8b507f5bd0046e273e3ba962696b44aea0e33" "revision" : "78faea2ff48626ef9a6d198123bcf7b299acefd1"
} }
}, },
{ {
@@ -130,9 +130,9 @@
{ {
"identity" : "swiftcbor", "identity" : "swiftcbor",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/valpackett/SwiftCBOR", "location" : "https://github.com/abhip2565/SwiftCBOR",
"state" : { "state" : {
"revision" : "04ccff117f6549121d5721ec84fdf0162122b90e", "revision" : "cc4e195a0ea2dce88f2fd6c6d73b2504e3c884fd",
"version" : "0.5.0" "version" : "0.5.0"
} }
}, },

View File

@@ -12,12 +12,18 @@ RCT_EXTERN_METHOD(requestCredentialByOffer:(NSString *)credentialOffer
resolver:(RCTPromiseResolveBlock)resolve resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) rejecter:(RCTPromiseRejectBlock)reject)
// Requests a credential from a trusted issuer using issuer metadata and client metadata (both as JSON strings) // Requests a credential from a trusted issuer using issuer URI, configuration ID, and client metadata (all as strings)
RCT_EXTERN_METHOD(requestCredentialFromTrustedIssuer:(NSString *)issuerMetadata RCT_EXTERN_METHOD(requestCredentialFromTrustedIssuer:(NSString *)credentialIssuer
credentialConfigurationId:(NSString *)credentialConfigurationId
clientMetadata:(NSString *)clientMetadata clientMetadata:(NSString *)clientMetadata
resolver:(RCTPromiseResolveBlock)resolve resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) 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) // Sends proof JWT back to native side (in response to onRequestProof)
RCT_EXTERN_METHOD(sendProofFromJS:(NSString *)jwtProof) 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) // Sends issuer trust decision (true/false) back to native side (in response to onCheckIssuerTrust)
RCT_EXTERN_METHOD(sendIssuerTrustResponseFromJS:(BOOL)isTrusted) 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 // Required by React Native
RCT_EXTERN_METHOD(requiresMainQueueSetup:(BOOL)isRequired) RCT_EXTERN_METHOD(requiresMainQueueSetup:(BOOL)isRequired)

View File

@@ -4,10 +4,13 @@ import VCIClient
@objc(InjiVciClient) @objc(InjiVciClient)
class RNVCIClientModule: NSObject, RCTBridgeModule { class RNVCIClientModule: NSObject, RCTBridgeModule {
private var vciClient: VCIClient? private var vciClient: VCIClient?
private var pendingProofContinuation: ((String) -> Void)? private var pendingProofContinuation: ((String) -> Void)?
private var pendingAuthCodeContinuation: ((String) -> Void)? private var pendingAuthCodeContinuation: ((String) -> Void)?
private var pendingTxCodeContinuation: ((String) -> Void)? private var pendingTxCodeContinuation: ((String) -> Void)?
private var pendingTokenResponseContinuation: ((String) -> Void)?
private var pendingIssuerTrustDecision: ((Bool) -> Void)? private var pendingIssuerTrustDecision: ((Bool) -> Void)?
static func moduleName() -> String { static func moduleName() -> String {
@@ -19,6 +22,8 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
vciClient = VCIClient(traceabilityId: traceabilityId) vciClient = VCIClient(traceabilityId: traceabilityId)
} }
// MARK: - Public API
@objc @objc
func requestCredentialByOffer( func requestCredentialByOffer(
_ credentialOffer: String, _ credentialOffer: String,
@@ -45,19 +50,24 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
length: length length: length
) )
}, },
getProofJwt: { accessToken, cNonce, issuerMetadata, credentialConfigId in authorizeUser: { authUrl in
try await self.getProofContinuationHook(
accessToken: accessToken,
cNonce: cNonce,
issuerMetadata: issuerMetadata,
credentialConfigId: credentialConfigId
)
},
getAuthCode: { authUrl in
try await self.getAuthCodeContinuationHook(authUrl: authUrl) try await self.getAuthCodeContinuationHook(authUrl: authUrl)
}, },
onCheckIssuerTrust: { issuerMetadata in getTokenResponse: { tokenRequest in
try await self.getIssuerTrustDecisionHook(issuerMetadata: issuerMetadata) 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 @objc
func requestCredentialFromTrustedIssuer( func requestCredentialFromTrustedIssuer(
_ issuerMetadata: String, _ credentialIssuer: String,
credentialConfigurationId: String,
clientMetadata: String, clientMetadata: String,
resolver resolve: @escaping RCTPromiseResolveBlock, resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock rejecter reject: @escaping RCTPromiseRejectBlock
@@ -82,22 +93,24 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
return return
} }
let issuer = try parseIssuerMeta(from: issuerMetadata)
let clientMeta = try parseClientMetadata(from: clientMetadata) let clientMeta = try parseClientMetadata(from: clientMetadata)
let response = try await vciClient.requestCredentialFromTrustedIssuer( let response = try await vciClient.requestCredentialFromTrustedIssuer(
issuerMetadata: issuer, credentialIssuer: credentialIssuer,
credentialConfigurationId: credentialConfigurationId,
clientMetadata: clientMeta, clientMetadata: clientMeta,
getProofJwt: { accessToken, cNonce, issuerMetadata, credentialConfigId in authorizeUser: { authUrl in
try await self.getProofContinuationHook(
accessToken: accessToken,
cNonce: cNonce,
issuerMetadata: issuerMetadata,
credentialConfigId: credentialConfigId
)
},
getAuthCode: { authUrl in
try await self.getAuthCodeContinuationHook(authUrl: authUrl) 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( let metadata = try await vciClient.getIssuerMetadata(credentialIssuer: credentialIssuer)
accessToken: String,
cNonce: String?,
issuerMetadata: [String: Any]?,
credentialConfigId: String?
) async throws -> String {
var issuerMetadataJson: String = ""
if let issuerMetadata = issuerMetadata { let data = try JSONSerialization.data(withJSONObject: metadata, options: [])
if let data = try? JSONSerialization.data(withJSONObject: issuerMetadata, options: []), guard let jsonString = String(data: data, encoding: .utf8) else {
let jsonString = String(data: data, encoding: .utf8) { throw NSError(domain: "JSONEncodingError", code: 0)
issuerMetadataJson = jsonString }
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 // MARK: - Callbacks to JS
self.pendingAuthCodeContinuation = { code in continuation.resume(returning: code) }
}
}
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() { if let bridge = RCTBridge.current() {
bridge.eventDispatcher().sendAppEvent(withName: "onRequestTxCode", body: [ bridge.eventDispatcher().sendAppEvent(
"inputMode": inputMode, withName: "onRequestTxCode",
"description": description, body: [
"length": length "inputMode": inputMode,
]) "description": description,
"length": length
]
)
} }
return try await withCheckedThrowingContinuation { continuation in return try await withCheckedThrowingContinuation { continuation in
@@ -162,15 +172,87 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
} }
} }
private func getIssuerTrustDecisionHook(issuerMetadata: [String: Any]) async throws -> Bool { private func getAuthCodeContinuationHook(authUrl: String) async throws -> String {
var metadataJson = "" if let bridge = RCTBridge.current() {
if let data = try? JSONSerialization.data(withJSONObject: issuerMetadata, options: []), bridge.eventDispatcher().sendAppEvent(
let string = String(data: data, encoding: .utf8) { withName: "onRequestAuthCode",
metadataJson = string 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() { 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 return try await withCheckedThrowingContinuation { continuation in
@@ -178,6 +260,8 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
} }
} }
// MARK: - Receivers from JS
@objc(sendProofFromJS:) @objc(sendProofFromJS:)
func sendProofFromJS(_ jwt: String) { func sendProofFromJS(_ jwt: String) {
pendingProofContinuation?(jwt) pendingProofContinuation?(jwt)
@@ -196,24 +280,25 @@ class RNVCIClientModule: NSObject, RCTBridgeModule {
pendingTxCodeContinuation = nil pendingTxCodeContinuation = nil
} }
@objc(sendTokenResponseFromJS:)
func sendTokenResponseFromJS(_ json: String) {
pendingTokenResponseContinuation?(json)
pendingTokenResponseContinuation = nil
}
@objc(sendIssuerTrustResponseFromJS:) @objc(sendIssuerTrustResponseFromJS:)
func sendIssuerTrustResponseFromJS(_ trusted: Bool) { func sendIssuerTrustResponseFromJS(_ trusted: Bool) {
pendingIssuerTrustDecision?(trusted) pendingIssuerTrustDecision?(trusted)
pendingIssuerTrustDecision = nil 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 { guard let data = jsonString.data(using: .utf8) else {
throw NSError(domain: "Invalid JSON string for clientMetadata", code: 0) throw NSError(domain: "Invalid JSON string for clientMetadata", code: 0)
} }
return try JSONDecoder().decode(ClientMetaData.self, from: data) 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)
} }
@objc static func requiresMainQueueSetup() -> Bool { @objc static func requiresMainQueueSetup() -> Bool {

View File

@@ -221,6 +221,10 @@
"title": "لا يوجد اتصال بالإنترنت", "title": "لا يوجد اتصال بالإنترنت",
"message": "الرجاء التحقق من اتصالك وإعادة المحاولة" "message": "الرجاء التحقق من اتصالك وإعادة المحاولة"
}, },
"networkRequestFailed": {
"title": "فشل طلب الشبكة",
"message": "لا يمكننا معالجة طلبك في الوقت الحالي."
},
"biometricsCancelled": { "biometricsCancelled": {
"title": "هل تريد إلغاء التنزيل؟", "title": "هل تريد إلغاء التنزيل؟",
"message": "مطلوب تأكيد البيومترية لمواصلة تنزيل البطاقة." "message": "مطلوب تأكيد البيومترية لمواصلة تنزيل البطاقة."
@@ -1103,4 +1107,4 @@
"confirm": "نعم، أثق بهذا", "confirm": "نعم، أثق بهذا",
"cancel": "لا، أعدني" "cancel": "لا، أعدني"
} }
} }

View File

@@ -221,6 +221,10 @@
"title": "No internet connection", "title": "No internet connection",
"message": "Please check your connection and retry" "message": "Please check your connection and retry"
}, },
"networkRequestFailed":{
"title": "Network request failed",
"message": "We are unable to precess your request at the moment."
},
"biometricsCancelled": { "biometricsCancelled": {
"title": "Do you want to cancel download?", "title": "Do you want to cancel download?",
"message": "Biometric confirmation is required to continue downloading the card." "message": "Biometric confirmation is required to continue downloading the card."

View File

@@ -221,6 +221,10 @@
"title": "Pakisuri ang iyong koneksyon at subukang muli", "title": "Pakisuri ang iyong koneksyon at subukang muli",
"message": "Mangyaring kumonekta sa internet 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": { "biometricsCancelled": {
"title": "Gusto mo bang kanselahin ang pag-download?", "title": "Gusto mo bang kanselahin ang pag-download?",
"message": "Kinakailangan ang biometric confirmation para magpatuloy sa pag-download ng card." "message": "Kinakailangan ang biometric confirmation para magpatuloy sa pag-download ng card."
@@ -887,7 +891,7 @@
"title": "Oops! Isang Error ang Naganap.", "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." "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", "title": "Hindi Suportadong Kahilingan",
"message": "Ang kahilingang ito ay hindi suportado. Pakiusap, sabihan ang tagapagsuri na subukan sa ibang paraan." "message": "Ang kahilingang ito ay hindi suportado. Pakiusap, sabihan ang tagapagsuri na subukan sa ibang paraan."
}, },
@@ -1106,4 +1110,4 @@
"confirm": "Oo, Pinagkakatiwalaan Ko Ito", "confirm": "Oo, Pinagkakatiwalaan Ko Ito",
"cancel": "Hindi, Bumalik Tayo" "cancel": "Hindi, Bumalik Tayo"
} }
} }

View File

@@ -222,6 +222,10 @@
"title": "कोई इंटरनेट कनेक्शन नहीं", "title": "कोई इंटरनेट कनेक्शन नहीं",
"message": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें" "message": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें"
}, },
"networkRequestFailed": {
"title": "नेटवर्क अनुरोध विफल हुआ",
"message": "हम इस समय आपके अनुरोध को संसाधित करने में असमर्थ हैं।"
},
"biometricsCancelled": { "biometricsCancelled": {
"title": "क्या आप डाउनलोड रद्द करना चाहते हैं?", "title": "क्या आप डाउनलोड रद्द करना चाहते हैं?",
"message": "कार्ड डाउनलोड करना जारी रखने के लिए बायोमेट्रिक पुष्टिकरण आवश्यक है।" "message": "कार्ड डाउनलोड करना जारी रखने के लिए बायोमेट्रिक पुष्टिकरण आवश्यक है।"
@@ -1107,4 +1111,4 @@
"confirm": "हाँ, मैं इस पर भरोसा करता हूँ", "confirm": "हाँ, मैं इस पर भरोसा करता हूँ",
"cancel": "नहीं, मुझे वापस ले चलो" "cancel": "नहीं, मुझे वापस ले चलो"
} }
} }

View File

@@ -220,6 +220,10 @@
"title": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ", "title": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ",
"message": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ" "message": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ"
}, },
"networkRequestFailed": {
"title": "ನೆಟ್‌ವರ್ಕ್ ವಿನಂತಿ ವಿಫಲವಾಗಿದೆ",
"message": "ಈ ಕ್ಷಣದಲ್ಲಿ ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
},
"biometricsCancelled": { "biometricsCancelled": {
"title": "ನೀವು ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲು ಬಯಸುವಿರಾ?", "title": "ನೀವು ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲು ಬಯಸುವಿರಾ?",
"message": "ಕಾರ್ಡ್ ಡೌನ್‌ಲೋಡ್ ಮಾಡುವುದನ್ನು ಮುಂದುವರಿಸಲು ಬಯೋಮೆಟ್ರಿಕ್ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ." "message": "ಕಾರ್ಡ್ ಡೌನ್‌ಲೋಡ್ ಮಾಡುವುದನ್ನು ಮುಂದುವರಿಸಲು ಬಯೋಮೆಟ್ರಿಕ್ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ."
@@ -887,7 +891,7 @@
"title": "ಓಹ್! ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ.", "title": "ಓಹ್! ಒಂದು ದೋಷ ಸಂಭವಿಸಿದೆ.",
"message": "ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಾಗ ತಾಂತ್ರಿಕ ತೊಂದರೆ ಸಂಭವಿಸಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." "message": "ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಾಗ ತಾಂತ್ರಿಕ ತೊಂದರೆ ಸಂಭವಿಸಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
}, },
"vpFormatsNotSupported": { "vpFormatsNotSupported": {
"title": "ಬೆಂಬಲಿಸಲ್ಪಟ್ಟ ವಿನಂತಿಯಲ್ಲ", "title": "ಬೆಂಬಲಿಸಲ್ಪಟ್ಟ ವಿನಂತಿಯಲ್ಲ",
"message": "ಈ ವಿನಂತಿಯನ್ನು ಬೆಂಬಲಿಸಲಾಗದು. ದಯವಿಟ್ಟು ಪರಿಶೀಲಕರನ್ನು ಬೇರೆ ಮಾರ್ಗದಿಂದ ಪ್ರಯತ್ನಿಸಲು ಕೇಳಿ." "message": "ಈ ವಿನಂತಿಯನ್ನು ಬೆಂಬಲಿಸಲಾಗದು. ದಯವಿಟ್ಟು ಪರಿಶೀಲಕರನ್ನು ಬೇರೆ ಮಾರ್ಗದಿಂದ ಪ್ರಯತ್ನಿಸಲು ಕೇಳಿ."
}, },
@@ -1106,4 +1110,4 @@
"confirm": "ಹೌದು, ನಾನು ನಂಬುತ್ತೇನೆ", "confirm": "ಹೌದು, ನಾನು ನಂಬುತ್ತೇನೆ",
"cancel": "ಇಲ್ಲ, ನನನ್ನು ಹಿಂದಕ್ಕೆ ಕರೆ" "cancel": "ಇಲ್ಲ, ನನನ್ನು ಹಿಂದಕ್ಕೆ ಕರೆ"
} }
} }

View File

@@ -220,6 +220,10 @@
"title": "இணைய இணைப்பு இல்லை", "title": "இணைய இணைப்பு இல்லை",
"message": "உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்" "message": "உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்"
}, },
"networkRequestFailed": {
"title": "நெட்வொர்க் கோரிக்கை தோல்வியடைந்தது",
"message": "தற்போது உங்கள் கோரிக்கையை செயலாக்க முடியவில்லை."
},
"biometricsCancelled": { "biometricsCancelled": {
"title": "பதிவிறக்கத்தை ரத்து செய்ய வேண்டுமா?", "title": "பதிவிறக்கத்தை ரத்து செய்ய வேண்டுமா?",
"message": "கார்டை தொடர்ந்து பதிவிறக்க பயோமெட்ரிக் உறுதிப்படுத்தல் தேவை." "message": "கார்டை தொடர்ந்து பதிவிறக்க பயோமெட்ரிக் உறுதிப்படுத்தல் தேவை."
@@ -1106,4 +1110,4 @@
"confirm": "ஆம், நம்புகிறேன்", "confirm": "ஆம், நம்புகிறேன்",
"cancel": "இல்லை, என்னை திரும்ப அழைத்துச் செல்" "cancel": "இல்லை, என்னை திரும்ப அழைத்துச் செல்"
} }
} }

View File

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

View File

@@ -33,5 +33,6 @@ export const IssuersEvents = {
TX_CODE_REQUEST: () => ({}), TX_CODE_REQUEST: () => ({}),
TX_CODE_RECEIVED: (txCode: string) => ({txCode}), TX_CODE_RECEIVED: (txCode: string) => ({txCode}),
ON_CONSENT_GIVEN: () => ({}), 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 {isSignedInResult} from '../../shared/CloudBackupAndRestoreUtils';
import {ErrorMessage, OIDCErrors} from '../../shared/openId4VCI/Utils'; import {ErrorMessage, OIDCErrors} from '../../shared/openId4VCI/Utils';
import {isHardwareKeystoreExists} from '../../shared/cryptoutil/cryptoUtil';
import {BiometricCancellationError} from '../../shared/error/BiometricCancellationError'; import {BiometricCancellationError} from '../../shared/error/BiometricCancellationError';
import {VerificationErrorType} from '../../shared/vcjs/verifyCredential'; import {VerificationErrorType} from '../../shared/vcjs/verifyCredential';
@@ -17,32 +16,6 @@ export const IssuersGuards = () => {
return context.keyType == ''; return context.keyType == '';
}, },
isInternetConnected: (_: any, event: any) => !!event.data.isConnected, 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) => { canSelectIssuerAgain: (context: any) => {
return ( return (
context.errorMessage.includes(OIDCErrors.OIDC_CONFIG_ERROR_PREFIX) || context.errorMessage.includes(OIDCErrors.OIDC_CONFIG_ERROR_PREFIX) ||
@@ -50,18 +23,13 @@ export const IssuersGuards = () => {
); );
}, },
shouldFetchIssuersAgain: (context: any) => context.issuers.length === 0, shouldFetchIssuersAgain: (context: any) => context.issuers.length === 0,
isCustomSecureKeystore: () => isHardwareKeystoreExists,
hasUserCancelledBiometric: (_: any, event: any) => hasUserCancelledBiometric: (_: any, event: any) =>
event.data instanceof BiometricCancellationError, event.data instanceof BiometricCancellationError,
isGenericError: (_: any, event: any) => {
const errorMessage = event.data.message;
return errorMessage === ErrorMessage.GENERIC;
},
isCredentialOfferFlow: (context: any) => { isCredentialOfferFlow: (context: any) => {
return context.isCredentialOfferFlow; return context.isCredentialOfferFlow;
}, },
isIssuerIdInTrustedIssuers: (_: any,event:any) => { isIssuerIdInTrustedIssuers: (_: any, event: any) => {
return event.data; 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 { createModel } from 'xstate/lib/model';
import {AuthorizeResult} from 'react-native-app-auth';
import { import {
CredentialTypes, CredentialTypes,
CredentialWrapper, CredentialWrapper,
IssuerWellknownResponse, IssuerWellknownResponse,
VerifiableCredential, VerifiableCredential,
} from '../VerifiableCredential/VCMetaMachine/vc'; } from '../VerifiableCredential/VCMetaMachine/vc';
import {AppServices} from '../../shared/GlobalContext'; import { AppServices } from '../../shared/GlobalContext';
import {VCMetadata} from '../../shared/VCMetadata'; import { VCMetadata } from '../../shared/VCMetadata';
import {IssuersEvents} from './IssuersEvents'; import { IssuersEvents } from './IssuersEvents';
import {issuerType} from './IssuersMachine'; import { issuerType } from './IssuersMachine';
export const IssuersModel = createModel( export const IssuersModel = createModel(
{ {
@@ -18,7 +17,7 @@ export const IssuersModel = createModel(
qrData: '' as string, qrData: '' as string,
selectedIssuer: {} as issuerType, selectedIssuer: {} as issuerType,
selectedIssuerWellknownResponse: {} as IssuerWellknownResponse, selectedIssuerWellknownResponse: {} as IssuerWellknownResponse,
tokenResponse: {} as AuthorizeResult, tokenResponse: {} as object,
errorMessage: '' as string, errorMessage: '' as string,
loadingReason: 'displayIssuers' as string, loadingReason: 'displayIssuers' as string,
verifiableCredential: null as VerifiableCredential | null, verifiableCredential: null as VerifiableCredential | null,
@@ -45,7 +44,9 @@ export const IssuersModel = createModel(
txCodeDescription: '' as string, txCodeDescription: '' as string,
txCodeLength: null as number | null, txCodeLength: null as number | null,
isCredentialOfferFlow: false as boolean, isCredentialOfferFlow: false as boolean,
credentialOfferIssuerMetadata: {} as object, credentialOfferCredentialIssuer: {} as string,
tokenRequestObject: {} as object,
credentialConfigurationId: '' as string,
}, },
{ {
events: IssuersEvents, events: IssuersEvents,

View File

@@ -104,7 +104,3 @@ export function selectSupportedCredentialTypes(state: State) {
export function selectIsQrScanning(state: State) { export function selectIsQrScanning(state: State) {
return state.matches('waitingForQrScan'); 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 NetInfo from '@react-native-community/netinfo';
import {NativeModules} from 'react-native'; import { NativeModules } from 'react-native';
import Cloud from '../../shared/CloudBackupAndRestoreUtils'; import Cloud from '../../shared/CloudBackupAndRestoreUtils';
import {CACHED_API} from '../../shared/api'; import getAllConfigurations, { CACHED_API } from '../../shared/api';
import { import {
fetchKeyPair, fetchKeyPair,
generateKeyPair, generateKeyPair,
} from '../../shared/cryptoutil/cryptoUtil'; } from '../../shared/cryptoutil/cryptoUtil';
import { import {
constructIssuerMetaData,
constructProofJWT, constructProofJWT,
hasKeyPair, hasKeyPair,
updateCredentialInformation, updateCredentialInformation,
verifyCredentialData, verifyCredentialData,
} from '../../shared/openId4VCI/Utils'; } from '../../shared/openId4VCI/Utils';
import VciClient from '../../shared/vciClient/VciClient'; import VciClient from '../../shared/vciClient/VciClient';
import {issuerType} from './IssuersMachine'; import { displayType, issuerType } from './IssuersMachine';
import {setItem} from '../store'; import { setItem } from '../store';
import {API_CACHED_STORAGE_KEYS} from '../../shared/constants'; import { API_CACHED_STORAGE_KEYS } from '../../shared/constants';
import {createCacheObject} from '../../shared/Utils'; import { createCacheObject } from '../../shared/Utils';
import { VerificationResult } from '../../shared/vcjs/verifyCredential';
export const IssuersService = () => { export const IssuersService = () => {
return { return {
@@ -30,21 +30,26 @@ export const IssuersService = () => {
}, },
checkInternet: async () => await NetInfo.fetch(), checkInternet: async () => await NetInfo.fetch(),
downloadIssuerWellknown: async (context: any) => { downloadIssuerWellknown: async (context: any) => {
const wellknownResponse = await CACHED_API.fetchIssuerWellknownConfig( const wellknownResponse = (await VciClient.getInstance().getIssuerMetadata(
context.selectedIssuer.issuer_id, context.selectedIssuer.credential_issuer_host,
context.selectedIssuer.credential_issuer_host )) as issuerType;
? context.selectedIssuer.credential_issuer_host const wellknownCacheObject = createCacheObject(wellknownResponse);
: context.selectedIssuer.credential_issuer, await setItem(
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(
context.selectedIssuer.credential_issuer_host,
),
wellknownCacheObject,
'',
); );
return wellknownResponse; return wellknownResponse;
}, },
getCredentialTypes: async (context: any) => { getCredentialTypes: async (context: any) => {
const credentialTypes = []; const credentialTypes: Array<{id: string; [key: string]: any}> = [];
const selectedIssuer = context.selectedIssuer; const selectedIssuer = context.selectedIssuer;
const keys = const keys = Object.keys(
selectedIssuer.credential_configuration_ids ?? selectedIssuer.credential_configurations_supported,
Object.keys(selectedIssuer.credential_configurations_supported); );
for (const key of keys) { for (const key of keys) {
if (selectedIssuer.credential_configurations_supported[key]) { if (selectedIssuer.credential_configurations_supported[key]) {
@@ -70,26 +75,35 @@ export const IssuersService = () => {
authEndpoint: authorizationEndpoint, authEndpoint: authorizationEndpoint,
}); });
}; };
const getProofJwt = async (accessToken: string, cNonce: string) => { const getProofJwt = async (
credentialIssuer: string,
cNonce: string | null,
proofSigningAlgosSupported: string[] | null,
) => {
sendBack({ sendBack({
type: 'PROOF_REQUEST', type: 'PROOF_REQUEST',
accessToken: accessToken, credentialIssuer: credentialIssuer,
cNonce: cNonce, cNonce: cNonce,
proofSigningAlgosSupported: proofSigningAlgosSupported,
}); });
}; };
const credential = const getTokenResponse = (tokenRequest: object) => {
sendBack({
type: 'TOKEN_REQUEST',
tokenRequest: tokenRequest,
});
};
const {credential} =
await VciClient.getInstance().requestCredentialFromTrustedIssuer( await VciClient.getInstance().requestCredentialFromTrustedIssuer(
constructIssuerMetaData( context.selectedIssuer.credential_issuer_host,
context.selectedIssuer, context.selectedCredentialType.id,
context.selectedCredentialType,
context.selectedCredentialType.scope,
),
{ {
clientId: context.selectedIssuer.client_id, clientId: context.selectedIssuer.client_id,
redirectUri: context.selectedIssuer.redirect_uri, redirectUri: context.selectedIssuer.redirect_uri,
}, },
getProofJwt, getProofJwt,
navigateToAuthView, navigateToAuthView,
getTokenResponse,
); );
return updateCredentialInformation(context, credential); return updateCredentialInformation(context, credential);
}, },
@@ -109,7 +123,7 @@ export const IssuersService = () => {
const {RNSecureKeystoreModule} = NativeModules; const {RNSecureKeystoreModule} = NativeModules;
try { try {
return await RNSecureKeystoreModule.hasAlias( return await RNSecureKeystoreModule.hasAlias(
context.credentialOfferIssuerMetadata.credential_issuer, context.credentialOfferCredentialIssuer,
); );
} catch (error) { } catch (error) {
console.error( console.error(
@@ -123,8 +137,8 @@ export const IssuersService = () => {
const {RNSecureKeystoreModule} = NativeModules; const {RNSecureKeystoreModule} = NativeModules;
try { try {
await RNSecureKeystoreModule.storeData( await RNSecureKeystoreModule.storeData(
context.credentialOfferIssuerMetadata.credential_issuer, context.credentialOfferCredentialIssuer,
JSON.stringify(context.credentialOfferIssuerMetadata), 'trusted',
); );
} catch { } catch {
console.error('Error updating issuer trust in keystore'); console.error('Error updating issuer trust in keystore');
@@ -138,39 +152,16 @@ export const IssuersService = () => {
}); });
}; };
const getSignedProofJwt = async ( const getSignedProofJwt = async (
accessToken: string, credentialIssuer: string,
cNonce: string | null, cNonce: string | null,
issuerMetadata: object, proofSigningAlgosSupported: string[] | null,
credentialConfigurationId: string,
) => { ) => {
let issuer = issuerMetadata as issuerType; sendBack({
issuer.issuer_id = issuer.credential_issuer; type: 'PROOF_REQUEST',
const wellknownCacheObject = createCacheObject(issuer); cNonce: cNonce,
await setItem( issuer: credentialIssuer,
API_CACHED_STORAGE_KEYS.fetchIssuerWellknownConfig(issuer.issuer_id), proofSigningAlgosSupported: proofSigningAlgosSupported,
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,
});
}
}; };
const getTxCode = async ( const getTxCode = async (
@@ -186,24 +177,55 @@ export const IssuersService = () => {
}); });
}; };
const requesTrustIssuerConsent = async (issuerMetadata: object) => { const requesTrustIssuerConsent = async (
const issuerMetadataObject = issuerMetadata as issuerType; credentialIssuer: string,
issuerDisplay: object[],
) => {
const issuerDisplayObject = issuerDisplay as displayType[];
sendBack({ sendBack({
type: 'TRUST_ISSUER_CONSENT_REQUEST', 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( const credentialResponse =
context.qrData, await VciClient.getInstance().requestCredentialByOffer(
getTxCode, context.qrData,
getSignedProofJwt, getTxCode,
navigateToAuthView, getSignedProofJwt,
requesTrustIssuerConsent, navigateToAuthView,
); getTokenResponse,
return credential; 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) => { updateCredential: async (context: any) => {
const credential = await updateCredentialInformation( const credential = await updateCredentialInformation(
context, context,
@@ -211,13 +233,25 @@ export const IssuersService = () => {
); );
return credential; 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) => { constructProof: async (context: any) => {
const issuerMeta = context.selectedIssuer;
const proofJWT = await constructProofJWT( const proofJWT = await constructProofJWT(
context.publicKey, context.publicKey,
context.privateKey, context.privateKey,
context.accessToken, context.credentialOfferCredentialIssuer,
issuerMeta, null,
context.keyType, context.keyType,
context.wellknownKeyTypes, context.wellknownKeyTypes,
true, true,
@@ -226,13 +260,13 @@ export const IssuersService = () => {
await VciClient.getInstance().sendProof(proofJWT); await VciClient.getInstance().sendProof(proofJWT);
return proofJWT; return proofJWT;
}, },
constructProofForTrustedIssuers: async (context: any) => { constructAndSendProofForTrustedIssuers: async (context: any) => {
const issuerMeta = context.selectedIssuer; const issuerMeta = context.selectedIssuer;
const proofJWT = await constructProofJWT( const proofJWT = await constructProofJWT(
context.publicKey, context.publicKey,
context.privateKey, context.privateKey,
context.accessToken, context.selectedIssuer.credential_issuer_host,
issuerMeta, context.selectedIssuer.client_id,
context.keyType, context.keyType,
context.wellknownKeyTypes, context.wellknownKeyTypes,
false, false,
@@ -267,16 +301,82 @@ export const IssuersService = () => {
return context.keyType; 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( const verificationResult = await verifyCredentialData(
context.verifiableCredential?.credential, verifiableCredential?.credential,
context.selectedCredentialType.format selectedCredentialType.format,
); );
if (!verificationResult.isVerified) { if (!verificationResult.isVerified) {
throw new Error(verificationResult.verificationErrorCode); throw new Error(verificationResult.verificationErrorCode);
} }
return verificationResult; 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({ setAllWellknownConfigResponse: model.assign({
wellKnownIssuerMap: (_, event) => { wellKnownIssuerMap: (_, event) => {
Object.entries(event.response).forEach(([key, value]) => { 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>; return event.response as Record<string, Object>;

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -129,7 +129,7 @@ export function parseMetadatas(metadataStrings: object[]) {
} }
export const getVCMetadata = (context: object, keyType: string) => { 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}`; const credentialId = `${UUID.generate()}_${issuer}`;
return VCMetadata.fromVC({ return VCMetadata.fromVC({
@@ -147,7 +147,7 @@ export const getVCMetadata = (context: object, keyType: string) => {
format: context['credentialWrapper'].format, format: context['credentialWrapper'].format,
downloadKeyType: keyType, downloadKeyType: keyType,
credentialType: getCredentialType(context.selectedCredentialType), 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 TECHNICAL_ERROR = 'Technical error';
export const NETWORK_REQUEST_FAILED = 'Network request failed'; 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 IOS_SIGNIN_FAILED = 'iCloud not available';
export const REQUEST_TIMEOUT = 'request timeout'; export const REQUEST_TIMEOUT = 'request timeout';
export const BIOMETRIC_CANCELLED = 'User has cancelled biometric'; export const BIOMETRIC_CANCELLED = 'User has cancelled biometric';

View File

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

View File

@@ -4,6 +4,8 @@ import {
} from '../machines/VerifiableCredential/VCMetaMachine/vc'; } from '../machines/VerifiableCredential/VCMetaMachine/vc';
import {__AppId} from './GlobalVariables'; import {__AppId} from './GlobalVariables';
import {MIMOTO_BASE_URL, REQUEST_TIMEOUT} from './constants'; 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'; 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( export async function request(
method: HTTP_METHOD, method: HTTP_METHOD,
path: `/${string}` | string, path: `/${string}` | string,
@@ -22,66 +32,81 @@ export async function request(
headers: Record<string, string> = { headers: Record<string, string> = {
'Content-Type': 'application/json', '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; let response;
const requestUrl = path.indexOf('https://') != -1 ? path : host + path;
if (timeoutMillis === undefined) { try {
response = await fetch(requestUrl, { if (timeoutMillis === undefined) {
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 {
response = await fetch(requestUrl, { response = await fetch(requestUrl, {
method, method,
headers, headers,
body: body ? JSON.stringify(body) : undefined, body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
}); });
} catch (error) { } else {
console.error( console.info(`Making a web request to ${requestUrl}`);
`Error occurred while making request: ${host + path}: ${error}`, const controller = new AbortController();
); const timeoutId = setTimeout(() => controller.abort(), timeoutMillis);
if (error.name === 'AbortError') {
throw new Error(REQUEST_TIMEOUT); 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) { if (response.status >= 400) {
let backendUrl = host + path; const backendUrl = host + path;
let errorMessage = const errorMessage =
jsonResponse.message || jsonResponse.message ||
(typeof jsonResponse.error === 'object' (typeof jsonResponse.error === 'object'
? JSON.stringify(jsonResponse.error) ? JSON.stringify(jsonResponse.error)
: jsonResponse.error); : jsonResponse.error);
console.error( console.error(
`The backend API ${backendUrl} returned error code ${response.status} with message --> ${errorMessage}`, `The backend API ${backendUrl} returned error code ${response.status} with message --> ${errorMessage}`,
); );
throw new Error(errorMessage); throw new Error(errorMessage);
} }
if (jsonResponse.errors && jsonResponse.errors.length) { if (jsonResponse.errors && jsonResponse.errors.length) {
let backendUrl = host + path; const { errorCode, errorMessage } = jsonResponse.errors.shift();
const {errorCode, errorMessage} = jsonResponse.errors.shift();
console.error( console.error(
'The backend API ' + `The backend API ${requestUrl} returned structured error --> error code: ${errorCode}, message: ${errorMessage}`,
backendUrl +
' returned error response --> error code is : ' +
errorCode +
' error message is : ' +
errorMessage,
); );
throw new BackendResponseError(errorCode, errorMessage); throw new BackendResponseError(errorCode, errorMessage);
} }

View File

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