Skip to content

Commit f711d45

Browse files
committed
chore: Improved the master password unlock method.
1 parent 2897fee commit f711d45

3 files changed

Lines changed: 34 additions & 16 deletions

File tree

lib/model/app_unlock/methods/local_auth.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class LocalAuthenticationAppUnlockMethod extends AppUnlockMethod {
1919
}
2020

2121
@override
22-
Future<CannotUnlockException?> canUnlock() async {
22+
Future<CannotUnlockException?> canUnlock(UnlockReason reason) async {
2323
if (!(await LocalAuthentication.instance.isSupported())) {
2424
return LocalAuthenticationDeviceNotSupported();
2525
}

lib/model/app_unlock/methods/master_password.dart

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,45 @@ class MasterPasswordAppUnlockMethod extends AppUnlockMethod<String> {
5555
}
5656

5757
@override
58-
Future<CannotUnlockException?> canUnlock() async {
59-
Salt? salt = await Salt.readFromLocalStorage();
60-
if (salt == null) {
61-
try {
62-
List<Totp> totps = await _ref.read(totpRepositoryProvider.future);
63-
salt = totps.firstOrNull?.encryptedData.encryptionSalt;
64-
if (salt == null) {
65-
return MasterPasswordNoSalt();
66-
}
67-
await salt.saveToLocalStorage();
68-
} catch (ex, stackTrace) {
69-
handleException(ex, stackTrace);
70-
return MasterPasswordNoSalt();
58+
Future<CannotUnlockException?> canUnlock(UnlockReason reason) async {
59+
if (reason != .openApp && reason != .sensibleAction) {
60+
List<Totp> totps = await _ref.read(totpRepositoryProvider.future);
61+
if (totps.isEmpty) {
62+
return null;
7163
}
7264
}
65+
if (!(await _ensureSaltAvailable())) {
66+
return MasterPasswordNoSalt();
67+
}
7368
List<PasswordVerificationMethod> passwordVerificationMethods = await _ref.read(passwordVerificationProvider.future);
7469
if (passwordVerificationMethods.isEmpty) {
7570
return MasterPasswordNoPasswordVerificationMethodAvailable();
7671
}
7772
return null;
7873
}
7974

75+
/// Ensures that the local salt exists, restoring it from the TOTP list when possible.
76+
Future<bool> _ensureSaltAvailable() async {
77+
if (await Salt.readFromLocalStorage() != null) {
78+
return true;
79+
}
80+
try {
81+
List<Totp> totps = await _ref.read(totpRepositoryProvider.future);
82+
Salt? salt = totps.firstOrNull?.encryptedData.encryptionSalt;
83+
if (salt == null) {
84+
return false;
85+
}
86+
await salt.saveToLocalStorage();
87+
_ref.invalidate(cryptoStoreProvider);
88+
_ref.invalidate(cryptoStoreVerificationMethodProvider);
89+
_ref.invalidate(passwordVerificationProvider);
90+
return true;
91+
} catch (ex, stackTrace) {
92+
handleException(ex, stackTrace);
93+
return false;
94+
}
95+
}
96+
8097
/// Prompts master password for unlock.
8198
Future<Result<String>> _promptMasterPasswordForUnlock(BuildContext context, String? message) async {
8299
String? password = await MasterPasswordInputDialog.prompt(

lib/model/app_unlock/methods/method.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:open_authenticator/i18n/localizable_exception.dart';
55
import 'package:open_authenticator/i18n/translations.g.dart';
66
import 'package:open_authenticator/model/app_unlock/reason.dart';
77
import 'package:open_authenticator/model/crypto.dart';
8+
import 'package:open_authenticator/model/password_verification/methods/crypto_store.dart';
89
import 'package:open_authenticator/model/password_verification/methods/method.dart';
910
import 'package:open_authenticator/model/password_verification/methods/password_signature.dart';
1011
import 'package:open_authenticator/model/password_verification/password_verification.dart';
@@ -47,7 +48,7 @@ sealed class AppUnlockMethod<T> {
4748
/// [context] is required so that we can interact with the user.
4849
Future<Result<T>> unlock(BuildContext context, UnlockReason reason) async {
4950
try {
50-
CannotUnlockException? cannotUnlockException = await canUnlock();
51+
CannotUnlockException? cannotUnlockException = await canUnlock(reason);
5152
if (cannotUnlockException != null) {
5253
throw cannotUnlockException;
5354
}
@@ -76,7 +77,7 @@ sealed class AppUnlockMethod<T> {
7677
AppLockState get defaultAppLockState => AppLockState.locked;
7778

7879
/// Returns whether the app can be unlocked using this method.
79-
Future<CannotUnlockException?> canUnlock() => Future.value(null);
80+
Future<CannotUnlockException?> canUnlock(UnlockReason reason) => Future.value(null);
8081

8182
/// Triggered when this method has been chosen has the app unlock method.
8283
/// [unlockResult] is the result of the [tryUnlock] call.

0 commit comments

Comments
 (0)