From 66a76a07677d59237f4b9c057a293dfa3c6a2868 Mon Sep 17 00:00:00 2001 From: Vishesh Handa Date: Wed, 10 Jun 2020 11:46:55 +0200 Subject: [PATCH] Revert "Use flutter_web_auth instead of our own OAuth mechanism" This reverts commit 763cbf8493c610dec0e7e344bee40ad331e7a272. This reverts commit ddad699b259bafe6c7ed630e7afc2eb38b7825e6. This is causing way too many problems - On Android with GitHub we occasionally get a User Cancelled exception. On iOS this doesn't work < ios11 I prefer keeping my way till then. Even though it doesn't support KeepAlive on Android. --- android/app/src/main/AndroidManifest.xml | 7 +- .../gitjournal/UriReceiverActivity.java | 35 ++++++++++ android/build.gradle | 3 +- ios/Runner/AppDelegate.m | 13 ++++ lib/apis/githost.dart | 3 +- lib/apis/github.dart | 55 ++++++++++----- lib/apis/gitlab.dart | 69 ++++++++++--------- lib/oauthapp.dart | 9 ++- lib/setup/autoconfigure.dart | 48 ++++++++----- pubspec.lock | 7 -- pubspec.yaml | 1 - 11 files changed, 170 insertions(+), 80 deletions(-) create mode 100644 android/app/src/main/java/io/gitjournal/gitjournal/UriReceiverActivity.java diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 44531033..b256d8d2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -53,12 +53,15 @@ - + - + + diff --git a/android/app/src/main/java/io/gitjournal/gitjournal/UriReceiverActivity.java b/android/app/src/main/java/io/gitjournal/gitjournal/UriReceiverActivity.java new file mode 100644 index 00000000..4cc670ab --- /dev/null +++ b/android/app/src/main/java/io/gitjournal/gitjournal/UriReceiverActivity.java @@ -0,0 +1,35 @@ +package io.gitjournal.gitjournal; + +import android.os.Bundle; +import android.util.Log; +import android.app.Activity; +import android.net.*; + +import android.content.Intent; + +import java.util.HashMap; +import java.util.Map; + +public class UriReceiverActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Uri data = getIntent().getData(); + + Map map = new HashMap<>(); + map.put("URL", data.toString()); + + MainActivity.channel.invokeMethod("onURL", map); + + // Now that all data has been sent back to Dart-land, we should re-open the Flutter + // activity. Due to the manifest-setting of the MainActivity ("singleTop), only a single + // instance will exist, popping the old one back up and destroying the preceding + // activities on the backstack, such as the custom tab. + // Flags taken from how the AppAuth-library accomplishes the same thing + Intent mainIntent = new Intent(this, MainActivity.class); + mainIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + startActivity(mainIntent); + finish(); + } + +} diff --git a/android/build.gradle b/android/build.gradle index 63e07adf..7933084d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,4 @@ buildscript { - ext.kotlin_version = '1.3.50' repositories { google() jcenter() @@ -11,7 +10,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.3.2' classpath 'com.google.gms:google-services:3.2.1' classpath 'io.fabric.tools:gradle:1.+' } diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m index c923f7ea..73bd6d03 100644 --- a/ios/Runner/AppDelegate.m +++ b/ios/Runner/AppDelegate.m @@ -426,4 +426,17 @@ bool handleError(FlutterResult result, int err) { } } +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + NSLog(@"openUrl called with url %@", url); + for (NSString *key in [options allKeys]) { + NSLog(@". %@: %@", key, [options objectForKey:key]); + } + + NSDictionary *args = @{@"URL": [url absoluteString]}; + [gitChannel invokeMethod:@"onURL" arguments:args]; + + return true; +} @end diff --git a/lib/apis/githost.dart b/lib/apis/githost.dart index df842441..1704f788 100644 --- a/lib/apis/githost.dart +++ b/lib/apis/githost.dart @@ -5,7 +5,8 @@ import 'package:flutter/foundation.dart'; typedef OAuthCallback = void Function(GitHostException); abstract class GitHost { - Future init(); + void init(OAuthCallback oAuthCallback); + Future launchOAuthScreen(); Future getUserInfo(); Future> listRepos(); diff --git a/lib/apis/github.dart b/lib/apis/github.dart index 7aaa63dc..b9af0195 100644 --- a/lib/apis/github.dart +++ b/lib/apis/github.dart @@ -2,8 +2,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; -import 'package:flutter_web_auth/flutter_web_auth.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:gitjournal/utils/logger.dart'; import 'githost.dart'; @@ -12,29 +13,39 @@ class GitHub implements GitHost { static const _clientID = "aa3072cbfb02b1db14ed"; static const _clientSecret = "010d303ea99f82330f2b228977cef9ddbf7af2cd"; + var _platform = const MethodChannel('gitjournal.io/git'); var _accessCode = ""; @override - Future init() async { - var url = "https://github.com/login/oauth/authorize?client_id=" + - _clientID + - "&scope=repo"; + void init(OAuthCallback callback) { + Future _handleMessages(MethodCall call) async { + if (call.method != "onURL") { + Log.d("GitHub Unknown Call: " + call.method); + return; + } - var resultUrl = await FlutterWebAuth.authenticate( - url: url, callbackUrlScheme: "gitjournal"); + closeWebView(); + Log.d("GitHub: Called onUrl with " + call.arguments.toString()); - var uri = Uri.parse(resultUrl); - var authCode = uri.queryParameters['code']; - if (authCode == null) { - Log.d("GitHub: Missing auth code. Now what?"); - throw GitHostException.OAuthFailed; + String url = call.arguments["URL"]; + var uri = Uri.parse(url); + var authCode = uri.queryParameters['code']; + if (authCode == null) { + Log.d("GitHub: Missing auth code. Now what?"); + callback(GitHostException.OAuthFailed); + } + + _accessCode = await _getAccessCode(authCode); + if (_accessCode == null || _accessCode.isEmpty) { + Log.d("GitHub: AccessCode is invalid: " + _accessCode); + callback(GitHostException.OAuthFailed); + } + + callback(null); } - _accessCode = await _getAccessCode(authCode); - if (_accessCode == null || _accessCode.isEmpty) { - Log.d("GitHub: AccessCode is invalid: " + _accessCode); - throw GitHostException.OAuthFailed; - } + _platform.setMethodCallHandler(_handleMessages); + Log.d("GitHub: Installed Handler"); } Future _getAccessCode(String authCode) async { @@ -55,6 +66,16 @@ class GitHub implements GitHost { return map["access_token"]; } + @override + Future launchOAuthScreen() async { + // FIXME: Add some 'state' over here! + + var url = "https://github.com/login/oauth/authorize?client_id=" + + _clientID + + "&scope=repo"; + return launch(url); + } + @override Future> listRepos() async { if (_accessCode.isEmpty) { diff --git a/lib/apis/gitlab.dart b/lib/apis/gitlab.dart index 29fae2c7..66fcacaf 100644 --- a/lib/apis/gitlab.dart +++ b/lib/apis/gitlab.dart @@ -3,8 +3,9 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; -import 'package:flutter_web_auth/flutter_web_auth.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:gitjournal/utils/logger.dart'; import 'githost.dart'; @@ -13,48 +14,54 @@ class GitLab implements GitHost { static const _clientID = "faf33c3716faf05bfb701b1b31e36c83a23c3ec2d7161f4ff00fba2275524d09"; + var _platform = const MethodChannel('gitjournal.io/git'); var _accessCode = ""; var _stateOAuth = ""; @override - Future init() async { - _stateOAuth = _randomString(10); + void init(OAuthCallback callback) { + Future _handleMessages(MethodCall call) async { + if (call.method != "onURL") { + Log.d("GitLab Unknown Call: " + call.method); + return; + } - var launchUrl = - "https://gitlab.com/oauth/authorize?client_id=$_clientID&response_type=token&state=$_stateOAuth&redirect_uri=gitjournal://login.oauth2"; + closeWebView(); + Log.d("GitLab: Called onUrl with " + call.arguments.toString()); - var url = await FlutterWebAuth.authenticate( - url: launchUrl, callbackUrlScheme: "gitjournal"); + String url = call.arguments["URL"]; + var queryParamters = url.substring(url.indexOf('#') + 1); + var map = Uri.splitQueryString(queryParamters); - var receievedState = _fetchQueryParam(url, "state"); - if (receievedState != _stateOAuth) { - Log.d("GitLab: OAuth State incorrect"); - Log.d("Required State: $_stateOAuth"); - Log.d("Actual State: $receievedState"); - throw GitHostException.OAuthFailed; + var state = map['state']; + if (state != _stateOAuth) { + Log.d("GitLab: OAuth State incorrect"); + Log.d("Required State: " + _stateOAuth); + Log.d("Actual State: " + state); + callback(GitHostException.OAuthFailed); + return; + } + + _accessCode = map['access_token']; + if (_accessCode == null) { + callback(GitHostException.OAuthFailed); + return; + } + + callback(null); } - _accessCode = _fetchQueryParam(url, "access_token"); - if (_accessCode == null) { - throw GitHostException.OAuthFailed; - } + _platform.setMethodCallHandler(_handleMessages); + Log.d("GitLab: Installed Handler"); } - // Example: gitjournal://login.oauth2#access_token=49ce9d1s11145acc7bddf0b6b2a5fbe2a15496e4975808731e054eceeb49468f&token_type=Bearer&state=qxpYY%5CckY%5D - String _fetchQueryParam(String url, String param) { - var map = Uri.parse(url).queryParameters; - var value = map[param]; - if (value != null && value.isNotEmpty) { - return value; - } + @override + Future launchOAuthScreen() async { + _stateOAuth = _randomString(10); - var paramIndex = url.indexOf("$param="); - if (paramIndex != -1) { - var stateStr = url.substring(paramIndex + "$param=".length).split('&')[0]; - return Uri.decodeQueryComponent(stateStr); - } - - return ""; + var url = + "https://gitlab.com/oauth/authorize?client_id=$_clientID&response_type=token&state=$_stateOAuth&redirect_uri=gitjournal://login.oauth2"; + return launch(url); } @override diff --git a/lib/oauthapp.dart b/lib/oauthapp.dart index 3eadda38..a9ada6fc 100644 --- a/lib/oauthapp.dart +++ b/lib/oauthapp.dart @@ -20,6 +20,13 @@ class OAuthAppState extends State { super.initState(); githost = createGitHost(GitHostType.GitHub); + githost.init((GitHostException e) { + if (e != null) { + print("Got exeception: $e"); + } else { + print("GitHost initialized and has access code"); + } + }); } @override @@ -34,7 +41,7 @@ class OAuthAppState extends State { RaisedButton( child: const Text("Open OAuth URL"), onPressed: () { - githost.init(); + githost.launchOAuthScreen(); }, ), RaisedButton( diff --git a/lib/setup/autoconfigure.dart b/lib/setup/autoconfigure.dart index a1427346..b0330833 100644 --- a/lib/setup/autoconfigure.dart +++ b/lib/setup/autoconfigure.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:function_types/function_types.dart'; import 'package:gitjournal/analytics.dart'; @@ -41,28 +42,39 @@ class GitHostSetupAutoConfigureState extends State { gitHost = createGitHost(widget.gitHostType); try { - await gitHost.init(); + gitHost.init((Exception error) async { + if (error != null) { + throw error; + } + Log.d("GitHost Initalized: " + widget.gitHostType.toString()); - Log.d("GitHost Initalized: " + widget.gitHostType.toString()); + try { + setState(() { + _message = "Reading User Info"; + }); + + var userInfo = await gitHost.getUserInfo(); + if (userInfo.name != null && userInfo.name.isNotEmpty) { + Settings.instance.gitAuthor = userInfo.name; + } + if (userInfo.email != null && userInfo.email.isNotEmpty) { + Settings.instance.gitAuthorEmail = userInfo.email; + } + Settings.instance.save(); + } on Exception catch (e, stacktrace) { + _handleGitHostException(e, stacktrace); + return; + } + widget.onDone(gitHost); + }); try { - setState(() { - _message = "Reading User Info"; - }); - - var userInfo = await gitHost.getUserInfo(); - if (userInfo.name != null && userInfo.name.isNotEmpty) { - Settings.instance.gitAuthor = userInfo.name; - } - if (userInfo.email != null && userInfo.email.isNotEmpty) { - Settings.instance.gitAuthorEmail = userInfo.email; - } - Settings.instance.save(); - } on Exception catch (e, stacktrace) { - _handleGitHostException(e, stacktrace); - return; + await gitHost.launchOAuthScreen(); + } on PlatformException catch (e, stack) { + print("LaunchOAuthScreen: Caught platform exception: " + e.toString()); + print(stack); + print("Ignoring it, since I don't know what else to do"); } - widget.onDone(gitHost); } on Exception catch (e, stacktrace) { _handleGitHostException(e, stacktrace); } diff --git a/pubspec.lock b/pubspec.lock index 24693a61..e910f235 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -303,13 +303,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_web_auth: - dependency: "direct main" - description: - name: flutter_web_auth - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.4" flutter_web_plugins: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 2301e60d..01bb139c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,6 @@ dependencies: quick_actions: ^0.4.0+5 receive_sharing_intent: ^1.4.0+2 flutter_plugin_android_lifecycle: ^1.0.8 # for fixing the build - flutter_web_auth: ^0.2.4 dev_dependencies: flutter_launcher_icons: "^0.7.2"