Skip to content

Commit 2c10b16

Browse files
authored
feat: Initial implementation of Open Authenticator for linux.
Without Firebase.
2 parents 670c8fb + 075e22e commit 2c10b16

34 files changed

+697
-223
lines changed

lib/i18n/de/local_auth.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"lockOut": "Die biometrische Authentifizierung ist deaktiviert. Bitte sperren und entsperren Sie Ihren Bildschirm, um sie zu aktivieren.",
1616
"goToSettingsDescription": "Die biometrische Authentifizierung ist auf Ihrem Gerät nicht eingerichtet. Bitte aktivieren Sie entweder Touch ID oder Face ID in den Einstellungen Ihres Geräts."
1717
},
18+
"methodChannel": {
19+
"title": "Authentifizierung erforderlich",
20+
"wrongPassword": "Falsches Passwort. Versuchen Sie es erneut."
21+
},
1822
"fallbackMessage": "Beim Überprüfen der lokalen Authentifizierung auf Ihrem Gerät ist ein Fehler aufgetreten. Bitte geben Sie unten Ihr Master-Passwort ein, um fortzufahren und die lokale Authentifizierung zu deaktivieren."
1923
}

lib/i18n/en/local_auth.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"lockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.",
1616
"goToSettingsDescription": "Biometric authentication is not set up on your device. Please either enable Touch ID or Face ID on your phone."
1717
},
18+
"methodChannel": {
19+
"title": "Authentication required",
20+
"wrongPassword": "Wrong password. Try again."
21+
},
1822
"fallbackMessage": "An error occurred while checking for local authentication on your device. Please enter your master password below to proceed and to disable local authentication as well."
1923
}

lib/i18n/fr/local_auth.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"lockOut": "L'authentification par biométrie est désactivée. Veuillez verrouiller puis déverrouiller votre appareil pour l'activer.",
1616
"goToSettingsDescription": "L'authentification par biométrie n'est pas configurée sur votre appareil. Veuillez activer Touch ID ou Face ID sur votre téléphone."
1717
},
18+
"methodChannel": {
19+
"title": "Authentification requise",
20+
"wrongPassword": "Mauvais mot de passe. Veuillez réessayer."
21+
},
1822
"fallbackMessage": "Une erreur est survenue pendant la vérification de l'authentification locale. Veuillez entrer votre mot de passe maître ci-dessous pour continuer et pour désactiver l'authentification locale."
1923
}

lib/i18n/it/local_auth.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"lockOut": "L'autenticazione biometrica è disabilitata. Blocca e sblocca lo schermo per abilitarla.",
1616
"goToSettingsDescription": "L'autenticazione biometrica non è configurata sul tuo dispositivo. Abilita Touch ID o Face ID sul tuo telefono."
1717
},
18+
"methodChannel": {
19+
"title": "Autenticazione necessaria",
20+
"wrongPassword": "Password sbagliata. Riprovare."
21+
},
1822
"fallbackMessage": "Si è verificato un errore durante il controllo dell'autenticazione locale sul tuo dispositivo. Inserisci la tua password principale qui sotto per procedere e disabilitare anche l'autenticazione locale."
1923
}

lib/i18n/pt/local_auth.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"lockOut": "A autenticação biométrica está desativada. Bloqueie e desbloqueie a tela para ativá-la.",
1616
"goToSettingsDescription": "A autenticação biométrica não está configurada no seu dispositivo. Ative o Touch ID ou Face ID no seu telefone."
1717
},
18+
"methodChannel": {
19+
"title": "Autenticação necessária",
20+
"wrongPassword": "Palavra-passe errada. Tente novamente."
21+
},
1822
"fallbackMessage": "Ocorreu um erro ao verificar a autenticação local no seu dispositivo. Insira sua senha mestra abaixo para continuar e também desativar a autenticação local."
1923
}

