Skip to content

Commit

Permalink
fix: 🐛 Fix Yubikey setup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chralu committed Nov 26, 2024
1 parent 7889699 commit 427b84f
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 26 deletions.
6 changes: 3 additions & 3 deletions lib/application/authentication/authentication.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions lib/application/authentication/password.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class _PasswordAuthenticationNotifier extends _$PasswordAuthenticationNotifier {
Future<AuthenticationResult> 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,
Expand Down
3 changes: 1 addition & 2 deletions lib/application/authentication/pin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class _PinAuthenticationNotifier extends _$PinAuthenticationNotifier {
Future<AuthenticationResult> 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,
Expand Down
9 changes: 5 additions & 4 deletions lib/application/authentication/yubikey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ class _YubikeyAuthenticationNotifier extends _$YubikeyAuthenticationNotifier {
}

Future<AuthenticationResult> 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(
Expand Down
9 changes: 9 additions & 0 deletions lib/domain/models/authentication.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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._();
}
162 changes: 162 additions & 0 deletions lib/domain/models/authentication.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<YubikeyOTPSettings> 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;
}
28 changes: 22 additions & 6 deletions lib/domain/usecases/authentication/authenticate_with_yubikey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,44 @@ class AuthenticateWithYubikey
implements UseCase<YubikeyCredentials, AuthenticationResult> {
const AuthenticateWithYubikey({
required this.repository,
this.settings,
});

@override
final AuthenticationRepositoryInterface repository;

final YubikeyOTPSettings? settings;

static int get maxFailedAttempts => AuthenticationWithLock.maxFailedAttempts;

Future<YubikeyOTPSettings?> 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<AuthenticationResult> 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);
Expand Down
5 changes: 5 additions & 0 deletions lib/ui/views/authenticate/set_yubikey_screen.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -339,6 +340,10 @@ class _SetYubikeyState extends ConsumerState<SetYubikey>
final auth = await YubikeyAuthScreenOverlay(
canNavigateBack: true,
challenge: widget.challenge,
settings: YubikeyOTPSettings(
clientId: clientId,
clientApiKey: clientApiKey,
),
).show(
context,
);
Expand Down
7 changes: 6 additions & 1 deletion lib/ui/views/authenticate/yubikey_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
);
}
Expand All @@ -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();
Expand Down Expand Up @@ -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)!;
Expand Down
Loading

0 comments on commit 427b84f

Please sign in to comment.