Bladeren bron

Merge pull request #84 from ente-io/handle_logout

Handle invalid sessions
Neeraj Gupta 2 jaren geleden
bovenliggende
commit
33c3b997ed

+ 3 - 0
lib/events/trigger_logout_event.dart

@@ -0,0 +1,3 @@
+import 'package:ente_auth/events/event.dart';
+
+class TriggerLogoutEvent extends Event {}

+ 25 - 17
lib/gateway/authenticator.dart

@@ -103,24 +103,32 @@ class AuthenticatorGateway {
   }
 
   Future<List<AuthEntity>> getDiff(int sinceTime, {int limit = 500}) async {
-    final response = await _dio.get(
-      _basedEndpoint + "/entity/diff",
-      queryParameters: {
-        "sinceTime": sinceTime,
-        "limit": limit,
-      },
-      options: Options(
-        headers: {
-          "X-Auth-Token": _config.getToken(),
+    try {
+      final response = await _dio.get(
+        _basedEndpoint + "/entity/diff",
+        queryParameters: {
+          "sinceTime": sinceTime,
+          "limit": limit,
         },
-      ),
-    );
-    final List<AuthEntity> authEntities = <AuthEntity>[];
-    final diff = response.data["diff"] as List;
-    for (var entry in diff) {
-      final AuthEntity entity = AuthEntity.fromMap(entry);
-      authEntities.add(entity);
+        options: Options(
+          headers: {
+            "X-Auth-Token": _config.getToken(),
+          },
+        ),
+      );
+      final List<AuthEntity> authEntities = <AuthEntity>[];
+      final diff = response.data["diff"] as List;
+      for (var entry in diff) {
+        final AuthEntity entity = AuthEntity.fromMap(entry);
+        authEntities.add(entity);
+      }
+      return authEntities;
+    } catch (e) {
+      if (e is DioError && e.response?.statusCode == 401) {
+        throw UnauthorizedError();
+      } else {
+        rethrow;
+      }
     }
-    return authEntities;
   }
 }

+ 0 - 1
lib/l10n/arb/app_de.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "de",
   "counterAppBarTitle": "Zähler",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"

+ 7 - 2
lib/l10n/arb/app_en.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "en",
   "counterAppBarTitle": "Counter",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"
@@ -14,6 +13,12 @@
   "codeSecretKeyHint" : "Secret Key",
   "codeAccountHint": "Account (you@domain.com)",
   "accountKeyType": "Type of key",
+  "sessionExpired": "Session expired",
+  "@sessionExpired": {
+    "description": "Title of the dialog when the users current session is invalid/expired"
+  },
+  "pleaseLoginAgain": "Please login again",
+  "loggingOut" : "Logging out...",
   "timeBasedKeyType": "Time based (TOTP)",
   "counterBasedKeyType": "Counter based (HOTP)",
   "saveAction": "Save",
@@ -58,7 +63,7 @@
   "supportDevs" : "Subscribe to <bold-green>ente</bold-green> to support this project.",
   "supportDiscount" : "Use coupon code \"AUTH\" to get 10% off first year",
   "changeEmail": "change email",
-  "ok": "OK",
+  "ok": "Ok",
   "cancel": "Cancel",
   "yes": "Yes",
   "no": "No",

+ 0 - 1
lib/l10n/arb/app_es.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "es",
   "counterAppBarTitle": "Contador",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"

+ 0 - 1
lib/l10n/arb/app_fr.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "fr",
   "counterAppBarTitle": "Compteur",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"

+ 0 - 1
lib/l10n/arb/app_it.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "it",
   "counterAppBarTitle": "Contatore",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"

+ 0 - 1
lib/l10n/arb/app_nl.arb

@@ -1,5 +1,4 @@
 {
-  "@@locale": "nl",
   "counterAppBarTitle": "Teller",
   "@counterAppBarTitle": {
     "description": "Text shown in the AppBar of the Counter Page"

+ 0 - 1
lib/l10n/arb/app_pt.arb

@@ -1,3 +1,2 @@
 {
-  "@@locale": "pt"
 }

+ 0 - 1
lib/l10n/arb/app_zh.arb

@@ -1,3 +1,2 @@
 {
-  "@@locale": "zh"
 }

+ 3 - 0
lib/models/authenticator/local_auth_entity.dart

@@ -5,15 +5,18 @@ import 'package:flutter/material.dart';
 @immutable
 class LocalAuthEntity {
   final int generatedID;
+
   // id can be null if a code has been scanned locally but it's yet to be
   // synced with the remote server.
   final String? id;
   final String encryptedData;
   final String header;
+
   // createdAt and updateAt will be equal to local time of creation or updation
   // till remote sync is completed.
   final int createdAt;
   final int updatedAt;
+
   // shouldSync indicates that the entry was locally created or updated. The
   // app should try to sync it to the server during next sync
   final bool shouldSync;

+ 22 - 0
lib/onboarding/view/onboarding_page.dart

@@ -1,10 +1,15 @@
 // ignore_for_file: import_of_legacy_library_into_null_safe
 
+import 'dart:async';
+
 import 'package:ente_auth/core/configuration.dart';
+import 'package:ente_auth/core/event_bus.dart';
 import 'package:ente_auth/ente_theme_data.dart';
+import 'package:ente_auth/events/trigger_logout_event.dart';
 import "package:ente_auth/l10n/l10n.dart";
 import 'package:ente_auth/ui/account/email_entry_page.dart';
 import 'package:ente_auth/ui/account/login_page.dart';
+import 'package:ente_auth/ui/account/logout_dialog.dart';
 import 'package:ente_auth/ui/account/password_entry_page.dart';
 import 'package:ente_auth/ui/account/password_reentry_page.dart';
 import 'package:ente_auth/ui/common/gradient_button.dart';
@@ -19,6 +24,23 @@ class OnboardingPage extends StatefulWidget {
 }
 
 class _OnboardingPageState extends State<OnboardingPage> {
+  late StreamSubscription<TriggerLogoutEvent> _triggerLogoutEvent;
+
+  @override
+  void initState() {
+    _triggerLogoutEvent =
+        Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
+      await autoLogoutAlert(context);
+    });
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    _triggerLogoutEvent.cancel();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     debugPrint("Building OnboardingPage");

+ 9 - 1
lib/services/authenticator_service.dart

@@ -8,6 +8,7 @@ import 'package:ente_auth/core/event_bus.dart';
 import 'package:ente_auth/core/network.dart';
 import 'package:ente_auth/events/codes_updated_event.dart';
 import 'package:ente_auth/events/signed_in_event.dart';
+import 'package:ente_auth/events/trigger_logout_event.dart';
 import 'package:ente_auth/gateway/authenticator.dart';
 import 'package:ente_auth/models/authenticator/auth_entity.dart';
 import 'package:ente_auth/models/authenticator/auth_key.dart';
@@ -132,6 +133,13 @@ class AuthenticatorService {
       await _localToRemoteSync();
       _logger.info("local push completed");
       Bus.instance.fire(CodesUpdatedEvent());
+    } on UnauthorizedError {
+      if ((await _db.removeSyncedData()) > 0) {
+        Bus.instance.fire(CodesUpdatedEvent());
+      }
+      debugPrint("Firing logout event");
+
+      Bus.instance.fire(TriggerLogoutEvent());
     } catch (e) {
       _logger.severe("Failed to sync with remote", e);
     }
@@ -140,7 +148,7 @@ class AuthenticatorService {
   Future<void> _remoteToLocalSync() async {
     _logger.info('Initiating remote to local sync');
     final int lastSyncTime = _prefs.getInt(_lastEntitySyncTime) ?? 0;
-    _logger.info("Current synctime is " + lastSyncTime.toString());
+    _logger.info("Current sync is " + lastSyncTime.toString());
     const int fetchLimit = 500;
     final List<AuthEntity> result =
         await _gateway.getDiff(lastSyncTime, limit: fetchLimit);

+ 7 - 0
lib/store/authenticator_db.dart

@@ -135,6 +135,13 @@ class AuthenticatorDB {
     return _convertRows(rows);
   }
 
+  // removeSyncedData will remove all the data which is synced with the server
+  Future<int> removeSyncedData() async {
+    final db = await instance.database;
+    return await db
+        .delete(entityTable, where: 'shouldSync = ?', whereArgs: [0]);
+  }
+
 // deleteByID will prefer generated id if both ids are passed during deletion
   Future<void> deleteByIDs({List<int>? generatedIDs, List<String>? ids}) async {
     final db = await instance.database;

+ 36 - 0
lib/ui/account/logout_dialog.dart

@@ -0,0 +1,36 @@
+import 'package:ente_auth/core/configuration.dart';
+import 'package:ente_auth/l10n/l10n.dart';
+import 'package:ente_auth/utils/dialog_util.dart';
+import 'package:flutter/material.dart';
+
+Future<void> autoLogoutAlert(BuildContext context) async {
+  final l10n = context.l10n;
+  final AlertDialog alert = AlertDialog(
+    title: Text(l10n.sessionExpired),
+    content: Text(l10n.pleaseLoginAgain),
+    actions: [
+      TextButton(
+        child: Text(
+          l10n.ok,
+          style: TextStyle(
+            color: Theme.of(context).colorScheme.primary,
+          ),
+        ),
+        onPressed: () async {
+          Navigator.of(context, rootNavigator: true).pop('dialog');
+          Navigator.of(context).popUntil((route) => route.isFirst);
+          final dialog = createProgressDialog(context, l10n.loggingOut);
+          await dialog.show();
+          await Configuration.instance.logout();
+          await dialog.hide();
+        },
+      ),
+    ],
+  );
+  await showDialog(
+    context: context,
+    builder: (BuildContext context) {
+      return alert;
+    },
+  );
+}

+ 8 - 0
lib/ui/home_page.dart

@@ -7,12 +7,14 @@ import 'dart:ui';
 import 'package:ente_auth/core/event_bus.dart';
 import 'package:ente_auth/ente_theme_data.dart';
 import 'package:ente_auth/events/codes_updated_event.dart';
+import 'package:ente_auth/events/trigger_logout_event.dart';
 import "package:ente_auth/l10n/l10n.dart";
 import 'package:ente_auth/models/code.dart';
 import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart';
 import 'package:ente_auth/services/preference_service.dart';
 import 'package:ente_auth/services/user_service.dart';
 import 'package:ente_auth/store/code_store.dart';
+import 'package:ente_auth/ui/account/logout_dialog.dart';
 import 'package:ente_auth/ui/code_widget.dart';
 import 'package:ente_auth/ui/common/loading_widget.dart';
 import 'package:ente_auth/ui/scanner_page.dart';
@@ -43,6 +45,7 @@ class _HomePageState extends State<HomePage> {
   List<Code> _codes = [];
   List<Code> _filteredCodes = [];
   StreamSubscription<CodesUpdatedEvent>? _streamSubscription;
+  StreamSubscription<TriggerLogoutEvent>? _triggerLogoutEvent;
 
   @override
   void initState() {
@@ -51,6 +54,10 @@ class _HomePageState extends State<HomePage> {
     _streamSubscription = Bus.instance.on<CodesUpdatedEvent>().listen((event) {
       _loadCodes();
     });
+    _triggerLogoutEvent =
+        Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
+      await autoLogoutAlert(context);
+    });
     super.initState();
   }
 
@@ -84,6 +91,7 @@ class _HomePageState extends State<HomePage> {
   @override
   void dispose() {
     _streamSubscription?.cancel();
+    _triggerLogoutEvent?.cancel();
     _textController.removeListener(_applyFiltering);
     super.dispose();
   }