diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoActionDescription.kt b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoActionDescription.kt index 6ca3ab80..8cd6c378 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoActionDescription.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoActionDescription.kt @@ -22,7 +22,8 @@ enum class FidoActionDescription(private val value: Int) { Reset(0), Unlock(1), SetPin(2), - ActionFailure(3); + DeleteCredential(3), + ActionFailure(4); val id: Int get() = value + dialogDescriptionOathIndex 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 1d1d1b8d..789ca5bd 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 @@ -20,7 +20,6 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import com.yubico.authenticator.AppContextManager -import com.yubico.authenticator.AppPreferences import com.yubico.authenticator.DialogIcon import com.yubico.authenticator.DialogManager import com.yubico.authenticator.DialogTitle @@ -159,8 +158,8 @@ class FidoManager( ) "delete_credential" -> deleteCredential( - args["rpId"] as String, - args["credentialId"] as String + args["rp_id"] as String, + args["credential_id"] as String ) else -> throw NotImplementedError() @@ -390,12 +389,13 @@ class FidoManager( val rpIds = credMan.enumerateRps() val credentials = rpIds.map { rpData -> - credMan.enumerateCredentials(rpData.rpIdHash).map { credential -> + credMan.enumerateCredentials(rpData.rpIdHash).map { credentialData -> FidoCredential( rpData.rp["id"] as String, - (credential.credentialId["id"] as ByteArray).asString(), - (credential.user["id"] as ByteArray).asString(), - credential.user["name"] as String + (credentialData.credentialId["id"] as ByteArray).asString(), + (credentialData.user["id"] as ByteArray).asString(), + credentialData.user["name"] as String, + publicKeyCredentialDescriptor = credentialData.credentialId ) } }.reduceOrNull { credentials, credentialList -> @@ -408,8 +408,30 @@ class FidoManager( } private suspend fun deleteCredential(rpId: String, credentialId: String): String = - useSession(FidoActionDescription.SetPin) { _ -> - "" + useSession(FidoActionDescription.DeleteCredential) { fidoSession -> + val credMan = CredentialManagement(fidoSession, clientPin!!.pinUvAuth, token!!) + + val credentialDescriptor = + fidoViewModel.credentials.value?.firstOrNull { + it.credentialId == credentialId && it.rpId == rpId + }?.publicKeyCredentialDescriptor + + credentialDescriptor?.let { + credMan.deleteCredential(credentialDescriptor) + fidoViewModel.removeCredential(rpId, credentialId) + return@useSession JSONObject( + mapOf( + "success" to true, + ) + ).toString() + } + + // could not find the credential to delete + JSONObject( + mapOf( + "success" to false, + ) + ).toString() } private suspend fun useSession( diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoViewModel.kt b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoViewModel.kt index 79802a22..3ad0fba5 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoViewModel.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/fido/FidoViewModel.kt @@ -37,4 +37,10 @@ class FidoViewModel : ViewModel() { fun updateCredentials(credentials: List) { _credentials.postValue(credentials) } + + fun removeCredential(rpId: String, credentialId: String) { + _credentials.postValue(_credentials.value?.filter { + it.credentialId != credentialId || it.rpId != rpId + }) + } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/fido/data/FidoCredential.kt b/android/app/src/main/kotlin/com/yubico/authenticator/fido/data/FidoCredential.kt index 5eb7a55e..ae102708 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/fido/data/FidoCredential.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/fido/data/FidoCredential.kt @@ -18,6 +18,7 @@ package com.yubico.authenticator.fido.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient @Serializable data class FidoCredential( @@ -28,5 +29,7 @@ data class FidoCredential( @SerialName("user_id") val userId: String, @SerialName("user_name") - val userName: String + val userName: String, + @Transient + val publicKeyCredentialDescriptor: Map = emptyMap() ) diff --git a/lib/android/fido/state.dart b/lib/android/fido/state.dart index 7ba8c670..ab9da453 100644 --- a/lib/android/fido/state.dart +++ b/lib/android/fido/state.dart @@ -161,8 +161,6 @@ class _FidoCredentialsNotifier extends FidoCredentialsNotifier { if (json == null) { state = const AsyncValue.loading(); } else { - // final fidoState = FidoState.fromJson(json); - _log.debug('Received event: $event'); List newState = List.from( (json as List).map((e) => FidoCredential.fromJson(e)).toList()); state = AsyncValue.data(newState); @@ -172,18 +170,34 @@ class _FidoCredentialsNotifier extends FidoCredentialsNotifier { }); ref.onDispose(_sub.cancel); - - // final fidoState = ref.watch(fidoStateProvider(devicePath)); - // // fidoState.whenData((value) async { - // // if (value.unlocked) { - // // final unlockResponse = - // // jsonDecode(await _methods.invokeMethod('credentials')); - // // } - // // }); - return Completer>().future; } @override - Future deleteCredential(FidoCredential credential) async {} + Future deleteCredential(FidoCredential credential) async { + try { + final deleteCredentialResponse = jsonDecode(await _methods.invokeMethod( + 'delete_credential', + { + 'rp_id': credential.rpId, + 'credential_id': credential.credentialId, + }, + )); + + if (deleteCredentialResponse['success'] == true) { + _log.debug('FIDO delete credential succeeded'); + } else { + _log.debug('FIDO delete credential failed'); + } + } on PlatformException catch (pe) { + var decodedException = pe.decode(); + if (decodedException is CancellationException) { + _log.debug('User cancelled delete credential FIDO operation'); + } else { + _log.error('Delete credential FIDO operation failed.', pe); + } + + throw decodedException; + } + } } diff --git a/lib/android/tap_request_dialog.dart b/lib/android/tap_request_dialog.dart index 7edd0c49..413a870a 100755 --- a/lib/android/tap_request_dialog.dart +++ b/lib/android/tap_request_dialog.dart @@ -77,6 +77,7 @@ enum _DDesc { fidoResetApplet, fidoUnlockSession, fidoSetPin, + fidoDeleteCredential, fidoActionFailure, // Others invalid; @@ -99,7 +100,8 @@ enum _DDesc { dialogDescriptionFidoIndex + 0: fidoResetApplet, dialogDescriptionFidoIndex + 1: fidoUnlockSession, dialogDescriptionFidoIndex + 2: fidoSetPin, - dialogDescriptionFidoIndex + 3: fidoActionFailure, + dialogDescriptionFidoIndex + 3: fidoDeleteCredential, + dialogDescriptionFidoIndex + 4: fidoActionFailure, }[id] ?? _DDesc.invalid; } @@ -176,6 +178,7 @@ class _DialogProvider { _DDesc.fidoResetApplet => l10n.s_nfc_dialog_fido_reset, _DDesc.fidoUnlockSession => l10n.s_nfc_dialog_fido_unlock, _DDesc.fidoSetPin => l10n.l_nfc_dialog_fido_set_pin, + _DDesc.fidoDeleteCredential => l10n.s_nfc_dialog_fido_delete_credential, _DDesc.fidoActionFailure => l10n.s_nfc_dialog_fido_failure, _ => '' }; diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index a373c988..82626c28 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -725,6 +725,7 @@ "s_nfc_dialog_fido_reset": null, "s_nfc_dialog_fido_unlock": null, "l_nfc_dialog_fido_set_pin": null, + "s_nfc_dialog_fido_delete_credential": null, "s_nfc_dialog_fido_failure": null, "@_ndef": {}, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index c5a34027..f2939e33 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -725,6 +725,7 @@ "s_nfc_dialog_fido_reset": "Action: reset FIDO applet", "s_nfc_dialog_fido_unlock": "Action: unlock FIDO applet", "l_nfc_dialog_fido_set_pin": "Action: set or change FIDO applet PIN", + "s_nfc_dialog_fido_delete_credential": "Action: delete Passkey", "s_nfc_dialog_fido_failure": "FIDO operation failed", "@_ndef": {}, diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index d7edc504..29dc75a2 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -725,6 +725,7 @@ "s_nfc_dialog_fido_reset": null, "s_nfc_dialog_fido_unlock": null, "l_nfc_dialog_fido_set_pin": null, + "s_nfc_dialog_fido_delete_credential": null, "s_nfc_dialog_fido_failure": null, "@_ndef": {}, diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index e610a9f5..cfed0181 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -725,6 +725,7 @@ "s_nfc_dialog_fido_reset": null, "s_nfc_dialog_fido_unlock": null, "l_nfc_dialog_fido_set_pin": null, + "s_nfc_dialog_fido_delete_credential": null, "s_nfc_dialog_fido_failure": null, "@_ndef": {}, diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 9b667d26..9ed730c9 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -725,6 +725,7 @@ "s_nfc_dialog_fido_reset": null, "s_nfc_dialog_fido_unlock": null, "l_nfc_dialog_fido_set_pin": null, + "s_nfc_dialog_fido_delete_credential": null, "s_nfc_dialog_fido_failure": null, "@_ndef": {},