diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoConnectionHelper.kt b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoConnectionHelper.kt index d422706f..bc81e92e 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoConnectionHelper.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoConnectionHelper.kt @@ -42,6 +42,14 @@ class FidoConnectionHelper(private val deviceManager: DeviceManager) { return requestHandled } + fun failPending(e: Exception) { + pendingAction?.let { action -> + logger.error("Failing pending action with {}", e.message) + action.invoke(Result.failure(e)) + pendingAction = null + } + } + fun cancelPending() { pendingAction?.let { action -> action.invoke(Result.failure(CancellationException())) diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoManager.kt b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoManager.kt index b1d648c1..8f6908ae 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoManager.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoManager.kt @@ -210,8 +210,13 @@ class FidoManager( // something went wrong, try to get DeviceInfo from any available connection type logger.error("Failure when processing YubiKey: ", e) - // Clear any cached FIDO state - fidoViewModel.clearSessionState() + connectionHelper.failPending(e) + + if (e !is IOException) { + // we don't clear the session on IOExceptions so that the session is ready for + // a possible re-run of a failed action. + fidoViewModel.clearSessionState() + } throw e } diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/oath/OathManager.kt b/android/app/src/main/kotlin/com/yubico/authenticator/oath/OathManager.kt index 9b540cf8..e5b1036b 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/oath/OathManager.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/oath/OathManager.kt @@ -266,7 +266,15 @@ class OathManager( ) ) if (!session.isLocked) { - oathViewModel.updateCredentials(calculateOathCodes(session)) + try { + oathViewModel.updateCredentials(calculateOathCodes(session)) + } catch (e: IOException) { + // in this situation we clear the session because otherwise + // the credential list would be in loading state + // clearing the session will prompt the user to try again + oathViewModel.clearSession() + throw e + } } // Awaiting an action for a different or no device? @@ -315,14 +323,19 @@ class OathManager( } } catch (e: Exception) { // OATH not enabled/supported, try to get DeviceInfo over other USB interfaces - logger.error("Failed to connect to CCID: ", e) - // Clear any cached OATH state - oathViewModel.clearSession() + logger.error("Exception during SmartCard connection/OATH session creation: ", e) + // Remove any pending action pendingAction?.let { action -> - logger.error("Cancelling pending action") + logger.error("Failing pending action with {}", e.message) + action.invoke(Result.failure(e)) pendingAction = null - action.invoke(Result.failure(CancellationException())) + } + + if (e !is IOException) { + // we don't clear the session on IOExceptions so that the session is ready for + // a possible re-run of a failed action. + oathViewModel.clearSession() } throw e diff --git a/lib/app/views/main_page.dart b/lib/app/views/main_page.dart index 4d088453..b2e18721 100755 --- a/lib/app/views/main_page.dart +++ b/lib/app/views/main_page.dart @@ -60,7 +60,8 @@ class MainPage extends ConsumerWidget { ref.listen>(currentDeviceDataProvider, (prev, next) { final serial = next.value?.info.serial; - if (serial != null && serial == prev?.value?.info.serial) { + if ((serial != null && serial == prev?.value?.info.serial) || + (next.hasValue && (prev != null && prev.isLoading))) { return; }