From 427b84f3dda1fac5e6d97bcf93805f4c8086fb06 Mon Sep 17 00:00:00 2001 From: Chralu Date: Tue, 26 Nov 2024 15:25:25 +0100 Subject: [PATCH] fix: :bug: Fix Yubikey setup. --- .../authentication/authentication.g.dart | 6 +- lib/application/authentication/password.dart | 3 +- lib/application/authentication/pin.dart | 3 +- lib/application/authentication/yubikey.dart | 9 +- lib/domain/models/authentication.dart | 9 + lib/domain/models/authentication.freezed.dart | 162 ++++++++++++++++++ .../authenticate_with_yubikey.dart | 28 ++- .../authenticate/set_yubikey_screen.dart | 5 + lib/ui/views/authenticate/yubikey_screen.dart | 7 +- .../tasks_notification_widget.freezed.dart | 24 ++- 10 files changed, 230 insertions(+), 26 deletions(-) diff --git a/lib/application/authentication/authentication.g.dart b/lib/application/authentication/authentication.g.dart index 9d26426d8..8e45d4aa9 100644 --- a/lib/application/authentication/authentication.g.dart +++ b/lib/application/authentication/authentication.g.dart @@ -118,7 +118,7 @@ final _authenticationGuardNotifierProvider = AsyncNotifierProvider< typedef _$AuthenticationGuardNotifier = AsyncNotifier; String _$passwordAuthenticationNotifierHash() => - r'02c3614561a3b1e763942a19d0a7260e45d3575f'; + r'd2ffeb5265f757c02c49978b72c30d9fdfcbbeaf'; /// See also [_PasswordAuthenticationNotifier]. @ProviderFor(_PasswordAuthenticationNotifier) @@ -137,7 +137,7 @@ final _passwordAuthenticationNotifierProvider = typedef _$PasswordAuthenticationNotifier = AutoDisposeAsyncNotifier; String _$pinAuthenticationNotifierHash() => - r'c29c13f584bf162b43e00e3b314fcf57dc988010'; + r'8e48abf3e4d6e928e6879133540c27a2b0695f9d'; /// See also [_PinAuthenticationNotifier]. @ProviderFor(_PinAuthenticationNotifier) @@ -172,7 +172,7 @@ final _authenticationSettingsNotifierProvider = NotifierProvider< typedef _$AuthenticationSettingsNotifier = Notifier; String _$yubikeyAuthenticationNotifierHash() => - r'87286dd0244e5680fd079da0f7140c62b8da7462'; + r'447a9823bd245f81c8a2da69e07564a10ea96d0f'; /// See also [_YubikeyAuthenticationNotifier]. @ProviderFor(_YubikeyAuthenticationNotifier) diff --git a/lib/application/authentication/password.dart b/lib/application/authentication/password.dart index 55482833e..bb2f4a4c6 100644 --- a/lib/application/authentication/password.dart +++ b/lib/application/authentication/password.dart @@ -30,8 +30,7 @@ class _PasswordAuthenticationNotifier extends _$PasswordAuthenticationNotifier { Future authenticateWithPassword( PasswordCredentials password, ) async { - final lState = state.value; - if (lState == null) return const AuthenticationResult.notSetup(); + final lState = await future; final authenticationRepository = ref.read( AuthenticationProviders.authenticationRepository, diff --git a/lib/application/authentication/pin.dart b/lib/application/authentication/pin.dart index a14bbb5bc..f6cbdc627 100644 --- a/lib/application/authentication/pin.dart +++ b/lib/application/authentication/pin.dart @@ -30,8 +30,7 @@ class _PinAuthenticationNotifier extends _$PinAuthenticationNotifier { Future decodeWithPin( PinCredentials pin, ) async { - final lState = state.value; - if (lState == null) return const AuthenticationResult.notSetup(); + final lState = await future; final authenticationRepository = ref.read( AuthenticationProviders.authenticationRepository, diff --git a/lib/application/authentication/yubikey.dart b/lib/application/authentication/yubikey.dart index 99ad8d348..3934e47fa 100644 --- a/lib/application/authentication/yubikey.dart +++ b/lib/application/authentication/yubikey.dart @@ -28,16 +28,17 @@ class _YubikeyAuthenticationNotifier extends _$YubikeyAuthenticationNotifier { } Future authenticateWithYubikey( - YubikeyCredentials otp, - ) async { - final lState = state.value; - if (lState == null) return const AuthenticationResult.notSetup(); + YubikeyCredentials otp, { + YubikeyOTPSettings? settings, + }) async { + final lState = await future; final authenticationRepository = ref.read( AuthenticationProviders.authenticationRepository, ); final authenticationResult = await AuthenticateWithYubikey( repository: authenticationRepository, + settings: settings, ).run(otp); authenticationResult.maybeMap( diff --git a/lib/domain/models/authentication.dart b/lib/domain/models/authentication.dart index ff4074713..bbe2ddf5b 100644 --- a/lib/domain/models/authentication.dart +++ b/lib/domain/models/authentication.dart @@ -46,3 +46,12 @@ class AuthenticationSettings with _$AuthenticationSettings { : PrivacyMaskOption.disabled, ); } + +@freezed +class YubikeyOTPSettings with _$YubikeyOTPSettings { + const factory YubikeyOTPSettings({ + required String clientId, + required String clientApiKey, + }) = _YubikeyOTPSettings; + const YubikeyOTPSettings._(); +} diff --git a/lib/domain/models/authentication.freezed.dart b/lib/domain/models/authentication.freezed.dart index 86e1f60f5..4dc680ef1 100644 --- a/lib/domain/models/authentication.freezed.dart +++ b/lib/domain/models/authentication.freezed.dart @@ -846,3 +846,165 @@ abstract class _AuthenticationSettings extends AuthenticationSettings { _$$AuthenticationSettingsImplCopyWith<_$AuthenticationSettingsImpl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +mixin _$YubikeyOTPSettings { + String get clientId => throw _privateConstructorUsedError; + String get clientApiKey => throw _privateConstructorUsedError; + + /// Create a copy of YubikeyOTPSettings + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $YubikeyOTPSettingsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $YubikeyOTPSettingsCopyWith<$Res> { + factory $YubikeyOTPSettingsCopyWith( + YubikeyOTPSettings value, $Res Function(YubikeyOTPSettings) then) = + _$YubikeyOTPSettingsCopyWithImpl<$Res, YubikeyOTPSettings>; + @useResult + $Res call({String clientId, String clientApiKey}); +} + +/// @nodoc +class _$YubikeyOTPSettingsCopyWithImpl<$Res, $Val extends YubikeyOTPSettings> + implements $YubikeyOTPSettingsCopyWith<$Res> { + _$YubikeyOTPSettingsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of YubikeyOTPSettings + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? clientId = null, + Object? clientApiKey = null, + }) { + return _then(_value.copyWith( + clientId: null == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String, + clientApiKey: null == clientApiKey + ? _value.clientApiKey + : clientApiKey // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$YubikeyOTPSettingsImplCopyWith<$Res> + implements $YubikeyOTPSettingsCopyWith<$Res> { + factory _$$YubikeyOTPSettingsImplCopyWith(_$YubikeyOTPSettingsImpl value, + $Res Function(_$YubikeyOTPSettingsImpl) then) = + __$$YubikeyOTPSettingsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String clientId, String clientApiKey}); +} + +/// @nodoc +class __$$YubikeyOTPSettingsImplCopyWithImpl<$Res> + extends _$YubikeyOTPSettingsCopyWithImpl<$Res, _$YubikeyOTPSettingsImpl> + implements _$$YubikeyOTPSettingsImplCopyWith<$Res> { + __$$YubikeyOTPSettingsImplCopyWithImpl(_$YubikeyOTPSettingsImpl _value, + $Res Function(_$YubikeyOTPSettingsImpl) _then) + : super(_value, _then); + + /// Create a copy of YubikeyOTPSettings + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? clientId = null, + Object? clientApiKey = null, + }) { + return _then(_$YubikeyOTPSettingsImpl( + clientId: null == clientId + ? _value.clientId + : clientId // ignore: cast_nullable_to_non_nullable + as String, + clientApiKey: null == clientApiKey + ? _value.clientApiKey + : clientApiKey // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$YubikeyOTPSettingsImpl extends _YubikeyOTPSettings + with DiagnosticableTreeMixin { + const _$YubikeyOTPSettingsImpl( + {required this.clientId, required this.clientApiKey}) + : super._(); + + @override + final String clientId; + @override + final String clientApiKey; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'YubikeyOTPSettings(clientId: $clientId, clientApiKey: $clientApiKey)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'YubikeyOTPSettings')) + ..add(DiagnosticsProperty('clientId', clientId)) + ..add(DiagnosticsProperty('clientApiKey', clientApiKey)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$YubikeyOTPSettingsImpl && + (identical(other.clientId, clientId) || + other.clientId == clientId) && + (identical(other.clientApiKey, clientApiKey) || + other.clientApiKey == clientApiKey)); + } + + @override + int get hashCode => Object.hash(runtimeType, clientId, clientApiKey); + + /// Create a copy of YubikeyOTPSettings + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$YubikeyOTPSettingsImplCopyWith<_$YubikeyOTPSettingsImpl> get copyWith => + __$$YubikeyOTPSettingsImplCopyWithImpl<_$YubikeyOTPSettingsImpl>( + this, _$identity); +} + +abstract class _YubikeyOTPSettings extends YubikeyOTPSettings { + const factory _YubikeyOTPSettings( + {required final String clientId, + required final String clientApiKey}) = _$YubikeyOTPSettingsImpl; + const _YubikeyOTPSettings._() : super._(); + + @override + String get clientId; + @override + String get clientApiKey; + + /// Create a copy of YubikeyOTPSettings + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$YubikeyOTPSettingsImplCopyWith<_$YubikeyOTPSettingsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/usecases/authentication/authenticate_with_yubikey.dart b/lib/domain/usecases/authentication/authenticate_with_yubikey.dart index b53cab51a..a968815db 100644 --- a/lib/domain/usecases/authentication/authenticate_with_yubikey.dart +++ b/lib/domain/usecases/authentication/authenticate_with_yubikey.dart @@ -5,28 +5,44 @@ class AuthenticateWithYubikey implements UseCase { const AuthenticateWithYubikey({ required this.repository, + this.settings, }); @override final AuthenticationRepositoryInterface repository; + final YubikeyOTPSettings? settings; + static int get maxFailedAttempts => AuthenticationWithLock.maxFailedAttempts; + Future get _settings async { + if (settings != null) return settings; + + final vault = await AuthentHiveSecuredDatasource.getInstance(); + + final clientApiKey = vault.getYubikeyClientAPIKey(); + final clientId = vault.getYubikeyClientID(); + if (clientId.isEmpty || clientApiKey.isEmpty) { + return null; + } + return YubikeyOTPSettings( + clientId: clientId, + clientApiKey: clientApiKey, + ); + } + @override Future run( YubikeyCredentials credentials, { UseCaseProgressListener? onProgress, }) async { - final vault = await AuthentHiveSecuredDatasource.getInstance(); - - final yubikeyClientAPIKey = vault.getYubikeyClientAPIKey(); - final yubikeyClientID = vault.getYubikeyClientID(); - if (yubikeyClientID.isEmpty && yubikeyClientAPIKey.isEmpty) { + final settings = await _settings; + if (settings == null) { return const AuthenticationResult.notSetup(); } final verificationResponse = await Yubidart() .otp - .verify(credentials.otp, yubikeyClientAPIKey, yubikeyClientID); + .verify(credentials.otp, settings.clientApiKey, settings.clientId); if (verificationResponse.status == 'OK') { return authenticationSucceed(credentials.challenge); diff --git a/lib/ui/views/authenticate/set_yubikey_screen.dart b/lib/ui/views/authenticate/set_yubikey_screen.dart index ffee6296f..bbf7fd946 100644 --- a/lib/ui/views/authenticate/set_yubikey_screen.dart +++ b/lib/ui/views/authenticate/set_yubikey_screen.dart @@ -1,5 +1,6 @@ /// SPDX-License-Identifier: AGPL-3.0-or-later import 'package:aewallet/application/authentication/authentication.dart'; +import 'package:aewallet/domain/models/authentication.dart'; import 'package:aewallet/ui/themes/archethic_theme.dart'; import 'package:aewallet/ui/themes/styles.dart'; import 'package:aewallet/ui/util/dimens.dart'; @@ -339,6 +340,10 @@ class _SetYubikeyState extends ConsumerState final auth = await YubikeyAuthScreenOverlay( canNavigateBack: true, challenge: widget.challenge, + settings: YubikeyOTPSettings( + clientId: clientId, + clientApiKey: clientApiKey, + ), ).show( context, ); diff --git a/lib/ui/views/authenticate/yubikey_screen.dart b/lib/ui/views/authenticate/yubikey_screen.dart index e09a6d99c..856e346b0 100644 --- a/lib/ui/views/authenticate/yubikey_screen.dart +++ b/lib/ui/views/authenticate/yubikey_screen.dart @@ -31,12 +31,14 @@ class YubikeyAuthScreenOverlay extends AuthScreenOverlay { YubikeyAuthScreenOverlay({ required bool canNavigateBack, required Uint8List challenge, + YubikeyOTPSettings? settings, }) : super( name: 'YubikeyScreenOverlay', widgetBuilder: (context, onDone) => _YubikeyScreen( canNavigateBack: canNavigateBack, challenge: challenge, onDone: onDone, + settings: settings, ), ); } @@ -46,11 +48,13 @@ class _YubikeyScreen extends ConsumerStatefulWidget { required this.canNavigateBack, required this.challenge, required this.onDone, + this.settings, }); final void Function(Uint8List? result) onDone; final bool canNavigateBack; final Uint8List challenge; + final YubikeyOTPSettings? settings; @override ConsumerState<_YubikeyScreen> createState() => _YubikeyScreenState(); @@ -108,7 +112,8 @@ class _YubikeyScreenState extends ConsumerState<_YubikeyScreen> AuthenticationProviders.yubikeyAuthentication.notifier, ) .authenticateWithYubikey( - YubikeyCredentials(otp: otp, challenge: Uint8List(2)), + YubikeyCredentials(otp: otp, challenge: widget.challenge), + settings: widget.settings, ); final localizations = AppLocalizations.of(context)!; diff --git a/lib/ui/views/notifications/layouts/tasks_notification_widget.freezed.dart b/lib/ui/views/notifications/layouts/tasks_notification_widget.freezed.dart index 1b2571f60..8a882402e 100644 --- a/lib/ui/views/notifications/layouts/tasks_notification_widget.freezed.dart +++ b/lib/ui/views/notifications/layouts/tasks_notification_widget.freezed.dart @@ -72,7 +72,7 @@ class _$TaskNotificationPopupCopyWithImpl<$Res, Object? actionType = null, Object? action = freezed, Object? onActionPressed = freezed, - Object? progressIndicatorColor = null, + Object? progressIndicatorColor = freezed, }) { return _then(_value.copyWith( key: freezed == key @@ -103,7 +103,7 @@ class _$TaskNotificationPopupCopyWithImpl<$Res, ? _value.onActionPressed : onActionPressed // ignore: cast_nullable_to_non_nullable as VoidCallback?, - progressIndicatorColor: null == progressIndicatorColor + progressIndicatorColor: freezed == progressIndicatorColor ? _value.progressIndicatorColor : progressIndicatorColor // ignore: cast_nullable_to_non_nullable as Color, @@ -152,7 +152,7 @@ class __$$TaskNotificationPopupImplCopyWithImpl<$Res> Object? actionType = null, Object? action = freezed, Object? onActionPressed = freezed, - Object? progressIndicatorColor = null, + Object? progressIndicatorColor = freezed, }) { return _then(_$TaskNotificationPopupImpl( key: freezed == key @@ -183,7 +183,7 @@ class __$$TaskNotificationPopupImplCopyWithImpl<$Res> ? _value.onActionPressed : onActionPressed // ignore: cast_nullable_to_non_nullable as VoidCallback?, - progressIndicatorColor: null == progressIndicatorColor + progressIndicatorColor: freezed == progressIndicatorColor ? _value.progressIndicatorColor : progressIndicatorColor // ignore: cast_nullable_to_non_nullable as Color, @@ -242,13 +242,21 @@ class _$TaskNotificationPopupImpl extends _TaskNotificationPopup { (identical(other.action, action) || other.action == action) && (identical(other.onActionPressed, onActionPressed) || other.onActionPressed == onActionPressed) && - (identical(other.progressIndicatorColor, progressIndicatorColor) || - other.progressIndicatorColor == progressIndicatorColor)); + const DeepCollectionEquality() + .equals(other.progressIndicatorColor, progressIndicatorColor)); } @override - int get hashCode => Object.hash(runtimeType, key, icon, title, description, - actionType, action, onActionPressed, progressIndicatorColor); + int get hashCode => Object.hash( + runtimeType, + key, + icon, + title, + description, + actionType, + action, + onActionPressed, + const DeepCollectionEquality().hash(progressIndicatorColor)); /// Create a copy of TaskNotificationPopup /// with the given fields replaced by the non-null parameter values.