lib/main.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import 'package:open_authenticator/pages/scan.dart';
2424
import 'package:open_authenticator/pages/settings/page.dart';
2525
import 'package:open_authenticator/pages/totp.dart';
2626
import 'package:open_authenticator/utils/account.dart';
27+
import 'package:open_authenticator/utils/firebase.dart';
2728
import 'package:open_authenticator/utils/firebase_app_check/firebase_app_check.dart';
28-
import 'package:open_authenticator/utils/firebase_crashlytics.dart';
2929
import 'package:open_authenticator/utils/platform.dart';
3030
import 'package:open_authenticator/utils/rate_my_app.dart';
3131
import 'package:open_authenticator/utils/result.dart';
@@ -40,6 +40,7 @@ import 'package:window_manager/window_manager.dart';
4040
/// Hello world !
4141
Future<void> main() async {
4242
WidgetsFlutterBinding.ensureInitialized();
43+
await SimpleSecureStorage.initialize(_OpenAuthenticatorSSSInitializationOptions());
4344
if (currentPlatform.isDesktop) {
4445
await windowManager.ensureInitialized();
4546
windowManager.waitUntilReadyToShow(
@@ -53,16 +54,17 @@ Future<void> main() async {
5354
await windowManager.focus();
5455
});
5556
}
56-
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
57-
await FirebaseAppCheck.instance.activate();
58-
if (FirebaseCrashlytics.instance.shouldEnable) {
59-
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
60-
PlatformDispatcher.instance.onError = (error, stack) {
61-
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
62-
return true;
63-
};
57+
if (isFirebaseSupported) {
58+
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
59+
await FirebaseAppCheck.instance.activate();
60+
if (isCrashlyticsEnabled) {
61+
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
62+
PlatformDispatcher.instance.onError = (error, stack) {
63+
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
64+
return true;
65+
};
66+
}
6467
}
65-
await SimpleSecureStorage.initialize(_OpenAuthenticatorSSSInitializationOptions());
6668
await LocaleSettings.useDeviceLocale();
6769
runApp(
6870
ProviderScope(

lib/model/app_unlock/method.dart

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_riverpod/flutter_riverpod.dart';
3-
import 'package:local_auth/local_auth.dart';
4-
// ignore: depend_on_referenced_packages
5-
import 'package:local_auth_android/local_auth_android.dart';
6-
// ignore: depend_on_referenced_packages
7-
import 'package:local_auth_darwin/local_auth_darwin.dart';
8-
// ignore: depend_on_referenced_packages
9-
import 'package:local_auth_windows/local_auth_windows.dart';
103
import 'package:open_authenticator/i18n/translations.g.dart';
114
import 'package:open_authenticator/model/crypto.dart';
125
import 'package:open_authenticator/model/password_verification/methods/method.dart';
136
import 'package:open_authenticator/model/password_verification/methods/password_signature.dart';
147
import 'package:open_authenticator/model/password_verification/password_verification.dart';
158
import 'package:open_authenticator/model/totp/repository.dart';
16-
import 'package:open_authenticator/utils/platform.dart';
9+
import 'package:open_authenticator/utils/local_authentication/local_authentication.dart';
1710
import 'package:open_authenticator/utils/result.dart';
1811
import 'package:open_authenticator/widgets/dialog/text_input_dialog.dart';
19-
import 'package:window_manager/window_manager.dart';
2012

2113
/// Allows to unlock the app.
2214
sealed class AppUnlockMethod<T> {
@@ -77,60 +69,17 @@ sealed class CannotUnlockException implements Exception {}
7769
class LocalAuthenticationAppUnlockMethod extends AppUnlockMethod {
7870
@override
7971
Future<Result> _tryUnlock(BuildContext context, Ref ref, UnlockReason reason) async {
80-
LocalAuthentication auth = LocalAuthentication();
81-
if (currentPlatform.isDesktop) {
82-
await windowManager.ensureInitialized();
83-
await windowManager.focus();
84-
await windowManager.setAlwaysOnTop(true);
85-
}
86-
if (!context.mounted) {
87-
return const ResultCancelled();
88-
}
89-
bool result = await auth.authenticate(
90-
localizedReason: translations.appUnlock.localAuthentication[reason.name] ?? 'Authenticate to access the app.',
91-
authMessages: [
92-
IOSAuthMessages(
93-
lockOut: translations.localAuth.ios.lockOut,
94-
goToSettingsButton: translations.localAuth.common.goToSettings,
95-
goToSettingsDescription: translations.localAuth.ios.goToSettingsDescription,
96-
cancelButton: MaterialLocalizations.of(context).cancelButtonLabel,
97-
),
98-
AndroidAuthMessages(
99-
biometricHint: translations.localAuth.android.biometricHint,
100-
biometricNotRecognized: translations.localAuth.android.biometricNotRecognized,
101-
biometricRequiredTitle: translations.localAuth.android.biometricRequiredTitle,
102-
biometricSuccess: translations.error.noError,
103-
cancelButton: MaterialLocalizations.of(context).cancelButtonLabel,
104-
deviceCredentialsRequiredTitle: translations.localAuth.android.deviceCredentialsRequiredTitle,
105-
deviceCredentialsSetupDescription: translations.localAuth.android.deviceCredentialsSetupDescription,
106-
goToSettingsButton: translations.localAuth.common.goToSettings,
107-
goToSettingsDescription: translations.localAuth.android.goToSettingsDescription,
108-
signInTitle: translations.localAuth.android.signInTitle,
109-
),
110-
const WindowsAuthMessages(),
111-
],
112-
);
113-
if (currentPlatform.isDesktop) {
114-
if (currentPlatform == Platform.macOS || currentPlatform == Platform.windows) {
115-
// See https://github.com/flutter/flutter/issues/122322.
116-
await windowManager.blur();
117-
await windowManager.focus();
118-
}
119-
await windowManager.setAlwaysOnTop(false);
120-
}
72+
bool result = await LocalAuthentication.instance.authenticate(context, translations.appUnlock.localAuthentication[reason.name] ?? 'Authenticate to access the app.');
12173
return result ? const ResultSuccess() : const ResultCancelled();
12274
}
12375

12476
@override
12577
Future<CannotUnlockException?> canUnlock(Ref ref) async {
126-
if (!(await isSupported())) {
78+
if (!(await LocalAuthentication.instance.isSupported())) {
12779
return LocalAuthenticationDeviceNotSupported();
12880
}
12981
return null;
13082
}
131-
132-
/// Returns whether this unlock method is supported;
133-
static Future<bool> isSupported() => LocalAuthentication().isDeviceSupported();
13483
}
13584

13685
/// Indicates that local authentication is not supported by the device.

lib/model/crypto.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ class StoredCryptoStore extends AsyncNotifier<CryptoStore?> {
2424
@override
2525
FutureOr<CryptoStore?> build() async {
2626
Salt? salt = await Salt.readFromLocalStorage();
27-
if (salt == null || !await SimpleSecureStorage.has(_kPasswordDerivedKeyKey)) {
27+
if (salt == null) {
2828
return null;
2929
}
30+
31+
String? derivedKey = await SimpleSecureStorage.read(_kPasswordDerivedKeyKey);
32+
if (derivedKey == null) {
33+
return null;
34+
}
35+
3036
return CryptoStore._(
31-
key: await AesGcmSecretKey.importRawKey(base64.decode((await SimpleSecureStorage.read(_kPasswordDerivedKeyKey))!)),
37+
key: await AesGcmSecretKey.importRawKey(base64.decode(derivedKey)),
3238
salt: salt,
3339
);
3440
}

lib/pages/settings/entries/clear_data.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:open_authenticator/model/settings/entry.dart';
1414
import 'package:open_authenticator/model/storage/local.dart';
1515
import 'package:open_authenticator/model/totp/image_cache.dart';
1616
import 'package:open_authenticator/pages/settings/entries/widgets.dart';
17+
import 'package:open_authenticator/utils/firebase.dart';
1718
import 'package:open_authenticator/utils/platform.dart';
1819
import 'package:open_authenticator/utils/result.dart';
1920
import 'package:open_authenticator/utils/shared_preferences_with_prefix.dart';
@@ -70,8 +71,10 @@ class ClearDataSettingsEntryWidget extends ConsumerWidget {
7071
context.showSnackBarForResult(logoutResult, retryIfError: true);
7172
return;
7273
}
73-
await FirebaseFirestore.instance.terminate();
74-
await FirebaseFirestore.instance.clearPersistence();
74+
if (isFirebaseSupported) {
75+
await FirebaseFirestore.instance.terminate();
76+
await FirebaseFirestore.instance.clearPersistence();
77+
}
7578
await SimpleSecureStorage.clear();
7679
TotpImageCacheManager totpImageCacheManager = ref.read(totpImageCacheManagerProvider.notifier);
7780
await totpImageCacheManager.clearCache();

lib/pages/settings/entries/enable_local_auth.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:open_authenticator/i18n/translations.g.dart';
44
import 'package:open_authenticator/model/app_unlock/method.dart';
55
import 'package:open_authenticator/model/settings/app_unlock_method.dart';
66
import 'package:open_authenticator/pages/settings/entries/widgets.dart';
7+
import 'package:open_authenticator/utils/local_authentication/local_authentication.dart';
78

89
/// Allows to configure [enableLocalAuthSettingsEntryProvider].
910
class EnableLocalAuthSettingsEntryWidget extends CheckboxSettingsEntryWidget<AppUnlockMethodSettingsEntry, AppUnlockMethod> {
@@ -19,7 +20,7 @@ class EnableLocalAuthSettingsEntryWidget extends CheckboxSettingsEntryWidget<App
1920

2021
@override
2122
Widget build(BuildContext context, WidgetRef ref) => FutureBuilder(
22-
future: LocalAuthenticationAppUnlockMethod.isSupported(),
23+
future: LocalAuthentication.instance.isSupported(),
2324
builder: (context, snapshot) {
2425
if (snapshot.data == null) {
2526
return createListTile(context, ref, enabled: false);

0 commit comments

Comments
 (0)