Browse Source

Add support for handling otpauth:// scheme

Neeraj Gupta 2 years ago
parent
commit
ea3cd1844b

+ 7 - 0
android/app/src/main/AndroidManifest.xml

@@ -28,6 +28,13 @@
                 <data android:scheme="ente-auth"/>
             </intent-filter>
 
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="otpauth" />
+            </intent-filter>
+
         </activity>
 
         <!-- Don't delete the meta-data below.

+ 75 - 62
ios/Runner/Info.plist

@@ -1,68 +1,81 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
-	<dict>
-		<key>CADisableMinimumFrameDurationOnPhone</key>
-		<true/>
-		<key>CFBundleDevelopmentRegion</key>
-		<string>$(DEVELOPMENT_LANGUAGE)</string>
-		<key>CFBundleDisplayName</key>
-		<string>auth</string>
-		<key>CFBundleExecutable</key>
-		<string>$(EXECUTABLE_NAME)</string>
-		<key>CFBundleIdentifier</key>
-		<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-		<key>CFBundleInfoDictionaryVersion</key>
-		<string>6.0</string>
-		<key>CFBundleLocalizations</key>
-		<array>
-			<string>en</string>
-			<string>es</string>
-		</array>
-		<key>CFBundleName</key>
-		<string>auth</string>
-		<key>CFBundlePackageType</key>
-		<string>APPL</string>
-		<key>CFBundleShortVersionString</key>
-		<string>$(FLUTTER_BUILD_NAME)</string>
-		<key>CFBundleSignature</key>
-		<string>????</string>
-		<key>CFBundleVersion</key>
-		<string>$(FLUTTER_BUILD_NUMBER)</string>
-		<key>LSRequiresIPhoneOS</key>
-		<true/>
-		<key>ITSAppUsesNonExemptEncryption</key>
-		<false/>
-		<key>MinimumOSVersion</key>
-		<string>12.0</string>
-		<key>NSCameraUsageDescription</key>
-		<string>This app needs camera access to scan QR codes</string>
-		<key>UILaunchStoryboardName</key>
-		<string>LaunchScreen</string>
-		<key>UIMainStoryboardFile</key>
-		<string>Main</string>
-		<key>UIStatusBarHidden</key>
-		<false/>
-		<key>UISupportedInterfaceOrientations</key>
-		<array>
-			<string>UIInterfaceOrientationPortrait</string>
-			<string>UIInterfaceOrientationLandscapeLeft</string>
-			<string>UIInterfaceOrientationLandscapeRight</string>
-		</array>
-		<key>UISupportedInterfaceOrientations~ipad</key>
-		<array>
-			<string>UIInterfaceOrientationPortrait</string>
-			<string>UIInterfaceOrientationPortraitUpsideDown</string>
-			<string>UIInterfaceOrientationLandscapeLeft</string>
-			<string>UIInterfaceOrientationLandscapeRight</string>
-		</array>
-		<key>UIViewControllerBasedStatusBarAppearance</key>
-		<false/>
-		<key>NSFaceIDUsageDescription</key>
-		<string>Please allow auth to lock itself with FaceID or TouchID</string>
-		<key>NSPhotoLibraryUsageDescription</key>
-		<string>Please allow auth to pick a file to import data from</string>
-		<key>UIApplicationSupportsIndirectInputEvents</key>
+<dict>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
 	<true/>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>auth</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleLocalizations</key>
+	<array>
+		<string>en</string>
+		<string>es</string>
+	</array>
+	<key>CFBundleName</key>
+	<string>auth</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(FLUTTER_BUILD_NAME)</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleURLTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>CFBundleURLName</key>
+			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>otpauth</string>
+			</array>
+		</dict>
+	</array>
+	<key>CFBundleVersion</key>
+	<string>$(FLUTTER_BUILD_NUMBER)</string>
+	<key>ITSAppUsesNonExemptEncryption</key>
+	<false/>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>MinimumOSVersion</key>
+	<string>12.0</string>
+	<key>NSCameraUsageDescription</key>
+	<string>This app needs camera access to scan QR codes</string>
+	<key>NSFaceIDUsageDescription</key>
+	<string>Please allow auth to lock itself with FaceID or TouchID</string>
+	<key>NSPhotoLibraryUsageDescription</key>
+	<string>Please allow auth to pick a file to import data from</string>
+	<key>UIApplicationSupportsIndirectInputEvents</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIStatusBarHidden</key>
+	<false/>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<false/>
 </dict>
 </plist>

