mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 02:12:13 +00:00
use a centralized version of flutter secure storage
This commit is contained in:
parent
646b9c22d3
commit
db9d9022fd
13 changed files with 77 additions and 53 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<HashMap<int, Uint8List>> getStore() async {
|
||||
const storage = FlutterSecureStorage();
|
||||
final storeSerialized = await storage.read(
|
||||
final storeSerialized = await SecureStorage.instance.read(
|
||||
key: SecureStorageKeys.signalSignedPreKey,
|
||||
);
|
||||
final store = HashMap<int, Uint8List>();
|
||||
|
|
@ -24,13 +24,12 @@ class SignalSignedPreKeyStore extends SignedPreKeyStore {
|
|||
}
|
||||
|
||||
Future<void> safeStore(HashMap<int, Uint8List> store) async {
|
||||
const storage = FlutterSecureStorage();
|
||||
final storeHashMap = <List<dynamic>>[];
|
||||
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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<bool> 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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<void> _uploadUploadRequest(MediaFileService media) async {
|
|||
return null;
|
||||
}
|
||||
|
||||
final apiAuthTokenRaw = await const FlutterSecureStorage().read(
|
||||
final apiAuthTokenRaw = await SecureStorage.instance.read(
|
||||
key: SecureStorageKeys.apiAuthToken,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<void> performTwonlySafeBackup({bool force = false}) async {
|
||||
if (userService.currentUser.twonlySafeBackup == null) {
|
||||
|
|
@ -84,12 +84,15 @@ Future<void> 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<void> 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<void> performTwonlySafeBackup({bool force = false}) async {
|
|||
return;
|
||||
}
|
||||
}
|
||||
await storage.write(
|
||||
|
||||
await SecureStorage.instance.write(
|
||||
key: SecureStorageKeys.twonlySafeLastBackupHash,
|
||||
value: backupHash,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<void> recoverBackup(
|
||||
String username,
|
||||
|
|
@ -92,7 +92,7 @@ Future<void> handleBackupData(
|
|||
);
|
||||
await originalDatabase.writeAsBytes(backupContent.twonlyDatabase);
|
||||
|
||||
const storage = FlutterSecureStorage();
|
||||
const storage = SecureStorage.instance;
|
||||
|
||||
final secureStorage = jsonDecode(backupContent.secureStorageJson);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<IdentityKeyPair?> getSignalIdentityKeyPair() async {
|
||||
final signalIdentity = await getSignalIdentity();
|
||||
|
|
@ -79,8 +79,7 @@ Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
|||
|
||||
Future<SignalIdentity?> 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<Uint8List> getUserPublicKey() async {
|
|||
}
|
||||
|
||||
Future<void> createIfNotExistsSignalIdentity() async {
|
||||
const storage = FlutterSecureStorage();
|
||||
|
||||
final signalIdentity = await storage.read(
|
||||
final signalIdentity = await SecureStorage.instance.read(
|
||||
key: SecureStorageKeys.signalIdentity,
|
||||
);
|
||||
|
||||
|
|
@ -132,7 +129,7 @@ Future<void> createIfNotExistsSignalIdentity() async {
|
|||
registrationId: registrationId,
|
||||
);
|
||||
|
||||
await storage.write(
|
||||
await SecureStorage.instance.write(
|
||||
key: SecureStorageKeys.signalIdentity,
|
||||
value: jsonEncode(storedSignalIdentity),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<bool> isUserCreated() async {
|
|||
|
||||
Future<UserData?> 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<String, dynamic>;
|
||||
final user = UserData.fromJson(userMap);
|
||||
return user;
|
||||
return UserData.fromJson(jsonDecode(userDataJson) as Map<String, dynamic>);
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
5
lib/src/utils/secure_storage.dart
Normal file
5
lib/src/utils/secure_storage.dart
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class SecureStorage {
|
||||
static const FlutterSecureStorage instance = FlutterSecureStorage();
|
||||
}
|
||||
|
|
@ -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<bool> deleteLocalUserData() async {
|
||||
final appDir = await getApplicationSupportDirectory();
|
||||
if (appDir.existsSync()) {
|
||||
appDir.deleteSync(recursive: true);
|
||||
}
|
||||
await const FlutterSecureStorage().deleteAll();
|
||||
await SecureStorage.instance.deleteAll();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<RegisterView> {
|
|||
subscriptionPlan: 'Preview',
|
||||
)..appVersion = 62;
|
||||
|
||||
await const FlutterSecureStorage().write(
|
||||
await SecureStorage.instance.write(
|
||||
key: SecureStorageKeys.userData,
|
||||
value: jsonEncode(userData),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<ContactUsView> {
|
|||
|
||||
final uploadRequestBytes = uploadRequest.writeToBuffer();
|
||||
|
||||
final apiAuthTokenRaw = await const FlutterSecureStorage().read(
|
||||
final apiAuthTokenRaw = await SecureStorage.instance.read(
|
||||
key: SecureStorageKeys.apiAuthToken,
|
||||
);
|
||||
if (apiAuthTokenRaw == null) {
|
||||
|
|
|
|||
|
|
@ -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<NotificationView> {
|
|||
|
||||
await initFCMAfterAuthenticated(force: true);
|
||||
|
||||
final storedToken = await (const FlutterSecureStorage().read(
|
||||
final storedToken = await SecureStorage.instance.read(
|
||||
key: SecureStorageKeys.googleFcm,
|
||||
));
|
||||
);
|
||||
|
||||
await setupNotificationWithUsers(force: true);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue