diff --git a/lib/pages/homeserver_picker.dart b/lib/pages/homeserver_picker.dart index fac56ff2..34204f1f 100644 --- a/lib/pages/homeserver_picker.dart +++ b/lib/pages/homeserver_picker.dart @@ -1,12 +1,21 @@ +import 'dart:async'; + import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/pages/views/homeserver_picker_view.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter/material.dart'; import '../utils/localized_exception_extension.dart'; import 'package:vrouter/vrouter.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'package:uni_links/uni_links.dart'; +import 'package:universal_html/html.dart' as html; + +import '../main.dart'; class HomeserverPicker extends StatefulWidget { @override @@ -18,6 +27,55 @@ class HomeserverPickerController extends State { String domain = AppConfig.defaultHomeserver; final TextEditingController homeserverController = TextEditingController(text: AppConfig.defaultHomeserver); + StreamSubscription _intentDataStreamSubscription; + + void _loginWithToken(String token) { + if (token?.isEmpty ?? true) return; + showFutureLoadingDialog( + context: context, + future: () => Matrix.of(context).client.login( + type: AuthenticationTypes.token, + token: token, + initialDeviceDisplayName: PlatformInfos.clientName, + ), + ); + } + + void _processIncomingUris(String text) async { + if (text == null || !text.startsWith(AppConfig.appOpenUrlScheme)) return; + VRouter.of(context).push('/home'); + final token = Uri.parse(text).queryParameters['loginToken']; + if (token != null) _loginWithToken(token); + } + + void _initReceiveUri() { + if (!PlatformInfos.isMobile) return; + // For receiving shared Uris + _intentDataStreamSubscription = linkStream.listen(_processIncomingUris); + if (FluffyChatApp.gotInitialLink == false) { + FluffyChatApp.gotInitialLink = true; + getInitialLink().then(_processIncomingUris); + } + } + + @override + void initState() { + super.initState(); + _initReceiveUri(); + if (kIsWeb) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final token = + Uri.parse(html.window.location.href).queryParameters['loginToken']; + _loginWithToken(token); + }); + } + } + + @override + void dispose() { + super.dispose(); + _intentDataStreamSubscription?.cancel(); + } /// Starts an analysis of the given homeserver. It uses the current domain and /// makes sure that it is prefixed with https. Then it searches for the diff --git a/lib/pages/sign_up.dart b/lib/pages/sign_up.dart index 7107f7c4..5acbfb6e 100644 --- a/lib/pages/sign_up.dart +++ b/lib/pages/sign_up.dart @@ -3,19 +3,14 @@ import 'dart:async'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/views/sign_up_view.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/famedlysdk_store.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:uni_links/uni_links.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:universal_html/html.dart' as html; -import 'package:vrouter/vrouter.dart'; - -import '../main.dart'; class SignUp extends StatefulWidget { @override @@ -25,7 +20,7 @@ class SignUp extends StatefulWidget { class SignUpController extends State { Map _rawLoginTypes; bool registrationSupported; - StreamSubscription _intentDataStreamSubscription; + List get identityProviders { if (!ssoLoginSupported) return []; final rawProviders = _rawLoginTypes.tryGetList('flows').singleWhere( @@ -36,54 +31,6 @@ class SignUpController extends State { .toList(); } - void _loginWithToken(String token) { - if (token?.isEmpty ?? true) return; - showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context).client.login( - type: AuthenticationTypes.token, - token: token, - initialDeviceDisplayName: PlatformInfos.clientName, - ), - ); - } - - void _processIncomingUris(String text) async { - if (text == null || !text.startsWith(AppConfig.appOpenUrlScheme)) return; - VRouter.of(context).push('/home'); - final token = Uri.parse(text).queryParameters['loginToken']; - if (token != null) _loginWithToken(token); - } - - void _initReceiveUri() { - if (!PlatformInfos.isMobile) return; - // For receiving shared Uris - _intentDataStreamSubscription = linkStream.listen(_processIncomingUris); - if (FluffyChatApp.gotInitialLink == false) { - FluffyChatApp.gotInitialLink = true; - getInitialLink().then(_processIncomingUris); - } - } - - @override - void initState() { - super.initState(); - _initReceiveUri(); - if (kIsWeb) { - WidgetsBinding.instance.addPostFrameCallback((_) { - final token = - Uri.parse(html.window.location.href).queryParameters['loginToken']; - _loginWithToken(token); - }); - } - } - - @override - void dispose() { - super.dispose(); - _intentDataStreamSubscription?.cancel(); - } - bool get passwordLoginSupported => Matrix.of(context) .client @@ -118,7 +65,15 @@ class SignUpController extends State { return _rawLoginTypes; } + static const String _ssoHomeserverKey = 'sso-homeserver'; + void ssoLoginAction(String id) { + if (kIsWeb) { + // We store the homserver in the local storage instead of a redirect + // parameter because of possible CSRF attacks. + Store().setItem( + _ssoHomeserverKey, Matrix.of(context).client.homeserver.toString()); + } final redirectUrl = kIsWeb ? html.window.location.href : AppConfig.appOpenUrlScheme.toLowerCase() + '://sso';