diff --git a/lib/core/bridge/wrapper/key_manager.dart b/lib/core/bridge/wrapper/key_manager.dart new file mode 100644 index 00000000..628fc0c1 --- /dev/null +++ b/lib/core/bridge/wrapper/key_manager.dart @@ -0,0 +1,22 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +class FlutterKeyManager { + const FlutterKeyManager(); + + static Future getLoginToken() => RustLib.instance.api + .crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken(); + + @override + int get hashCode => 0; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FlutterKeyManager && runtimeType == other.runtimeType; +} diff --git a/lib/core/frb_generated.dart b/lib/core/frb_generated.dart index e4be04f0..0b769958 100644 --- a/lib/core/frb_generated.dart +++ b/lib/core/frb_generated.dart @@ -5,6 +5,7 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; import 'dart:async'; import 'dart:convert'; @@ -70,7 +71,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.12.0'; @override - int get rustContentHash => 1680338106; + int get rustContentHash => 1007286393; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -82,6 +83,9 @@ class RustLib extends BaseEntrypoint { } abstract class RustLibApi extends BaseApi { + Future + crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken(); + Future crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion(); @@ -165,7 +169,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @override Future - crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion() { + crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken() { return handler.executeNormal( NormalTask( callFfi: (port_) { @@ -181,6 +185,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_AnyhowException, ), + constMeta: + kCrateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginTokenConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta + get kCrateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginTokenConstMeta => + const TaskConstMeta( + debugName: "flutter_key_manager_get_login_token", + argNames: [], + ); + + @override + Future + crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 2, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_AnyhowException, + ), constMeta: kCrateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersionConstMeta, argValues: [], @@ -211,7 +248,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 2, + funcId: 3, port: port_, ); }, @@ -254,7 +291,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 3, + funcId: 4, port: port_, ); }, @@ -296,7 +333,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 4, + funcId: 5, port: port_, ); }, @@ -334,7 +371,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 5, + funcId: 6, port: port_, ); }, @@ -375,7 +412,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 6, + funcId: 7, port: port_, ); }, @@ -499,7 +536,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 7, + funcId: 8, port: port_, ); }, @@ -564,7 +601,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 8, + funcId: 9, port: port_, ); }, @@ -1183,6 +1220,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dco_decode_init_config(raw); } + @protected + FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 0) + throw Exception('unexpected arr length: expect 0 but see ${arr.length}'); + return FlutterKeyManager(); + } + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1378,6 +1424,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_init_config(deserializer)); } + @protected + FlutterKeyManager sse_decode_flutter_key_manager( + SseDeserializer deserializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + return FlutterKeyManager(); + } + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -1821,6 +1875,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_init_config(self, serializer); } + @protected + void sse_encode_flutter_key_manager( + FlutterKeyManager self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + } + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, diff --git a/lib/core/frb_generated.io.dart b/lib/core/frb_generated.io.dart index d71edb15..482e21c8 100644 --- a/lib/core/frb_generated.io.dart +++ b/lib/core/frb_generated.io.dart @@ -5,6 +5,7 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; import 'dart:async'; import 'dart:convert'; @@ -119,6 +120,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig dco_decode_box_autoadd_init_config(dynamic raw); + @protected + FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw); + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw); @@ -204,6 +208,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer); + @protected + FlutterKeyManager sse_decode_flutter_key_manager( + SseDeserializer deserializer, + ); + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -397,6 +406,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_flutter_key_manager( + FlutterKeyManager self, + SseSerializer serializer, + ); + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, diff --git a/lib/core/frb_generated.web.dart b/lib/core/frb_generated.web.dart index 2febda82..868baea7 100644 --- a/lib/core/frb_generated.web.dart +++ b/lib/core/frb_generated.web.dart @@ -8,6 +8,7 @@ import 'bridge.dart'; import 'bridge/callbacks.dart'; +import 'bridge/wrapper/key_manager.dart'; import 'bridge/wrapper/user_discovery.dart'; import 'dart:async'; import 'dart:convert'; @@ -121,6 +122,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig dco_decode_box_autoadd_init_config(dynamic raw); + @protected + FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw); + @protected FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw); @@ -206,6 +210,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer); + @protected + FlutterKeyManager sse_decode_flutter_key_manager( + SseDeserializer deserializer, + ); + @protected FlutterUserDiscovery sse_decode_flutter_user_discovery( SseDeserializer deserializer, @@ -399,6 +408,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { SseSerializer serializer, ); + @protected + void sse_encode_flutter_key_manager( + FlutterKeyManager self, + SseSerializer serializer, + ); + @protected void sse_encode_flutter_user_discovery( FlutterUserDiscovery self, diff --git a/lib/globals.dart b/lib/globals.dart index 87b0e975..752bc1d6 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -35,7 +35,7 @@ class AppState { static bool isInBackgroundTask = false; static bool allowErrorTrackingViaSentry = false; static bool gotMessageFromServer = false; - static int latestAppVersionId = 110; + static int latestAppVersionId = 111; } class AppGlobalKeys { diff --git a/lib/main.dart b/lib/main.dart index 66174396..507051a3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -80,7 +80,7 @@ void main() async { unawaited(StartupGuard.markAppStartup()); var storageError = await twonlyMinimumInitialization(); - unawaited(initFCMService()); + await initFCMService(); var userExists = false; @@ -185,6 +185,13 @@ Future runMigrations() async { } }); } + if (userService.currentUser.appVersion < 111) { + await UserService.update((u) { + u + ..appVersion = 111 + ..canUseLoginTokenForAuth = false; + }); + } } Future postStartupTasks() async { diff --git a/lib/src/model/json/userdata.model.dart b/lib/src/model/json/userdata.model.dart index 52b9857b..bf1babaa 100644 --- a/lib/src/model/json/userdata.model.dart +++ b/lib/src/model/json/userdata.model.dart @@ -128,6 +128,9 @@ class UserData { @JsonKey(defaultValue: true) bool updateFCMToken = true; + @JsonKey(defaultValue: true) + bool canUseLoginTokenForAuth = true; + // --- BACKUP --- DateTime? nextTimeToShowBackupNotice; diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart index 02743418..9d470c28 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart @@ -1248,66 +1248,6 @@ class ApplicationData_GetUserById extends $pb.GeneratedMessage { void clearUserId() => $_clearField(1); } -class ApplicationData_UpdatePlanOptions extends $pb.GeneratedMessage { - factory ApplicationData_UpdatePlanOptions({ - $core.bool? autoRenewal, - }) { - final result = create(); - if (autoRenewal != null) result.autoRenewal = autoRenewal; - return result; - } - - ApplicationData_UpdatePlanOptions._(); - - factory ApplicationData_UpdatePlanOptions.fromBuffer( - $core.List<$core.int> data, - [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(data, registry); - factory ApplicationData_UpdatePlanOptions.fromJson($core.String json, - [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(json, registry); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'ApplicationData.UpdatePlanOptions', - package: - const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), - createEmptyInstance: create) - ..aOB(1, _omitFieldNames ? '' : 'autoRenewal') - ..hasRequiredFields = false; - - @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') - ApplicationData_UpdatePlanOptions clone() => deepCopy(); - @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') - ApplicationData_UpdatePlanOptions copyWith( - void Function(ApplicationData_UpdatePlanOptions) updates) => - super.copyWith((message) => - updates(message as ApplicationData_UpdatePlanOptions)) - as ApplicationData_UpdatePlanOptions; - - @$core.override - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ApplicationData_UpdatePlanOptions create() => - ApplicationData_UpdatePlanOptions._(); - @$core.override - ApplicationData_UpdatePlanOptions createEmptyInstance() => create(); - @$core.pragma('dart2js:noInline') - static ApplicationData_UpdatePlanOptions getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor( - create); - static ApplicationData_UpdatePlanOptions? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get autoRenewal => $_getBF(0); - @$pb.TagNumber(1) - set autoRenewal($core.bool value) => $_setBool(0, value); - @$pb.TagNumber(1) - $core.bool hasAutoRenewal() => $_has(0); - @$pb.TagNumber(1) - void clearAutoRenewal() => $_clearField(1); -} - class ApplicationData_GetAvailablePlans extends $pb.GeneratedMessage { factory ApplicationData_GetAvailablePlans() => create(); @@ -2178,7 +2118,7 @@ enum ApplicationData_ApplicationData { deprecated16, deprecated17, removeAdditionalUser, - updatePlanOptions, + deprecated19, downloadDone, getSignedPrekeyByUserid, updateSignedPrekey, @@ -2209,7 +2149,7 @@ class ApplicationData extends $pb.GeneratedMessage { ApplicationData_Deprecated? deprecated16, ApplicationData_Deprecated? deprecated17, ApplicationData_RemoveAdditionalUser? removeAdditionalUser, - ApplicationData_UpdatePlanOptions? updatePlanOptions, + ApplicationData_Deprecated? deprecated19, ApplicationData_DownloadDone? downloadDone, ApplicationData_GetSignedPreKeyByUserId? getSignedPrekeyByUserid, ApplicationData_UpdateSignedPreKey? updateSignedPrekey, @@ -2241,7 +2181,7 @@ class ApplicationData extends $pb.GeneratedMessage { if (deprecated17 != null) result.deprecated17 = deprecated17; if (removeAdditionalUser != null) result.removeAdditionalUser = removeAdditionalUser; - if (updatePlanOptions != null) result.updatePlanOptions = updatePlanOptions; + if (deprecated19 != null) result.deprecated19 = deprecated19; if (downloadDone != null) result.downloadDone = downloadDone; if (getSignedPrekeyByUserid != null) result.getSignedPrekeyByUserid = getSignedPrekeyByUserid; @@ -2283,7 +2223,7 @@ class ApplicationData extends $pb.GeneratedMessage { 16: ApplicationData_ApplicationData.deprecated16, 17: ApplicationData_ApplicationData.deprecated17, 18: ApplicationData_ApplicationData.removeAdditionalUser, - 19: ApplicationData_ApplicationData.updatePlanOptions, + 19: ApplicationData_ApplicationData.deprecated19, 20: ApplicationData_ApplicationData.downloadDone, 22: ApplicationData_ApplicationData.getSignedPrekeyByUserid, 23: ApplicationData_ApplicationData.updateSignedPrekey, @@ -2380,10 +2320,9 @@ class ApplicationData extends $pb.GeneratedMessage { 18, _omitFieldNames ? '' : 'removeAdditionalUser', protoName: 'removeAdditionalUser', subBuilder: ApplicationData_RemoveAdditionalUser.create) - ..aOM( - 19, _omitFieldNames ? '' : 'updatePlanOptions', - protoName: 'updatePlanOptions', - subBuilder: ApplicationData_UpdatePlanOptions.create) + ..aOM(19, _omitFieldNames ? '' : 'deprecated19', + protoName: 'deprecated_19', + subBuilder: ApplicationData_Deprecated.create) ..aOM( 20, _omitFieldNames ? '' : 'downloadDone', protoName: 'downloadDone', @@ -2672,16 +2611,15 @@ class ApplicationData extends $pb.GeneratedMessage { $_ensure(14); @$pb.TagNumber(19) - ApplicationData_UpdatePlanOptions get updatePlanOptions => $_getN(15); + ApplicationData_Deprecated get deprecated19 => $_getN(15); @$pb.TagNumber(19) - set updatePlanOptions(ApplicationData_UpdatePlanOptions value) => - $_setField(19, value); + set deprecated19(ApplicationData_Deprecated value) => $_setField(19, value); @$pb.TagNumber(19) - $core.bool hasUpdatePlanOptions() => $_has(15); + $core.bool hasDeprecated19() => $_has(15); @$pb.TagNumber(19) - void clearUpdatePlanOptions() => $_clearField(19); + void clearDeprecated19() => $_clearField(19); @$pb.TagNumber(19) - ApplicationData_UpdatePlanOptions ensureUpdatePlanOptions() => $_ensure(15); + ApplicationData_Deprecated ensureDeprecated19() => $_ensure(15); @$pb.TagNumber(20) ApplicationData_DownloadDone get downloadDone => $_getN(16); diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart index 73373bf9..c51f8c2b 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart @@ -447,13 +447,13 @@ const ApplicationData$json = { '10': 'deprecated17' }, { - '1': 'updatePlanOptions', + '1': 'deprecated_19', '3': 19, '4': 1, '5': 11, - '6': '.client_to_server.ApplicationData.UpdatePlanOptions', + '6': '.client_to_server.ApplicationData.Deprecated', '9': 0, - '10': 'updatePlanOptions' + '10': 'deprecated19' }, { '1': 'downloadDone', @@ -561,7 +561,6 @@ const ApplicationData$json = { ApplicationData_ChangeUsername$json, ApplicationData_UpdateGoogleFcmToken$json, ApplicationData_GetUserById$json, - ApplicationData_UpdatePlanOptions$json, ApplicationData_GetAvailablePlans$json, ApplicationData_GetAddAccountsInvites$json, ApplicationData_GetCurrentPlanInfos$json, @@ -636,14 +635,6 @@ const ApplicationData_GetUserById$json = { ], }; -@$core.Deprecated('Use applicationDataDescriptor instead') -const ApplicationData_UpdatePlanOptions$json = { - '1': 'UpdatePlanOptions', - '2': [ - {'1': 'auto_renewal', '3': 1, '4': 1, '5': 8, '10': 'autoRenewal'}, - ], -}; - @$core.Deprecated('Use applicationDataDescriptor instead') const ApplicationData_GetAvailablePlans$json = { '1': 'GetAvailablePlans', @@ -788,48 +779,47 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode( 'dGVkSABSDGRlcHJlY2F0ZWQxNRJTCg1kZXByZWNhdGVkXzE2GBAgASgLMiwuY2xpZW50X3RvX3' 'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRGVwcmVjYXRlZEgAUgxkZXByZWNhdGVkMTYSUwoNZGVw' 'cmVjYXRlZF8xNxgRIAEoCzIsLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlcH' - 'JlY2F0ZWRIAFIMZGVwcmVjYXRlZDE3EmMKEXVwZGF0ZVBsYW5PcHRpb25zGBMgASgLMjMuY2xp' - 'ZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlUGxhbk9wdGlvbnNIAFIRdXBkYX' - 'RlUGxhbk9wdGlvbnMSVAoMZG93bmxvYWREb25lGBQgASgLMi4uY2xpZW50X3RvX3NlcnZlci5B' - 'cHBsaWNhdGlvbkRhdGEuRG93bmxvYWREb25lSABSDGRvd25sb2FkRG9uZRJ1ChdnZXRTaWduZW' - 'RQcmVrZXlCeVVzZXJpZBgWIAEoCzI5LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRh' - 'LkdldFNpZ25lZFByZUtleUJ5VXNlcklkSABSF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkEmYKEn' - 'VwZGF0ZVNpZ25lZFByZWtleRgXIAEoCzI0LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25E' - 'YXRhLlVwZGF0ZVNpZ25lZFByZUtleUgAUhJ1cGRhdGVTaWduZWRQcmVrZXkSVwoNZGVsZXRlQW' - 'Njb3VudBgYIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlbGV0ZUFj' - 'Y291bnRIAFINZGVsZXRlQWNjb3VudBJOCgpyZXBvcnRVc2VyGBkgASgLMiwuY2xpZW50X3RvX3' - 'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVwb3J0VXNlckgAUgpyZXBvcnRVc2VyEloKDmNoYW5n' - 'ZVVzZXJuYW1lGBogASgLMjAuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuQ2hhbm' - 'dlVXNlcm5hbWVIAFIOY2hhbmdlVXNlcm5hbWUSUQoLaXBhUHVyY2hhc2UYGyABKAsyLS5jbGll' - 'bnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFQdXJjaGFzZUgAUgtpcGFQdXJjaGFzZR' - 'JXCg1pcGFGb3JjZUNoZWNrGBwgASgLMi8uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRh' - 'dGEuSVBBRm9yY2VDaGVja0gAUg1pcGFGb3JjZUNoZWNrEmwKFHJlbW92ZUFkZGl0aW9uYWxVc2' - 'VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVtb3ZlQWRkaXRp' - 'b25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoRYWRkQWRkaXRpb25hbFVzZXIYHS' - 'ABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5BZGRBZGRpdGlvbmFsVXNl' - 'ckgAUhFhZGRBZGRpdGlvbmFsVXNlchJZCg9zZXRfbG9naW5fdG9rZW4YHiABKAsyLy5jbGllbn' - 'RfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5TZXRMb2dpblRva2VuSABSDXNldExvZ2luVG9r' - 'ZW4aagoLVGV4dE1lc3NhZ2USFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhIKBGJvZHkYAyABKA' - 'xSBGJvZHkSIAoJcHVzaF9kYXRhGAQgASgMSABSCHB1c2hEYXRhiAEBQgwKCl9wdXNoX2RhdGEa' - 'LwoRR2V0VXNlckJ5VXNlcm5hbWUSGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lGiwKDkNoYW' - '5nZVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEgASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVG' - 'Y21Ub2tlbhIdCgpnb29nbGVfZmNtGAEgASgJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFw' - 'oHdXNlcl9pZBgBIAEoA1IGdXNlcklkGjYKEVVwZGF0ZVBsYW5PcHRpb25zEiEKDGF1dG9fcmVu' - 'ZXdhbBgBIAEoCFILYXV0b1JlbmV3YWwaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQW' - 'Njb3VudHNJbnZpdGVzGhUKE0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25h' - 'bFVzZXISFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCg' - 'd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNl' - 'cl9pZBgBIAEoA1IGdXNlcklkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZW' - 'tleV9pZBgBIAEoA1IOc2lnbmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2ln' - 'bmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZX' - 'lTaWduYXR1cmUaNQoMRG93bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3du' - 'bG9hZFRva2VuGk4KClJlcG9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3' - 'J0ZWRVc2VySWQSFgoGcmVhc29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJv' - 'ZHVjdF9pZBgBIAEoCVIJcHJvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcm' - 'lmaWNhdGlvbl9kYXRhGAMgASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2sa' - 'DwoNRGVsZXRlQWNjb3VudBosChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUg' - 'Z1c2VySWQaMAoNU2V0TG9naW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tl' - 'bhoMCgpEZXByZWNhdGVkQhEKD0FwcGxpY2F0aW9uRGF0YQ=='); + 'JlY2F0ZWRIAFIMZGVwcmVjYXRlZDE3ElMKDWRlcHJlY2F0ZWRfMTkYEyABKAsyLC5jbGllbnRf' + 'dG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5EZXByZWNhdGVkSABSDGRlcHJlY2F0ZWQxORJUCg' + 'xkb3dubG9hZERvbmUYFCABKAsyLi5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5E' + 'b3dubG9hZERvbmVIAFIMZG93bmxvYWREb25lEnUKF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkGB' + 'YgASgLMjkuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuR2V0U2lnbmVkUHJlS2V5' + 'QnlVc2VySWRIAFIXZ2V0U2lnbmVkUHJla2V5QnlVc2VyaWQSZgoSdXBkYXRlU2lnbmVkUHJla2' + 'V5GBcgASgLMjQuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlU2lnbmVk' + 'UHJlS2V5SABSEnVwZGF0ZVNpZ25lZFByZWtleRJXCg1kZWxldGVBY2NvdW50GBggASgLMi8uY2' + 'xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRGVsZXRlQWNjb3VudEgAUg1kZWxldGVB' + 'Y2NvdW50Ek4KCnJlcG9ydFVzZXIYGSABKAsyLC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW' + '9uRGF0YS5SZXBvcnRVc2VySABSCnJlcG9ydFVzZXISWgoOY2hhbmdlVXNlcm5hbWUYGiABKAsy' + 'MC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5DaGFuZ2VVc2VybmFtZUgAUg5jaG' + 'FuZ2VVc2VybmFtZRJRCgtpcGFQdXJjaGFzZRgbIAEoCzItLmNsaWVudF90b19zZXJ2ZXIuQXBw' + 'bGljYXRpb25EYXRhLklQQVB1cmNoYXNlSABSC2lwYVB1cmNoYXNlElcKDWlwYUZvcmNlQ2hlY2' + 'sYHCABKAsyLy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFGb3JjZUNoZWNr' + 'SABSDWlwYUZvcmNlQ2hlY2sSbAoUcmVtb3ZlQWRkaXRpb25hbFVzZXIYEiABKAsyNi5jbGllbn' + 'RfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5SZW1vdmVBZGRpdGlvbmFsVXNlckgAUhRyZW1v' + 'dmVBZGRpdGlvbmFsVXNlchJjChFhZGRBZGRpdGlvbmFsVXNlchgdIAEoCzIzLmNsaWVudF90b1' + '9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkFkZEFkZGl0aW9uYWxVc2VySABSEWFkZEFkZGl0aW9u' + 'YWxVc2VyElkKD3NldF9sb2dpbl90b2tlbhgeIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG' + 'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhpqCgtUZXh0TWVzc2Fn' + 'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2' + 'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy' + 'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX' + 'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds' + 'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg' + 'Z1c2VySWQaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQWNjb3VudHNJbnZpdGVzGhUK' + 'E0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZB' + 'gBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1' + 'c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlck' + 'lkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2ln' + 'bmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ2' + '5lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUaNQoMRG93' + 'bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuGk4KClJlcG' + '9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3J0ZWRVc2VySWQSFgoGcmVh' + 'c29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJvZHVjdF9pZBgBIAEoCVIJcH' + 'JvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcmlmaWNhdGlvbl9kYXRhGAMg' + 'ASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2saDwoNRGVsZXRlQWNjb3VudB' + 'osChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMAoNU2V0TG9n' + 'aW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tlbhoMCgpEZXByZWNhdGVkQh' + 'EKD0FwcGxpY2F0aW9uRGF0YQ=='); @$core.Deprecated('Use responseDescriptor instead') const Response$json = { diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index a2a3e3d4..a53e2c05 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -15,6 +15,7 @@ import 'package:flutter/foundation.dart'; import 'package:libsignal_protocol_dart/src/ecc/ed25519.dart'; import 'package:mutex/mutex.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:twonly/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; @@ -450,6 +451,21 @@ class ApiService { await onAuthenticated(); } else { unawaited(onAuthenticated()); + + try { + Log.info('Switching authentication to login token'); + final loginToken = await FlutterKeyManager.getLoginToken(); + final res = await _setLoginToken(loginToken); + if (res.isSuccess) { + Log.info('Switch was successfully.'); + await UserService.update((u) => u.canUseLoginTokenForAuth = true); + await SecureStorage.instance.delete( + key: SecureStorageKeys.apiAuthToken, + ); + } + } catch (e) { + Log.error(e); + } } return true; } @@ -466,9 +482,51 @@ class ApiService { return false; } + Future tryAuthenticateWithLoginToken() async { + try { + final loginToken = await FlutterKeyManager.getLoginToken(); + + final authenticate = Handshake_AuthenticateWithLoginToken() + ..userId = Int64(userService.currentUser.userId) + ..appVersion = (await PackageInfo.fromPlatform()).version + ..deviceId = Int64(userService.currentUser.deviceId) + ..inBackground = AppState.isInBackgroundTask + ..secretLoginToken = loginToken.toList(); + + final handshake = Handshake()..authenticateWithLoginToken = authenticate; + final req = createClientToServerFromHandshake(handshake); + + final result = await sendRequestSync(req, authenticated: false); + + if (result.isSuccess) { + Log.info('websocket is authenticated'); + isAuthenticated = true; + if (AppState.isInBackgroundTask) { + await onAuthenticated(); + } else { + unawaited(onAuthenticated()); + } + return true; + } + if (result.isError) { + if (result.error != ErrorCode.AuthTokenNotValid && + result.error != ErrorCode.ForegroundSessionConnected) { + Log.error( + 'got error while authenticating to the server: ${result.error}', + ); + return false; + } + } + } catch (e) { + Log.error(e); + } + return false; + } + Future authenticate() async { return lockAuthentication.protect(() async { if (isAuthenticated) return; + if (await getSignalIdentity() == null) { Log.error('Signal identity not found.'); return; @@ -476,6 +534,11 @@ class ApiService { if (!userService.isUserCreated) return; + if (userService.currentUser.canUseLoginTokenForAuth) { + await tryAuthenticateWithLoginToken(); + return; + } + if (await tryAuthenticateWithToken()) { return; } @@ -542,6 +605,8 @@ class ApiService { final signedPreKey = (await signalStore.loadSignedPreKeys())[0]; + final loginToken = await FlutterKeyManager.getLoginToken(); + final register = Handshake_Register() ..username = username ..publicIdentityKey = (await signalStore.getIdentityKeyPair()) @@ -552,6 +617,7 @@ class ApiService { ..signedPrekeySignature = signedPreKey.signature ..signedPrekeyId = Int64(signedPreKey.id) ..langCode = ui.PlatformDispatcher.instance.locale.languageCode + ..loginToken = loginToken ..proofOfWork = Int64(proofOfWorkResult) ..isIos = Platform.isIOS; @@ -617,6 +683,13 @@ class ApiService { return sendRequestSync(req, ensureRetransmission: true); } + Future _setLoginToken(List token) async { + final get = ApplicationData_SetLoginToken()..loginToken = token; + final appData = ApplicationData()..setLoginToken = get; + final req = createClientToServerFromApplicationData(appData); + return sendRequestSync(req); + } + Future getUserData(String username) async { final get = ApplicationData_GetUserByUsername()..username = username; final appData = ApplicationData()..getUserByUsername = get; @@ -645,13 +718,6 @@ class ApiService { return null; } - Future updatePlanOptions(bool autoRenewal) async { - final get = ApplicationData_UpdatePlanOptions()..autoRenewal = autoRenewal; - final appData = ApplicationData()..updatePlanOptions = get; - final req = createClientToServerFromApplicationData(appData); - return sendRequestSync(req); - } - Future removeAdditionalUser(Int64 userId) async { final get = ApplicationData_RemoveAdditionalUser()..userId = userId; final appData = ApplicationData()..removeAdditionalUser = get; diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart index e84a77dc..eb304f42 100644 --- a/lib/src/services/api/mediafiles/upload.api.dart +++ b/lib/src/services/api/mediafiles/upload.api.dart @@ -1,6 +1,4 @@ import 'dart:async'; -import 'dart:convert'; - import 'package:background_downloader/background_downloader.dart'; import 'package:clock/clock.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -12,7 +10,6 @@ import 'package:http/http.dart' as http; import 'package:mutex/mutex.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; -import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; @@ -21,12 +18,12 @@ import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart'; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/services/api/mediafiles/media_background.api.dart'; import 'package:twonly/src/services/api/messages.api.dart'; +import 'package:twonly/src/services/api/utils.api.dart'; import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/exclusive_access.utils.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(); @@ -620,22 +617,17 @@ Future _uploadUploadRequest(MediaFileService media) async { return null; } - final apiAuthTokenRaw = await SecureStorage.instance.read( - key: SecureStorageKeys.apiAuthToken, - ); - - if (apiAuthTokenRaw == null) { - Log.error('api auth token not defined.'); - return null; - } - final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw)); - final apiUrl = 'http${apiService.apiSecure}://${apiService.apiHost}/api/upload'; - // try { Log.info('Starting upload from ${media.mediaFile.mediaId}'); + final headers = await getAuthenticationHeader(); + if (headers == null) { + Log.error('Auth headers are empty. Returning'); + return; + } + final task = UploadTask.fromFile( taskId: 'upload_${media.mediaFile.mediaId}', displayName: media.mediaFile.type.name, @@ -643,9 +635,7 @@ Future _uploadUploadRequest(MediaFileService media) async { url: apiUrl, priority: 0, retries: 10, - headers: { - 'x-twonly-auth-token': apiAuthToken, - }, + headers: headers, ); final connectivityResult = await Connectivity().checkConnectivity(); diff --git a/lib/src/services/api/utils.api.dart b/lib/src/services/api/utils.api.dart index 13ca8f05..ad68abd2 100644 --- a/lib/src/services/api/utils.api.dart +++ b/lib/src/services/api/utils.api.dart @@ -1,6 +1,10 @@ +import 'dart:convert'; + import 'package:drift/drift.dart'; import 'package:fixnum/fixnum.dart'; +import 'package:twonly/core/bridge/wrapper/key_manager.dart'; import 'package:twonly/locator.dart'; +import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart' @@ -14,6 +18,9 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dar import 'package:twonly/src/services/api/messages.api.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/signal/session.signal.dart'; +import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/secure_storage.dart'; class Result { Result.error(this.error) : value = null; @@ -106,3 +113,36 @@ Future importSignalContactAndCreateRequest( return true; } + +Future?> getAuthenticationHeader() async { + var headers = {}; + + if (userService.currentUser.canUseLoginTokenForAuth) { + final loginToken = await FlutterKeyManager.getLoginToken(); + + headers = { + 'x-twonly-user-id': userService.currentUser.userId + .toRadixString(16) + .padLeft(16, '0') + .toUpperCase(), + 'x-twonly-login-token': uint8ListToHex(loginToken), + }; + } else { + final apiAuthTokenRaw = await SecureStorage.instance.read( + key: SecureStorageKeys.apiAuthToken, + ); + + if (apiAuthTokenRaw == null) { + Log.error('api auth token not defined.'); + return null; + } + + final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw)); + + headers = { + 'x-twonly-auth-token': apiAuthToken, + }; + } + + return headers; +} diff --git a/lib/src/services/background/callback_dispatcher.background.dart b/lib/src/services/background/callback_dispatcher.background.dart index 6aba3078..fbc22029 100644 --- a/lib/src/services/background/callback_dispatcher.background.dart +++ b/lib/src/services/background/callback_dispatcher.background.dart @@ -34,6 +34,7 @@ Future initializeBackgroundTaskManager() async { void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { SentryWidgetsFlutterBinding.ensureInitialized(); + await AppEnvironment.init(); switch (task) { case 'eu.twonly.periodic_task': // if (await initBackgroundExecution()) { @@ -58,7 +59,6 @@ Future initBackgroundExecution() async { return false; } - await AppEnvironment.init(); AppState.isInBackgroundTask = true; if (await StartupGuard.isAppStarting()) { diff --git a/lib/src/services/notifications/background.notifications.dart b/lib/src/services/notifications/background.notifications.dart index 45ab7a0f..e2c7e16a 100644 --- a/lib/src/services/notifications/background.notifications.dart +++ b/lib/src/services/notifications/background.notifications.dart @@ -5,7 +5,7 @@ import 'dart:math'; import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'; import 'package:cryptography_plus/cryptography_plus.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:path_provider/path_provider.dart'; +import 'package:twonly/globals.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/localization/generated/app_localizations.dart'; @@ -266,8 +266,7 @@ Future showLocalPushNotificationWithoutUserId( } Future getAvatarIcon(int contactId) async { - final directory = await getApplicationCacheDirectory(); - final avatarsDirectory = Directory('${directory.path}/avatars'); + final avatarsDirectory = Directory('${AppEnvironment.cacheDir}/avatars'); final filePath = '${avatarsDirectory.path}/$contactId.png'; final file = File(filePath); if (file.existsSync()) { diff --git a/lib/src/services/notifications/fcm.notifications.dart b/lib/src/services/notifications/fcm.notifications.dart index 8c5a7009..59618688 100644 --- a/lib/src/services/notifications/fcm.notifications.dart +++ b/lib/src/services/notifications/fcm.notifications.dart @@ -119,6 +119,7 @@ Future initFCMService() async { @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { SentryWidgetsFlutterBinding.ensureInitialized(); + await AppEnvironment.init(); final isInitialized = await initBackgroundExecution(); Log.info('Handling a background message: ${message.messageId}'); await handleRemoteMessage(message); diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart index 1a9c7f16..946d0408 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/stickers.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'package:clock/clock.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; -import 'package:path_provider/path_provider.dart'; +import 'package:twonly/globals.dart'; import 'package:twonly/src/utils/log.dart'; class Sticker { @@ -21,8 +21,7 @@ class Sticker { } Future> getStickerIndex() async { - final directory = await getApplicationCacheDirectory(); - final indexFile = File('${directory.path}/stickers.json'); + final indexFile = File('${AppEnvironment.cacheDir}/stickers.json'); var res = []; if (indexFile.existsSync() && kReleaseMode) { diff --git a/lib/src/visual/views/chats/chat_messages_components/message_input.dart b/lib/src/visual/views/chats/chat_messages_components/message_input.dart index d00143fa..cc54579c 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_input.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_input.dart @@ -7,8 +7,8 @@ import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:twonly/globals.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; @@ -131,8 +131,7 @@ class _MessageInputState extends State { _currentDuration = 0; }); await HapticFeedback.heavyImpact(); - final audioTmpPath = - '${(await getApplicationCacheDirectory()).path}/recording.m4a'; + final audioTmpPath = '${AppEnvironment.cacheDir}/recording.m4a'; unawaited( recorderController.record( path: audioTmpPath, 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 26840839..8f66ecb9 100644 --- a/lib/src/visual/views/settings/help/contact_us.view.dart +++ b/lib/src/visual/views/settings/help/contact_us.view.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:device_info_plus/device_info_plus.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; @@ -11,6 +9,7 @@ import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; 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/services/api/utils.api.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/secure_storage.dart'; @@ -57,16 +56,18 @@ class _ContactUsState extends State { Log.error('api auth token not defined.'); return null; } - final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw)); - final apiUrl = 'http${apiService.apiSecure}://${apiService.apiHost}/api/upload'; - final requestMultipart = http.MultipartRequest( - 'POST', - Uri.parse(apiUrl), - ); - requestMultipart.headers['x-twonly-auth-token'] = apiAuthToken; + final requestMultipart = http.MultipartRequest('POST', Uri.parse(apiUrl)); + + final headers = await getAuthenticationHeader(); + if (headers == null) { + Log.error('Auth headers are empty. Returning'); + return null; + } + + requestMultipart.headers.addAll(headers); requestMultipart.files.add( http.MultipartFile.fromBytes( diff --git a/rust/src/backup/backup_password.rs b/rust/src/backup/backup_password.rs index 8d91a63d..52264603 100644 --- a/rust/src/backup/backup_password.rs +++ b/rust/src/backup/backup_password.rs @@ -1,8 +1,10 @@ use crate::error::{Result, TwonlyError}; use crate::keys::KeyManager; +use crate::secure_storage::{self, SecureStorage}; use aes_gcm::aead::rand_core::RngCore; use aes_gcm::aead::{Aead, KeyInit, OsRng}; use aes_gcm::{Aes256Gcm, Nonce}; +use mdk_core::key_packages; use scrypt::{scrypt, Params}; use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -21,11 +23,16 @@ impl BackupPasswordKeys { } } - pub(crate) fn from_password(password: &str, salt: &str) -> Result { + pub(crate) fn from_password(password: &str, username: &str) -> Result { let params = Params::new(17, 8, 1)?; let mut output = [0u8; 64]; - scrypt(password.as_bytes(), salt.as_bytes(), ¶ms, &mut output)?; + scrypt( + password.as_bytes(), + username.as_bytes(), + ¶ms, + &mut output, + )?; let mut backup_id = [0u8; 32]; let mut encryption_key = [0u8; 32]; @@ -34,21 +41,13 @@ impl BackupPasswordKeys { Ok(Self::new(backup_id, encryption_key)) } -} -#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] -pub(crate) struct BackupPlainTextContent { - pub(crate) user_id: i64, - pub(crate) key_manager: KeyManager, -} - -impl BackupPlainTextContent { - fn get_encrypted_backup(&self) -> Result> { - let Some(keys) = &self.key_manager.backup_password else { + fn encrypt_key_manager(key_manager: KeyManager) -> Result> { + let Some(keys) = &key_manager.backup_password else { return Err(TwonlyError::Generic("No backup password".into())); }; - let serialized_bytes = postcard::to_allocvec(&self)?; + let serialized_bytes = postcard::to_allocvec(&key_manager)?; let key = aes_gcm::Key::::from_slice(&keys.encryption_key); let cipher = Aes256Gcm::new(key); @@ -66,10 +65,11 @@ impl BackupPlainTextContent { Ok(encrypted_bytes) } - pub(crate) fn from_encrypted_backup( + pub(crate) fn restore_key_manager( + secure_storage: SecureStorage, encrypted_bytes: &[u8], keys: &BackupPasswordKeys, - ) -> Result { + ) -> Result<()> { if encrypted_bytes.len() < 12 { return Err(TwonlyError::Generic( "Invalid encrypted backup length".into(), @@ -84,10 +84,22 @@ impl BackupPlainTextContent { let decrypted_bytes = cipher.decrypt(nonce, ciphertext)?; - Ok(postcard::from_bytes(&decrypted_bytes)?) + let key_manager: KeyManager = postcard::from_bytes(&decrypted_bytes)?; + + key_manager.store_to_keychain(&secure_storage)?; + + Ok(()) } } +#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +pub(crate) struct BackupPlainTextContent { + pub(crate) user_id: i64, + pub(crate) key_manager: KeyManager, +} + +impl BackupPlainTextContent {} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/bridge/wrapper/key_manager.rs b/rust/src/bridge/wrapper/key_manager.rs new file mode 100644 index 00000000..efe1dc88 --- /dev/null +++ b/rust/src/bridge/wrapper/key_manager.rs @@ -0,0 +1,12 @@ +use crate::error::Result; +use crate::{bridge::get_twonly_flutter, keys::KeyManager}; + +pub struct FlutterKeyManager {} + +impl FlutterKeyManager { + pub async fn get_login_token() -> Result> { + let ctx = get_twonly_flutter()?; + let key_manager = KeyManager::try_from_keychain(&ctx.secure_storage)?; + Ok(key_manager.main_key.get_login_token().to_vec()) + } +} diff --git a/rust/src/bridge/wrapper/mod.rs b/rust/src/bridge/wrapper/mod.rs index 16456710..e76f21d2 100644 --- a/rust/src/bridge/wrapper/mod.rs +++ b/rust/src/bridge/wrapper/mod.rs @@ -1 +1,2 @@ +pub mod key_manager; pub mod user_discovery; diff --git a/rust/src/context.rs b/rust/src/context.rs index 50caf212..5b8a01c3 100644 --- a/rust/src/context.rs +++ b/rust/src/context.rs @@ -99,7 +99,7 @@ impl Context { Err(err) => { tracing::error!("{err}"); if rust_db_path.exists() { - tracing::error!("Rust Database exsist, while the key manager not. This must be a secure storage error."); + tracing::error!("Rust Database exists, while the key manager not. This must be a secure storage error."); return Err(TwonlyError::SecureStorageError); } tracing::info!("Generating a new key manager."); diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 48193e15..d5e35e75 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1680338106; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1007286393; // Section: executor @@ -46,6 +46,21 @@ flutter_rust_bridge::frb_generated_default_handler!(); // Section: wire_funcs +fn wire__crate__bridge__wrapper__key_manager__flutter_key_manager_get_login_token_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "flutter_key_manager_get_login_token", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { + let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; + let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); + deserializer.end(); move |context| async move { + transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { + let output_ok = crate::bridge::wrapper::key_manager::FlutterKeyManager::get_login_token().await?; Ok(output_ok) + })().await) + } }) +} fn wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -729,6 +744,13 @@ impl SseDecode for bool { } } +impl SseDecode for crate::bridge::wrapper::key_manager::FlutterKeyManager { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + return crate::bridge::wrapper::key_manager::FlutterKeyManager {}; + } +} + impl SseDecode for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -917,14 +939,15 @@ fn pde_ffi_dispatcher_primary_impl( ) { // Codec=Pde (Serialization + dispatch), see doc to use other codecs match func_id { - 1 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len), -2 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_new_messages_impl(port, ptr, rust_vec_len, data_len), -3 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_handle_new_messages_impl(port, ptr, rust_vec_len, data_len), -4 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_initialize_or_update_impl(port, ptr, rust_vec_len, data_len), -5 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_should_request_new_messages_impl(port, ptr, rust_vec_len, data_len), -6 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len), -7 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len), -8 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len), + 1 => wire__crate__bridge__wrapper__key_manager__flutter_key_manager_get_login_token_impl(port, ptr, rust_vec_len, data_len), +2 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len), +3 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_new_messages_impl(port, ptr, rust_vec_len, data_len), +4 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_handle_new_messages_impl(port, ptr, rust_vec_len, data_len), +5 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_initialize_or_update_impl(port, ptr, rust_vec_len, data_len), +6 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_should_request_new_messages_impl(port, ptr, rust_vec_len, data_len), +7 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len), +8 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len), +9 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -966,6 +989,23 @@ impl flutter_rust_bridge::IntoIntoDart> } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::bridge::wrapper::key_manager::FlutterKeyManager { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + Vec::::new().into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::bridge::wrapper::key_manager::FlutterKeyManager +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::bridge::wrapper::key_manager::FlutterKeyManager +{ + fn into_into_dart(self) -> crate::bridge::wrapper::key_manager::FlutterKeyManager { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery { @@ -1073,6 +1113,11 @@ impl SseEncode for bool { } } +impl SseEncode for crate::bridge::wrapper::key_manager::FlutterKeyManager { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + impl SseEncode for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} diff --git a/rust/src/keys/main_key.rs b/rust/src/keys/main_key.rs index 9a90885d..812f9464 100644 --- a/rust/src/keys/main_key.rs +++ b/rust/src/keys/main_key.rs @@ -40,12 +40,12 @@ impl MainKey { /// Download token required to download a backup. /// This ensures that the user who tries to download the backup must have knowledge over the /// main key - pub fn backup_download_token(&self) -> [u8; 32] { + pub fn get_backup_download_token(&self) -> [u8; 32] { self.derive_key(b"backup_download_token") } /// Uses as a password to authenitcate agains the server - pub fn server_auth_token(&self) -> [u8; 32] { + pub fn get_login_token(&self) -> [u8; 32] { self.derive_key(b"server_auth_token") } diff --git a/rust/src/keys/mod.rs b/rust/src/keys/mod.rs index 215cdb4e..252f735a 100644 --- a/rust/src/keys/mod.rs +++ b/rust/src/keys/mod.rs @@ -7,6 +7,7 @@ use crate::error::TwonlyError; pub(crate) use crate::keys::identity_key::IdentityKey; pub(crate) use crate::keys::main_key::{DatabaseKey, MainKey}; use crate::secure_storage::SecureStorage; +use aes_gcm::Aes256Gcm; use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -14,6 +15,7 @@ const KEY_MANAGER_ID: &str = "twonly_key_manager"; #[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub(crate) struct KeyManager { + pub(crate) user_id: Option, pub(crate) main_key: MainKey, pub(crate) identity_keys: Vec, pub(crate) backup_password: Option, @@ -25,6 +27,7 @@ impl KeyManager { main_key: MainKey::generate(), identity_keys: vec![], backup_password: None, + user_id: None, }) }