mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
Merge PR #1467
This commit is contained in:
commit
91397d264a
@ -334,7 +334,9 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
oathViewModel.credentials.streamTo(this, messenger, "android.oath.credentials"),
|
||||
fidoViewModel.sessionState.streamTo(this, messenger, "android.fido.sessionState"),
|
||||
fidoViewModel.credentials.streamTo(this, messenger, "android.fido.credentials"),
|
||||
fidoViewModel.fingerprints.streamTo(this, messenger, "android.fido.fingerprints"),
|
||||
fidoViewModel.resetState.streamTo(this, messenger, "android.fido.reset"),
|
||||
fidoViewModel.registerFingerprint.streamTo(this, messenger, "android.fido.registerFp"),
|
||||
)
|
||||
|
||||
viewModel.appContext.observe(this) {
|
||||
@ -348,7 +350,10 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
// only recreate the contextManager object if it cannot be reused
|
||||
if (appContext == OperationContext.Home ||
|
||||
(appContext == OperationContext.Oath && contextManager is OathManager) ||
|
||||
(appContext == OperationContext.FidoPasskeys && contextManager is FidoManager)
|
||||
(appContext in listOf(
|
||||
OperationContext.FidoPasskeys,
|
||||
OperationContext.FidoFingerprints
|
||||
) && contextManager is FidoManager)
|
||||
) {
|
||||
// no need to dispose this context
|
||||
} else {
|
||||
@ -367,6 +372,7 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
appPreferences
|
||||
)
|
||||
|
||||
OperationContext.FidoFingerprints,
|
||||
OperationContext.FidoPasskeys -> FidoManager(
|
||||
messenger,
|
||||
deviceManager,
|
||||
|
@ -23,7 +23,10 @@ enum class FidoActionDescription(private val value: Int) {
|
||||
Unlock(1),
|
||||
SetPin(2),
|
||||
DeleteCredential(3),
|
||||
ActionFailure(4);
|
||||
DeleteFingerprint(4),
|
||||
RenameFingerprint(5),
|
||||
RegisterFingerprint(6),
|
||||
ActionFailure(7);
|
||||
|
||||
val id: Int
|
||||
get() = value + dialogDescriptionFidoIndex
|
||||
|
@ -16,16 +16,17 @@
|
||||
|
||||
package com.yubico.authenticator.fido
|
||||
|
||||
import android.nfc.TagLostException
|
||||
import com.yubico.authenticator.AppContextManager
|
||||
import com.yubico.authenticator.DialogManager
|
||||
import com.yubico.authenticator.MainViewModel
|
||||
import com.yubico.authenticator.NULL
|
||||
import com.yubico.authenticator.asString
|
||||
import com.yubico.authenticator.device.DeviceListener
|
||||
import com.yubico.authenticator.device.DeviceManager
|
||||
import com.yubico.authenticator.device.Info
|
||||
import com.yubico.authenticator.device.UnknownDevice
|
||||
import com.yubico.authenticator.fido.data.FidoCredential
|
||||
import com.yubico.authenticator.fido.data.FidoFingerprint
|
||||
import com.yubico.authenticator.fido.data.Session
|
||||
import com.yubico.authenticator.fido.data.SessionInfo
|
||||
import com.yubico.authenticator.fido.data.YubiKitFidoSession
|
||||
@ -38,13 +39,17 @@ import com.yubico.yubikit.core.Transport
|
||||
import com.yubico.yubikit.core.YubiKeyConnection
|
||||
import com.yubico.yubikit.core.YubiKeyDevice
|
||||
import com.yubico.yubikit.core.application.ApplicationNotAvailableException
|
||||
import com.yubico.yubikit.core.application.CommandState
|
||||
import com.yubico.yubikit.core.fido.CtapException
|
||||
import com.yubico.yubikit.core.fido.FidoConnection
|
||||
import com.yubico.yubikit.core.internal.Logger
|
||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||
import com.yubico.yubikit.core.util.Result
|
||||
import com.yubico.yubikit.fido.ctap.BioEnrollment
|
||||
import com.yubico.yubikit.fido.ctap.ClientPin
|
||||
import com.yubico.yubikit.fido.ctap.CredentialManagement
|
||||
import com.yubico.yubikit.fido.ctap.Ctap2Session.InfoData
|
||||
import com.yubico.yubikit.fido.ctap.FingerprintBioEnrollment
|
||||
import com.yubico.yubikit.fido.ctap.PinUvAuthDummyProtocol
|
||||
import com.yubico.yubikit.fido.ctap.PinUvAuthProtocol
|
||||
import com.yubico.yubikit.fido.ctap.PinUvAuthProtocolV1
|
||||
@ -72,6 +77,12 @@ class FidoManager(
|
||||
dialogManager: DialogManager,
|
||||
) : AppContextManager(), DeviceListener {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private object HexCodec {
|
||||
fun bytesToHexString(bytes: ByteArray) : String = bytes.toHexString()
|
||||
fun hexStringToBytes(hex: String) : ByteArray = hex.hexToByteArray()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getPreferredPinUvAuthProtocol(infoData: InfoData): PinUvAuthProtocol {
|
||||
val pinUvAuthProtocols = infoData.pinUvAuthProtocols
|
||||
@ -135,6 +146,21 @@ class FidoManager(
|
||||
args["credentialId"] as String
|
||||
)
|
||||
|
||||
"deleteFingerprint" -> deleteFingerprint(
|
||||
args["templateId"] as String
|
||||
)
|
||||
|
||||
"renameFingerprint" -> renameFingerprint(
|
||||
args["templateId"] as String,
|
||||
args["name"] as String
|
||||
)
|
||||
|
||||
"registerFingerprint" -> registerFingerprint(
|
||||
args["name"] as String?,
|
||||
)
|
||||
|
||||
"cancelRegisterFingerprint" -> cancelRegisterFingerprint()
|
||||
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
@ -215,8 +241,7 @@ class FidoManager(
|
||||
|
||||
fidoViewModel.setSessionState(
|
||||
Session(
|
||||
fidoSession.cachedInfo,
|
||||
pinStore.hasPin()
|
||||
fidoSession.cachedInfo, pinStore.hasPin()
|
||||
)
|
||||
)
|
||||
|
||||
@ -234,12 +259,14 @@ class FidoManager(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPermissions(fidoSession: YubiKitFidoSession): Int {
|
||||
// TODO: Add bio Enrollment permissions if supported
|
||||
private fun getPinPermissionsCM(fidoSession: YubiKitFidoSession): Int {
|
||||
return if (CredentialManagement.isSupported(fidoSession.cachedInfo))
|
||||
ClientPin.PIN_PERMISSION_CM
|
||||
else
|
||||
0
|
||||
ClientPin.PIN_PERMISSION_CM else 0
|
||||
}
|
||||
|
||||
private fun getPinPermissionsBE(fidoSession: YubiKitFidoSession): Int {
|
||||
return if (BioEnrollment.isSupported(fidoSession.cachedInfo))
|
||||
ClientPin.PIN_PERMISSION_BE else 0
|
||||
}
|
||||
|
||||
private fun unlockSession(
|
||||
@ -250,13 +277,21 @@ class FidoManager(
|
||||
|
||||
//fidoViewModel.setSessionLoadingState()
|
||||
|
||||
val permissions = getPermissions(fidoSession)
|
||||
val pinPermissionsCM = getPinPermissionsCM(fidoSession)
|
||||
val pinPermissionsBE = getPinPermissionsBE(fidoSession)
|
||||
val permissions = pinPermissionsCM or pinPermissionsBE
|
||||
|
||||
if (permissions != 0) {
|
||||
val token = clientPin.getPinToken(pin, permissions, null)
|
||||
val credentials = getCredentials(fidoSession, clientPin, token)
|
||||
logger.debug("Creds: {}", credentials)
|
||||
fidoViewModel.updateCredentials(credentials)
|
||||
|
||||
if (pinPermissionsBE != 0) {
|
||||
val fingerprints = getFingerprints(fidoSession, clientPin, token)
|
||||
logger.debug("Fingerprints: {}", fingerprints)
|
||||
fidoViewModel.updateFingerprints(fingerprints)
|
||||
}
|
||||
} else {
|
||||
clientPin.getPinToken(pin, permissions, "yubico-authenticator.example.com")
|
||||
}
|
||||
@ -384,10 +419,8 @@ class FidoManager(
|
||||
val clientPin =
|
||||
ClientPin(fidoSession, getPreferredPinUvAuthProtocol(fidoSession.cachedInfo))
|
||||
|
||||
val permissions = getPermissions(fidoSession)
|
||||
|
||||
val permissions = getPinPermissionsCM(fidoSession)
|
||||
val token = clientPin.getPinToken(pinStore.getPin(), permissions, null)
|
||||
|
||||
val credMan = CredentialManagement(fidoSession, clientPin.pinUvAuth, token)
|
||||
|
||||
val credentialDescriptor =
|
||||
@ -413,6 +446,153 @@ class FidoManager(
|
||||
).toString()
|
||||
}
|
||||
|
||||
private fun getFingerprints(
|
||||
fidoSession: YubiKitFidoSession,
|
||||
clientPin: ClientPin,
|
||||
pinUvAuthToken: ByteArray
|
||||
): List<FidoFingerprint> {
|
||||
val bioEnrollment =
|
||||
FingerprintBioEnrollment(fidoSession, clientPin.pinUvAuth, pinUvAuthToken)
|
||||
|
||||
val enrollments: Map<ByteArray, String?> = bioEnrollment.enumerateEnrollments()
|
||||
return enrollments.map { enrollment ->
|
||||
FidoFingerprint(HexCodec.bytesToHexString(enrollment.key), enrollment.value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private suspend fun deleteFingerprint(templateId: String): String =
|
||||
connectionHelper.useSession(FidoActionDescription.DeleteFingerprint) { fidoSession ->
|
||||
|
||||
val clientPin =
|
||||
ClientPin(fidoSession, getPreferredPinUvAuthProtocol(fidoSession.cachedInfo))
|
||||
|
||||
val token =
|
||||
clientPin.getPinToken(
|
||||
pinStore.getPin(),
|
||||
getPinPermissionsBE(fidoSession),
|
||||
null
|
||||
)
|
||||
|
||||
|
||||
val bioEnrollment = FingerprintBioEnrollment(fidoSession, clientPin.pinUvAuth, token)
|
||||
bioEnrollment.removeEnrollment(HexCodec.hexStringToBytes(templateId))
|
||||
fidoViewModel.removeFingerprint(templateId)
|
||||
fidoViewModel.setSessionState(Session(fidoSession.info, pinStore.hasPin()))
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to true,
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
|
||||
private suspend fun renameFingerprint(templateId: String, name: String): String =
|
||||
connectionHelper.useSession(FidoActionDescription.RenameFingerprint) { fidoSession ->
|
||||
|
||||
val clientPin =
|
||||
ClientPin(fidoSession, getPreferredPinUvAuthProtocol(fidoSession.cachedInfo))
|
||||
|
||||
val token =
|
||||
clientPin.getPinToken(
|
||||
pinStore.getPin(),
|
||||
getPinPermissionsBE(fidoSession),
|
||||
null
|
||||
)
|
||||
|
||||
val bioEnrollment = FingerprintBioEnrollment(fidoSession, clientPin.pinUvAuth, token)
|
||||
bioEnrollment.setName(HexCodec.hexStringToBytes(templateId), name)
|
||||
fidoViewModel.renameFingerprint(templateId, name)
|
||||
fidoViewModel.setSessionState(Session(fidoSession.info, pinStore.hasPin()))
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to true,
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
|
||||
private var state : CommandState? = null
|
||||
private fun cancelRegisterFingerprint(): String {
|
||||
state?.cancel()
|
||||
return NULL
|
||||
}
|
||||
|
||||
private suspend fun registerFingerprint(name: String?): String =
|
||||
connectionHelper.useSession(FidoActionDescription.RegisterFingerprint) { fidoSession ->
|
||||
state?.cancel()
|
||||
state = CommandState()
|
||||
val clientPin =
|
||||
ClientPin(fidoSession, getPreferredPinUvAuthProtocol(fidoSession.cachedInfo))
|
||||
|
||||
val token =
|
||||
clientPin.getPinToken(
|
||||
pinStore.getPin(),
|
||||
getPinPermissionsBE(fidoSession),
|
||||
null
|
||||
)
|
||||
|
||||
val bioEnrollment = FingerprintBioEnrollment(fidoSession, clientPin.pinUvAuth, token)
|
||||
|
||||
val fingerprintEnrollmentContext = bioEnrollment.enroll(null)
|
||||
var templateId: ByteArray? = null
|
||||
while (templateId == null) {
|
||||
try {
|
||||
templateId = fingerprintEnrollmentContext.capture(state)
|
||||
fidoViewModel.updateRegisterFpState(
|
||||
createCaptureEvent(fingerprintEnrollmentContext.remaining!!)
|
||||
)
|
||||
} catch (captureError: FingerprintBioEnrollment.CaptureError) {
|
||||
fidoViewModel.updateRegisterFpState(createCaptureErrorEvent(captureError.code))
|
||||
} catch (ctapException: CtapException) {
|
||||
when (ctapException.ctapError) {
|
||||
CtapException.ERR_KEEPALIVE_CANCEL -> {
|
||||
fingerprintEnrollmentContext.cancel()
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to false,
|
||||
"status" to "user-cancelled"
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
CtapException.ERR_USER_ACTION_TIMEOUT -> {
|
||||
fingerprintEnrollmentContext.cancel()
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to false,
|
||||
"status" to "user-action-timeout"
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
else -> throw ctapException
|
||||
}
|
||||
} catch (io: IOException) {
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to false,
|
||||
"status" to "connection-error"
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.isNullOrBlank()) {
|
||||
bioEnrollment.setName(templateId, name)
|
||||
Logger.debug(logger, "Set name to {}", name)
|
||||
}
|
||||
|
||||
val templateIdHexString = HexCodec.bytesToHexString(templateId)
|
||||
fidoViewModel.addFingerprint(FidoFingerprint(templateIdHexString, name))
|
||||
fidoViewModel.setSessionState(Session(fidoSession.info, pinStore.hasPin()))
|
||||
|
||||
return@useSession JSONObject(
|
||||
mapOf(
|
||||
"success" to true,
|
||||
"template_id" to templateIdHexString,
|
||||
"name" to name
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
|
||||
|
||||
override fun onDisconnected() {
|
||||
if (!resetHelper.inProgress) {
|
||||
fidoViewModel.clearSessionState()
|
||||
|
@ -29,6 +29,8 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.json.JSONObject
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.util.Timer
|
||||
@ -46,6 +48,24 @@ enum class FidoResetState(val value: String) {
|
||||
Touch("touch")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed class FidoRegisterFpEvent(val status: String)
|
||||
|
||||
@Serializable
|
||||
data class FidoRegisterFpCaptureEvent(private val remaining: Int) : FidoRegisterFpEvent("capture")
|
||||
|
||||
@Serializable
|
||||
data class FidoRegisterFpCaptureErrorEvent(val code: Int) : FidoRegisterFpEvent("capture-error")
|
||||
|
||||
|
||||
fun createCaptureEvent(remaining: Int): FidoRegisterFpCaptureEvent {
|
||||
return FidoRegisterFpCaptureEvent(remaining)
|
||||
}
|
||||
|
||||
fun createCaptureErrorEvent(code: Int) : FidoRegisterFpCaptureErrorEvent {
|
||||
return FidoRegisterFpCaptureErrorEvent(code)
|
||||
}
|
||||
|
||||
class FidoResetHelper(
|
||||
private val deviceManager: DeviceManager,
|
||||
private val fidoViewModel: FidoViewModel,
|
||||
|
@ -21,7 +21,9 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.yubico.authenticator.ViewModelData
|
||||
import com.yubico.authenticator.fido.data.FidoCredential
|
||||
import com.yubico.authenticator.fido.data.FidoFingerprint
|
||||
import com.yubico.authenticator.fido.data.Session
|
||||
import org.json.JSONObject
|
||||
|
||||
class FidoViewModel : ViewModel() {
|
||||
private val _sessionState = MutableLiveData<ViewModelData>()
|
||||
@ -60,4 +62,36 @@ class FidoViewModel : ViewModel() {
|
||||
fun updateResetState(resetState: FidoResetState) {
|
||||
_resetState.postValue(resetState.value)
|
||||
}
|
||||
|
||||
private val _fingerprints = MutableLiveData<List<FidoFingerprint>>()
|
||||
val fingerprints: LiveData<List<FidoFingerprint>> = _fingerprints
|
||||
|
||||
fun updateFingerprints(fingerprints: List<FidoFingerprint>) {
|
||||
_fingerprints.postValue(fingerprints)
|
||||
}
|
||||
|
||||
fun addFingerprint(fingerprint: FidoFingerprint) {
|
||||
_fingerprints.postValue(_fingerprints.value?.plus(fingerprint))
|
||||
}
|
||||
|
||||
fun removeFingerprint(templateId: String) {
|
||||
_fingerprints.postValue(_fingerprints.value?.filter {
|
||||
it.templateId != templateId
|
||||
})
|
||||
}
|
||||
|
||||
fun renameFingerprint(templateId: String, name: String) {
|
||||
_fingerprints.postValue(_fingerprints.value?.map {
|
||||
if (it.templateId == templateId) {
|
||||
FidoFingerprint(templateId, name)
|
||||
} else it
|
||||
})
|
||||
}
|
||||
|
||||
private val _registerFingerprint = MutableLiveData<FidoRegisterFpEvent>()
|
||||
val registerFingerprint: LiveData<FidoRegisterFpEvent> = _registerFingerprint
|
||||
|
||||
fun updateRegisterFpState(registerFpState: FidoRegisterFpEvent) {
|
||||
_registerFingerprint.postValue(registerFpState)
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Yubico.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.yubico.authenticator.fido.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FidoFingerprint(
|
||||
@SerialName("template_id")
|
||||
val templateId: String,
|
||||
@SerialName("name")
|
||||
val name: String?
|
||||
)
|
@ -25,6 +25,7 @@ import '../../app/logging.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../desktop/models.dart';
|
||||
import '../../exception/cancellation_exception.dart';
|
||||
import '../../exception/no_data_exception.dart';
|
||||
import '../../exception/platform_exception_decoder.dart';
|
||||
@ -165,14 +166,91 @@ final androidFingerprintProvider = AsyncNotifierProvider.autoDispose
|
||||
_FidoFingerprintsNotifier.new);
|
||||
|
||||
class _FidoFingerprintsNotifier extends FidoFingerprintsNotifier {
|
||||
final _events = const EventChannel('android.fido.fingerprints');
|
||||
late StreamSubscription _sub;
|
||||
|
||||
@override
|
||||
FutureOr<List<Fingerprint>> build(DevicePath devicePath) async {
|
||||
return [];
|
||||
_sub = _events.receiveBroadcastStream().listen((event) {
|
||||
final json = jsonDecode(event);
|
||||
if (json == null) {
|
||||
state = const AsyncValue.loading();
|
||||
} else {
|
||||
List<Fingerprint> newState = List.from(
|
||||
(json as List).map((e) => Fingerprint.fromJson(e)).toList());
|
||||
state = AsyncValue.data(newState);
|
||||
}
|
||||
}, onError: (err, stackTrace) {
|
||||
state = AsyncValue.error(err, stackTrace);
|
||||
});
|
||||
|
||||
ref.onDispose(_sub.cancel);
|
||||
return Completer<List<Fingerprint>>().future;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<FingerprintEvent> registerFingerprint({String? name}) {
|
||||
final controller = StreamController<FingerprintEvent>();
|
||||
const registerEvents = EventChannel('android.fido.registerFp');
|
||||
|
||||
final registerFpSub =
|
||||
registerEvents.receiveBroadcastStream().skip(1).listen((event) {
|
||||
if (controller.isClosed) {
|
||||
_log.debug('Controller already closed, ignoring: $event');
|
||||
}
|
||||
_log.debug('Received register fingerprint event: $event');
|
||||
if (event is String && event.isNotEmpty) {
|
||||
final e = jsonDecode(event);
|
||||
_log.debug('Received register fingerprint event: $e');
|
||||
|
||||
final status = e['status'];
|
||||
|
||||
controller.sink.add(switch (status) {
|
||||
'capture' => FingerprintEvent.capture(e['remaining']),
|
||||
'capture-error' => FingerprintEvent.error(e['code']),
|
||||
final other => throw UnimplementedError(other)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
controller.onCancel = () async {
|
||||
if (!controller.isClosed) {
|
||||
_log.debug('Cancelling fingerprint registration');
|
||||
await _methods.invokeMethod('cancelRegisterFingerprint');
|
||||
await registerFpSub.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
controller.onListen = () async {
|
||||
try {
|
||||
final registerFpResult =
|
||||
await _methods.invokeMethod('registerFingerprint', {'name': name});
|
||||
|
||||
_log.debug('Finished registerFingerprint with: $registerFpResult');
|
||||
|
||||
final resultJson = jsonDecode(registerFpResult);
|
||||
|
||||
if (resultJson['success'] == true) {
|
||||
controller.sink
|
||||
.add(FingerprintEvent.complete(Fingerprint.fromJson(resultJson)));
|
||||
} else {
|
||||
// TODO abstract platform errors
|
||||
final errorStatus = resultJson['status'];
|
||||
if (errorStatus != 'user-cancelled') {
|
||||
throw RpcError(errorStatus, 'Platform error: $errorStatus', {});
|
||||
}
|
||||
}
|
||||
} on PlatformException catch (pe) {
|
||||
_log.debug('Received platform exception: \'$pe\'');
|
||||
final decoded = pe.decode();
|
||||
controller.sink.addError(decoded);
|
||||
} catch (e) {
|
||||
_log.debug('Received error: \'$e\'');
|
||||
controller.sink.addError(e);
|
||||
} finally {
|
||||
await controller.sink.close();
|
||||
}
|
||||
};
|
||||
|
||||
return controller.stream;
|
||||
}
|
||||
@ -180,11 +258,60 @@ class _FidoFingerprintsNotifier extends FidoFingerprintsNotifier {
|
||||
@override
|
||||
Future<Fingerprint> renameFingerprint(
|
||||
Fingerprint fingerprint, String name) async {
|
||||
return fingerprint;
|
||||
try {
|
||||
final renameFingerprintResponse = jsonDecode(await _methods.invokeMethod(
|
||||
'renameFingerprint',
|
||||
{
|
||||
'templateId': fingerprint.templateId,
|
||||
'name': name,
|
||||
},
|
||||
));
|
||||
|
||||
if (renameFingerprintResponse['success'] == true) {
|
||||
_log.debug('FIDO rename fingerprint succeeded');
|
||||
return Fingerprint(fingerprint.templateId, name);
|
||||
} else {
|
||||
_log.debug('FIDO rename fingerprint failed');
|
||||
return fingerprint;
|
||||
}
|
||||
} on PlatformException catch (pe) {
|
||||
var decodedException = pe.decode();
|
||||
if (decodedException is CancellationException) {
|
||||
_log.debug('User cancelled rename fingerprint FIDO operation');
|
||||
} else {
|
||||
_log.error('Rename fingerprint FIDO operation failed.', pe);
|
||||
}
|
||||
|
||||
throw decodedException;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteFingerprint(Fingerprint fingerprint) async {}
|
||||
Future<void> deleteFingerprint(Fingerprint fingerprint) async {
|
||||
try {
|
||||
final deleteFingerprintResponse = jsonDecode(await _methods.invokeMethod(
|
||||
'deleteFingerprint',
|
||||
{
|
||||
'templateId': fingerprint.templateId,
|
||||
},
|
||||
));
|
||||
|
||||
if (deleteFingerprintResponse['success'] == true) {
|
||||
_log.debug('FIDO delete fingerprint succeeded');
|
||||
} else {
|
||||
_log.debug('FIDO delete fingerprint failed');
|
||||
}
|
||||
} on PlatformException catch (pe) {
|
||||
var decodedException = pe.decode();
|
||||
if (decodedException is CancellationException) {
|
||||
_log.debug('User cancelled delete fingerprint FIDO operation');
|
||||
} else {
|
||||
_log.error('Delete fingerprint FIDO operation failed.', pe);
|
||||
}
|
||||
|
||||
throw decodedException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final androidCredentialProvider = AsyncNotifierProvider.autoDispose
|
||||
|
@ -85,12 +85,20 @@ Future<Widget> initialize() async {
|
||||
),
|
||||
androidSdkVersionProvider.overrideWithValue(await getAndroidSdkVersion()),
|
||||
androidNfcSupportProvider.overrideWithValue(await getHasNfc()),
|
||||
supportedSectionsProvider.overrideWithValue(
|
||||
[Section.home, Section.accounts, Section.passkeys]),
|
||||
supportedSectionsProvider.overrideWithValue([
|
||||
Section.home,
|
||||
Section.accounts,
|
||||
Section.fingerprints,
|
||||
Section.passkeys
|
||||
]),
|
||||
// this specifies the priority of sections to show when
|
||||
// the connected YubiKey does not support current section
|
||||
androidSectionPriority.overrideWithValue(
|
||||
[Section.accounts, Section.passkeys, Section.home]),
|
||||
androidSectionPriority.overrideWithValue([
|
||||
Section.accounts,
|
||||
Section.fingerprints,
|
||||
Section.passkeys,
|
||||
Section.home
|
||||
]),
|
||||
supportedThemesProvider.overrideWith(
|
||||
(ref) => ref.watch(androidSupportedThemesProvider),
|
||||
),
|
||||
|
@ -78,6 +78,8 @@ enum _DDesc {
|
||||
fidoUnlockSession,
|
||||
fidoSetPin,
|
||||
fidoDeleteCredential,
|
||||
fidoDeleteFingerprint,
|
||||
fidoRenameFingerprint,
|
||||
fidoActionFailure,
|
||||
// Others
|
||||
invalid;
|
||||
@ -101,7 +103,9 @@ enum _DDesc {
|
||||
dialogDescriptionFidoIndex + 1: fidoUnlockSession,
|
||||
dialogDescriptionFidoIndex + 2: fidoSetPin,
|
||||
dialogDescriptionFidoIndex + 3: fidoDeleteCredential,
|
||||
dialogDescriptionFidoIndex + 4: fidoActionFailure,
|
||||
dialogDescriptionFidoIndex + 4: fidoDeleteFingerprint,
|
||||
dialogDescriptionFidoIndex + 5: fidoRenameFingerprint,
|
||||
dialogDescriptionFidoIndex + 6: fidoActionFailure,
|
||||
}[id] ??
|
||||
_DDesc.invalid;
|
||||
}
|
||||
@ -179,6 +183,8 @@ class _DialogProvider {
|
||||
_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.fidoDeleteFingerprint => l10n.s_nfc_dialog_fido_delete_fingerprint,
|
||||
_DDesc.fidoRenameFingerprint => l10n.s_nfc_dialog_fido_rename_fingerprint,
|
||||
_DDesc.fidoActionFailure => l10n.s_nfc_dialog_fido_failure,
|
||||
_ => ''
|
||||
};
|
||||
|
@ -63,6 +63,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
|
||||
void dispose() {
|
||||
_animator.dispose();
|
||||
_nameFocus.dispose();
|
||||
_subscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -838,6 +838,8 @@
|
||||
"s_nfc_dialog_fido_unlock": null,
|
||||
"l_nfc_dialog_fido_set_pin": null,
|
||||
"s_nfc_dialog_fido_delete_credential": null,
|
||||
"s_nfc_dialog_fido_delete_fingerprint": null,
|
||||
"s_nfc_dialog_fido_rename_fingerprint": null,
|
||||
"s_nfc_dialog_fido_failure": null,
|
||||
|
||||
"@_ndef": {},
|
||||
|
@ -838,6 +838,8 @@
|
||||
"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_delete_fingerprint": "Action: delete fingerprint",
|
||||
"s_nfc_dialog_fido_rename_fingerprint": "Action: rename fingerprint",
|
||||
"s_nfc_dialog_fido_failure": "FIDO operation failed",
|
||||
|
||||
"@_ndef": {},
|
||||
|
@ -838,6 +838,8 @@
|
||||
"s_nfc_dialog_fido_unlock": null,
|
||||
"l_nfc_dialog_fido_set_pin": null,
|
||||
"s_nfc_dialog_fido_delete_credential": null,
|
||||
"s_nfc_dialog_fido_delete_fingerprint": null,
|
||||
"s_nfc_dialog_fido_rename_fingerprint": null,
|
||||
"s_nfc_dialog_fido_failure": null,
|
||||
|
||||
"@_ndef": {},
|
||||
|
@ -838,6 +838,8 @@
|
||||
"s_nfc_dialog_fido_unlock": null,
|
||||
"l_nfc_dialog_fido_set_pin": null,
|
||||
"s_nfc_dialog_fido_delete_credential": null,
|
||||
"s_nfc_dialog_fido_delete_fingerprint": null,
|
||||
"s_nfc_dialog_fido_rename_fingerprint": null,
|
||||
"s_nfc_dialog_fido_failure": null,
|
||||
|
||||
"@_ndef": {},
|
||||
|
@ -838,6 +838,8 @@
|
||||
"s_nfc_dialog_fido_unlock": null,
|
||||
"l_nfc_dialog_fido_set_pin": null,
|
||||
"s_nfc_dialog_fido_delete_credential": null,
|
||||
"s_nfc_dialog_fido_delete_fingerprint": null,
|
||||
"s_nfc_dialog_fido_rename_fingerprint": null,
|
||||
"s_nfc_dialog_fido_failure": null,
|
||||
|
||||
"@_ndef": {},
|
||||
|
Loading…
Reference in New Issue
Block a user