+ 58 - 3
lib/ui/home_page.dart

@@ -1,6 +1,7 @@
 import 'dart:async';
 import 'dart:io';
 
+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/codes_updated_event.dart';
@@ -19,10 +20,14 @@ import 'package:ente_auth/ui/home/home_empty_state.dart';
 import 'package:ente_auth/ui/home/speed_dial_label_widget.dart';
 import 'package:ente_auth/ui/scanner_page.dart';
 import 'package:ente_auth/ui/settings_page.dart';
+import 'package:ente_auth/utils/dialog_util.dart';
+import 'package:ente_auth/utils/totp_util.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter_speed_dial/flutter_speed_dial.dart';
 import 'package:logging/logging.dart';
 import 'package:move_to_background/move_to_background.dart';
+import 'package:uni_links/uni_links.dart';
 
 class HomePage extends StatefulWidget {
   const HomePage({Key? key}) : super(key: key);
@@ -58,16 +63,15 @@ class _HomePageState extends State<HomePage> {
         Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
       await autoLogoutAlert(context);
     });
+    _initDeepLinks();
     super.initState();
   }
 
   void _loadCodes() {
     CodeStore.instance.getAllCodes().then((codes) {
       _codes = codes;
-      _filteredCodes = codes;
-
       _hasLoaded = true;
-      setState(() {});
+      _applyFiltering();
     });
   }
 
@@ -263,6 +267,57 @@ class _HomePageState extends State<HomePage> {
     }
   }
 
+  Future<bool> _initDeepLinks() async {
+    // Platform messages may fail, so we use a try/catch PlatformException.
+    try {
+      final String? initialLink = await getInitialLink();
+      // Parse the link and warn the user, if it is not correct,
+      // but keep in mind it could be `null`.
+      if (initialLink != null) {
+        _handleDeeplink(context, initialLink);
+        return true;
+      } else {
+        _logger.info("No initial link received.");
+      }
+    } on PlatformException {
+      // Handle exception by warning the user their action did not succeed
+      // return?
+      _logger.severe("PlatformException thrown while getting initial link");
+    }
+
+    // Attach a listener to the stream
+    linkStream.listen(
+      (String? link) {
+        _handleDeeplink(context, link);
+      },
+      onError: (err) {
+        _logger.severe(err);
+      },
+    );
+    return false;
+  }
+
+  void _handleDeeplink(BuildContext context, String? link) {
+    if (!Configuration.instance.hasConfiguredAccount() || link == null) {
+      return;
+    }
+    if (mounted && link.toLowerCase().startsWith("otpauth://")) {
+      try {
+        final newCode = Code.fromRawData(link);
+        getNextTotp(newCode);
+        CodeStore.instance.addCode(newCode);
+        _showSearchBox = true;
+        _textController.text = newCode.account;
+        _searchText = newCode.account;
+        _applyFiltering();
+        setState(() {});
+      } catch (e, s) {
+        showGenericErrorDialog(context: context);
+        _logger.severe("error while handling deeplink", e, s);
+      }
+    }
+  }
+
   Widget _getFab() {
     return SpeedDial(
       icon: Icons.add,

+ 0 - 2
lib/ui/settings/support_section_widget.dart

@@ -1,5 +1,3 @@
-
-
 import 'package:ente_auth/core/constants.dart';
 import 'package:ente_auth/core/logging/super_logging.dart';
 import 'package:ente_auth/l10n/l10n.dart';