diff --git a/lib/main.dart b/lib/main.dart index 4f65052b..8f63a19f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:twonly/app.dart'; @@ -28,6 +27,7 @@ import 'package:twonly/src/services/notifications/setup.notifications.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/avatars.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:twonly/src/utils/storage.dart'; /// This function is used to initialized the absolute minimum so it @@ -56,13 +56,23 @@ void main() async { await initFCMService(); - final userExists = await userService.tryInit(); + var userExists = false; + var storageError = false; + try { + userExists = await userService.tryInit(); + } catch (e) { + Log.error('Failed to initialize user session due to storage error: $e'); + storageError = true; + } + + final dbFile = File('${AppEnvironment.supportDir}/twonly.sqlite'); + final dbExists = dbFile.existsSync(); if (Platform.isIOS && userExists) { - final db = File('${AppEnvironment.supportDir}/twonly.sqlite'); - if (!db.existsSync()) { + if (!dbExists) { Log.error('[twonly] IOS: App was removed and then reinstalled again...'); - await const FlutterSecureStorage().deleteAll(); + await SecureStorage.instance.deleteAll(); + userExists = false; } } @@ -80,9 +90,21 @@ void main() async { unawaited(performTwonlySafeBackup()); unawaited(initializeBackgroundTaskManager()); + } else if (!storageError) { + if (!dbExists) { + Log.info( + 'User is not yet registered and no database found. Ensuring clean state.', + ); + await deleteLocalUserData(); + } else { + Log.error( + 'User not found in secure storage, but database exists. Skipping destructive wipe.', + ); + } } else { - Log.info('User is not yet register. Ensure all local data is removed.'); - await deleteLocalUserData(); + Log.error( + 'Storage error occurred and database exists. Skipping wipe to prevent data loss.', + ); } final settingsController = SettingsChangeProvider(); diff --git a/lib/src/database/signal/signal_signed_pre_key_store.dart b/lib/src/database/signal/signal_signed_pre_key_store.dart index 4e949e47..5017230c 100644 --- a/lib/src/database/signal/signal_signed_pre_key_store.dart +++ b/lib/src/database/signal/signal_signed_pre_key_store.dart @@ -1,14 +1,14 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; class SignalSignedPreKeyStore extends SignedPreKeyStore { Future> getStore() async { - const storage = FlutterSecureStorage(); - final storeSerialized = await storage.read( + final storeSerialized = await SecureStorage.instance.read( key: SecureStorageKeys.signalSignedPreKey, ); final store = HashMap(); @@ -24,13 +24,12 @@ class SignalSignedPreKeyStore extends SignedPreKeyStore { } Future safeStore(HashMap store) async { - const storage = FlutterSecureStorage(); final storeHashMap = >[]; for (final item in store.entries) { storeHashMap.add([item.key, base64Encode(item.value)]); } final storeSerialized = json.encode(storeHashMap); - await storage.write( + await SecureStorage.instance.write( key: SecureStorageKeys.signalSignedPreKey, value: storeSerialized, ); diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index 70e51353..eb84969a 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -11,7 +11,6 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:drift/drift.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; // ignore: implementation_imports import 'package:libsignal_protocol_dart/src/ecc/ed25519.dart'; import 'package:mutex/mutex.dart'; @@ -44,6 +43,7 @@ import 'package:twonly/src/services/user_study.service.dart'; import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:web_socket_channel/io.dart'; final lockConnecting = Mutex(); @@ -417,8 +417,7 @@ class ApiService { } Future tryAuthenticateWithToken(int userId) async { - const storage = FlutterSecureStorage(); - final apiAuthToken = await storage.read( + final apiAuthToken = await SecureStorage.instance.read( key: SecureStorageKeys.apiAuthToken, ); final user = await getUser(); @@ -518,8 +517,7 @@ class ApiService { final apiAuthToken = result2.value.authtoken as Uint8List; final apiAuthTokenB64 = base64Encode(apiAuthToken); - const storage = FlutterSecureStorage(); - await storage.write( + await SecureStorage.instance.write( key: SecureStorageKeys.apiAuthToken, value: apiAuthTokenB64, ); diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart index 60a28e37..7689365b 100644 --- a/lib/src/services/api/mediafiles/upload.api.dart +++ b/lib/src/services/api/mediafiles/upload.api.dart @@ -8,7 +8,6 @@ import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'; import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:drift/drift.dart'; import 'package:fixnum/fixnum.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart' as http; import 'package:mutex/mutex.dart'; import 'package:twonly/globals.dart'; @@ -26,6 +25,7 @@ import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:workmanager/workmanager.dart' hide TaskStatus; final lockRetransmission = Mutex(); @@ -607,7 +607,7 @@ Future _uploadUploadRequest(MediaFileService media) async { return null; } - final apiAuthTokenRaw = await const FlutterSecureStorage().read( + final apiAuthTokenRaw = await SecureStorage.instance.read( key: SecureStorageKeys.apiAuthToken, ); diff --git a/lib/src/services/backup/create.backup.dart b/lib/src/services/backup/create.backup.dart index 5bb70f03..fa2a4523 100644 --- a/lib/src/services/backup/create.backup.dart +++ b/lib/src/services/backup/create.backup.dart @@ -9,7 +9,6 @@ import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'; import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:path/path.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; @@ -21,6 +20,7 @@ import 'package:twonly/src/services/backup/common.backup.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; Future performTwonlySafeBackup({bool force = false}) async { if (userService.currentUser.twonlySafeBackup == null) { @@ -84,12 +84,15 @@ Future performTwonlySafeBackup({bool force = false}) async { // ignore: inference_failure_on_collection_literal final secureStorageBackup = {}; - const storage = FlutterSecureStorage(); - secureStorageBackup[SecureStorageKeys.signalIdentity] = await storage.read( - key: SecureStorageKeys.signalIdentity, - ); - secureStorageBackup[SecureStorageKeys.signalSignedPreKey] = await storage - .read(key: SecureStorageKeys.signalSignedPreKey); + secureStorageBackup[SecureStorageKeys.signalIdentity] = await SecureStorage + .instance + .read( + key: SecureStorageKeys.signalIdentity, + ); + secureStorageBackup[SecureStorageKeys.signalSignedPreKey] = + await SecureStorage.instance.read( + key: SecureStorageKeys.signalSignedPreKey, + ); final userBackup = await getUser(); if (userBackup == null) return; @@ -129,7 +132,7 @@ Future performTwonlySafeBackup({bool force = false}) async { force = true; } - final lastHash = await storage.read( + final lastHash = await SecureStorage.instance.read( key: SecureStorageKeys.twonlySafeLastBackupHash, ); @@ -139,7 +142,8 @@ Future performTwonlySafeBackup({bool force = false}) async { return; } } - await storage.write( + + await SecureStorage.instance.write( key: SecureStorageKeys.twonlySafeLastBackupHash, value: backupHash, ); diff --git a/lib/src/services/backup/restore.backup.dart b/lib/src/services/backup/restore.backup.dart index 7d0fbe7c..f2721e0e 100644 --- a/lib/src/services/backup/restore.backup.dart +++ b/lib/src/services/backup/restore.backup.dart @@ -6,7 +6,6 @@ import 'dart:io'; import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'; import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:drift/drift.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart'; import 'package:twonly/globals.dart'; @@ -16,6 +15,7 @@ import 'package:twonly/src/model/protobuf/client/generated/backup.pb.dart'; import 'package:twonly/src/services/backup/common.backup.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; Future recoverBackup( String username, @@ -92,7 +92,7 @@ Future handleBackupData( ); await originalDatabase.writeAsBytes(backupContent.twonlyDatabase); - const storage = FlutterSecureStorage(); + const storage = SecureStorage.instance; final secureStorage = jsonDecode(backupContent.secureStorageJson); diff --git a/lib/src/services/signal/identity.signal.dart b/lib/src/services/signal/identity.signal.dart index bb053c2c..6086217d 100644 --- a/lib/src/services/signal/identity.signal.dart +++ b/lib/src/services/signal/identity.signal.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:clock/clock.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; @@ -13,6 +12,7 @@ import 'package:twonly/src/services/signal/protocol_state.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; Future getSignalIdentityKeyPair() async { final signalIdentity = await getSignalIdentity(); @@ -79,8 +79,7 @@ Future> signalGetPreKeys() async { Future getSignalIdentity() async { try { - const storage = FlutterSecureStorage(); - var signalIdentityJson = await storage.read( + var signalIdentityJson = await SecureStorage.instance.read( key: SecureStorageKeys.signalIdentity, ); if (signalIdentityJson == null) { @@ -102,9 +101,7 @@ Future getUserPublicKey() async { } Future createIfNotExistsSignalIdentity() async { - const storage = FlutterSecureStorage(); - - final signalIdentity = await storage.read( + final signalIdentity = await SecureStorage.instance.read( key: SecureStorageKeys.signalIdentity, ); @@ -132,7 +129,7 @@ Future createIfNotExistsSignalIdentity() async { registrationId: registrationId, ); - await storage.write( + await SecureStorage.instance.write( key: SecureStorageKeys.signalIdentity, value: jsonEncode(storedSignalIdentity), ); diff --git a/lib/src/services/user.service.dart b/lib/src/services/user.service.dart index 37c8c146..8ea64c4c 100644 --- a/lib/src/services/user.service.dart +++ b/lib/src/services/user.service.dart @@ -11,6 +11,7 @@ import 'package:twonly/src/model/json/userdata.model.dart'; import 'package:twonly/src/providers/purchases.provider.dart'; import 'package:twonly/src/services/subscription.service.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; class UserService { late UserData currentUser; @@ -45,18 +46,16 @@ Future isUserCreated() async { Future getUser() async { try { - final userJson = await const FlutterSecureStorage().read( + final userDataJson = await SecureStorage.instance.read( key: SecureStorageKeys.userData, ); - if (userJson == null) { + if (userDataJson == null) { return null; } - final userMap = jsonDecode(userJson) as Map; - final user = UserData.fromJson(userMap); - return user; + return UserData.fromJson(jsonDecode(userDataJson) as Map); } catch (e) { - Log.error('Error getting user: $e'); - return null; + Log.error('could not load user: $e'); + rethrow; // Rethrow instead of returning null to distinguish error from missing user } } diff --git a/lib/src/utils/secure_storage.dart b/lib/src/utils/secure_storage.dart new file mode 100644 index 00000000..b2d62241 --- /dev/null +++ b/lib/src/utils/secure_storage.dart @@ -0,0 +1,5 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class SecureStorage { + static const FlutterSecureStorage instance = FlutterSecureStorage(); +} diff --git a/lib/src/utils/storage.dart b/lib/src/utils/storage.dart index b963b693..b6e4e283 100644 --- a/lib/src/utils/storage.dart +++ b/lib/src/utils/storage.dart @@ -1,11 +1,11 @@ -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; Future deleteLocalUserData() async { final appDir = await getApplicationSupportDirectory(); if (appDir.existsSync()) { appDir.deleteSync(recursive: true); } - await const FlutterSecureStorage().deleteAll(); + await SecureStorage.instance.deleteAll(); return true; } diff --git a/lib/src/visual/views/onboarding/register.view.dart b/lib/src/visual/views/onboarding/register.view.dart index f275ec71..f507b489 100644 --- a/lib/src/visual/views/onboarding/register.view.dart +++ b/lib/src/visual/views/onboarding/register.view.dart @@ -5,7 +5,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:go_router/go_router.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; @@ -16,6 +15,7 @@ import 'package:twonly/src/services/signal/identity.signal.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/pow.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart'; import 'package:twonly/src/visual/views/groups/group.view.dart'; @@ -138,7 +138,7 @@ class _RegisterViewState extends State { subscriptionPlan: 'Preview', )..appVersion = 62; - await const FlutterSecureStorage().write( + await SecureStorage.instance.write( key: SecureStorageKeys.userData, value: jsonEncode(userData), ); diff --git a/lib/src/visual/views/settings/help/contact_us.view.dart b/lib/src/visual/views/settings/help/contact_us.view.dart index 3ff15996..2d91c099 100644 --- a/lib/src/visual/views/settings/help/contact_us.view.dart +++ b/lib/src/visual/views/settings/help/contact_us.view.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:http/http.dart' as http; import 'package:package_info_plus/package_info_plus.dart'; @@ -12,6 +11,7 @@ import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:twonly/src/visual/views/settings/help/contact_us/submit_message.view.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -48,7 +48,7 @@ class _ContactUsState extends State { final uploadRequestBytes = uploadRequest.writeToBuffer(); - final apiAuthTokenRaw = await const FlutterSecureStorage().read( + final apiAuthTokenRaw = await SecureStorage.instance.read( key: SecureStorageKeys.apiAuthToken, ); if (apiAuthTokenRaw == null) { diff --git a/lib/src/visual/views/settings/notification.view.dart b/lib/src/visual/views/settings/notification.view.dart index c2769ed2..26a3b591 100644 --- a/lib/src/visual/views/settings/notification.view.dart +++ b/lib/src/visual/views/settings/notification.view.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hashlib/random.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; @@ -12,6 +11,7 @@ import 'package:twonly/src/services/notifications/fcm.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart'; class NotificationView extends StatefulWidget { @@ -33,9 +33,9 @@ class _NotificationViewState extends State { await initFCMAfterAuthenticated(force: true); - final storedToken = await (const FlutterSecureStorage().read( + final storedToken = await SecureStorage.instance.read( key: SecureStorageKeys.googleFcm, - )); + ); await setupNotificationWithUsers(force: true);