mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-23 20:49:26 +01:00
Merge branch 'krille/sso' into 'main'
feat: Implement sso Closes #13 See merge request famedly/fluffychat!346
This commit is contained in:
commit
5292f41d0c
@ -10,6 +10,7 @@ abstract class AppConfig {
|
||||
static const bool enableRegistration = true;
|
||||
static String _privacyUrl = 'https://fluffychat.im/en/privacy.html';
|
||||
static String get privacyUrl => _privacyUrl;
|
||||
static const String appId = 'im.fluffychat.FluffyChat';
|
||||
static const String sourceCodeUrl = 'https://gitlab.com/famedly/fluffychat';
|
||||
static const String supportUrl =
|
||||
'https://gitlab.com/famedly/fluffychat/issues';
|
||||
|
@ -156,8 +156,13 @@ class MatrixState extends State<Matrix> {
|
||||
),
|
||||
);
|
||||
default:
|
||||
Logs().w('Warning! Cannot handle the stage "$stage"');
|
||||
return;
|
||||
await widget.apl.currentState.pushNamed(
|
||||
'/authwebview/$stage/${uiaRequest.session}',
|
||||
arguments: () => null,
|
||||
);
|
||||
return uiaRequest.completeStage(
|
||||
AuthenticationData(session: uiaRequest.session),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import 'package:fluffychat/views/settings_notifications.dart';
|
||||
import 'package:fluffychat/views/settings_style.dart';
|
||||
import 'package:fluffychat/views/sign_up.dart';
|
||||
import 'package:fluffychat/views/sign_up_password.dart';
|
||||
import 'package:fluffychat/views/sso_web_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FluffyRoutes {
|
||||
@ -47,6 +48,8 @@ class FluffyRoutes {
|
||||
return ViewData(mainView: (_) => HomeserverPicker());
|
||||
case 'login':
|
||||
return ViewData(mainView: (_) => Login());
|
||||
case 'sso':
|
||||
return ViewData(mainView: (_) => SsoWebView());
|
||||
case 'signup':
|
||||
if (parts.length == 5 && parts[2] == 'password') {
|
||||
return ViewData(
|
||||
@ -129,6 +132,17 @@ class FluffyRoutes {
|
||||
mainView: (_) => Archive(),
|
||||
emptyView: (_) => EmptyPage(),
|
||||
);
|
||||
case 'authwebview':
|
||||
if (parts.length == 4) {
|
||||
return ViewData(
|
||||
mainView: (_) => AuthWebView(
|
||||
parts[2],
|
||||
Uri.decodeComponent(parts[3]),
|
||||
settings.arguments,
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'discover':
|
||||
return ViewData(
|
||||
mainView: (_) =>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:fluffychat/app_config.dart';
|
||||
import 'package:fluffychat/components/sentry_switch_list_tile.dart';
|
||||
@ -30,10 +31,32 @@ class _HomeserverPickerState extends State<HomeserverPicker> {
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
// Look up well known
|
||||
try {
|
||||
await Matrix.of(context).client.checkHomeserver(homeserver);
|
||||
final wellKnown = await MatrixApi(homeserver: Uri.parse(homeserver))
|
||||
.requestWellKnownInformations();
|
||||
homeserver = wellKnown.mHomeserver.baseUrl;
|
||||
} catch (e) {
|
||||
Logs().v('Found no well known information', e);
|
||||
}
|
||||
|
||||
try {
|
||||
await Matrix.of(context)
|
||||
.client
|
||||
.checkHomeserver(homeserver, supportedLoginTypes: {
|
||||
AuthenticationTypes.password,
|
||||
if (PlatformInfos.isMobile) AuthenticationTypes.sso
|
||||
});
|
||||
final loginTypes = await Matrix.of(context).client.requestLoginTypes();
|
||||
if (loginTypes.flows
|
||||
.any((flow) => flow.type == AuthenticationTypes.password)) {
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamed(AppConfig.enableRegistration ? '/signup' : '/login');
|
||||
} else if (loginTypes.flows
|
||||
.any((flow) => flow.type == AuthenticationTypes.sso)) {
|
||||
await AdaptivePageLayout.of(context).pushNamed('/sso');
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore: unawaited_futures
|
||||
FlushbarHelper.createError(
|
||||
|
@ -22,6 +22,9 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
||||
|
||||
void reload() => setState(() => devices = null);
|
||||
|
||||
bool _loadingDeletingDevices = false;
|
||||
String _errorDeletingDevices;
|
||||
|
||||
void _removeDevicesAction(BuildContext context, List<Device> devices) async {
|
||||
if (await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
@ -33,33 +36,24 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
||||
for (var userDevice in devices) {
|
||||
deviceIds.add(userDevice.deviceId);
|
||||
}
|
||||
final password = await showTextInputDialog(
|
||||
title: L10n.of(context).pleaseEnterYourPassword,
|
||||
context: context,
|
||||
textFields: [
|
||||
DialogTextField(
|
||||
hintText: '******',
|
||||
obscureText: true,
|
||||
minLines: 1,
|
||||
maxLines: 1,
|
||||
)
|
||||
],
|
||||
);
|
||||
if (password == null) return;
|
||||
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => matrix.client.deleteDevices(
|
||||
try {
|
||||
setState(() {
|
||||
_loadingDeletingDevices = true;
|
||||
_errorDeletingDevices = null;
|
||||
});
|
||||
await matrix.client.uiaRequestBackground(
|
||||
(auth) => matrix.client.deleteDevices(
|
||||
deviceIds,
|
||||
auth: AuthenticationPassword(
|
||||
password: password.single,
|
||||
user: matrix.client.userID,
|
||||
identifier: AuthenticationUserIdentifier(user: matrix.client.userID),
|
||||
),
|
||||
auth: auth,
|
||||
),
|
||||
);
|
||||
if (success.error == null) {
|
||||
reload();
|
||||
} catch (e, s) {
|
||||
Logs().v('Error while deleting devices', e, s);
|
||||
setState(() => _errorDeletingDevices = e.toString());
|
||||
} finally {
|
||||
setState(() => _loadingDeletingDevices = false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,11 +121,16 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
||||
if (devices.isNotEmpty)
|
||||
ListTile(
|
||||
title: Text(
|
||||
_errorDeletingDevices ??
|
||||
L10n.of(context).removeAllOtherDevices,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
trailing: Icon(Icons.delete_outline),
|
||||
onTap: () => _removeDevicesAction(context, devices),
|
||||
trailing: _loadingDeletingDevices
|
||||
? CircularProgressIndicator()
|
||||
: Icon(Icons.delete_outline),
|
||||
onTap: _loadingDeletingDevices
|
||||
? null
|
||||
: () => _removeDevicesAction(context, devices),
|
||||
),
|
||||
Divider(height: 1),
|
||||
Expanded(
|
||||
|
60
lib/views/sso_web_view.dart
Normal file
60
lib/views/sso_web_view.dart
Normal file
@ -0,0 +1,60 @@
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
|
||||
class SsoWebView extends StatefulWidget {
|
||||
@override
|
||||
_SsoWebViewState createState() => _SsoWebViewState();
|
||||
}
|
||||
|
||||
class _SsoWebViewState extends State<SsoWebView> {
|
||||
bool _loading = false;
|
||||
String _error;
|
||||
|
||||
void _login(BuildContext context, String token) async {
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
await Matrix.of(context).client.login(
|
||||
type: AuthenticationTypes.token,
|
||||
userIdentifierType: null,
|
||||
token: token,
|
||||
initialDeviceDisplayName: Matrix.of(context).clientName,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logs().e('Login with token failed', e, s);
|
||||
setState(() => _error = e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final url =
|
||||
'${Matrix.of(context).client.homeserver?.toString()}/_matrix/client/r0/login/sso/redirect?redirectUrl=${Uri.encodeQueryComponent(AppConfig.appId.toLowerCase() + '://sso')}';
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
L10n.of(context).logInTo(Matrix.of(context).client.homeserver?.host ??
|
||||
L10n.of(context).oopsSomethingWentWrong),
|
||||
),
|
||||
),
|
||||
body: _error != null
|
||||
? Center(child: Text(_error))
|
||||
: _loading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: WebView(
|
||||
initialUrl: url,
|
||||
javascriptMode: JavascriptMode.unrestricted,
|
||||
onPageStarted: (url) {
|
||||
if (url.startsWith(AppConfig.appId.toLowerCase())) {
|
||||
_login(context,
|
||||
Uri.parse(url).queryParameters['loginToken']);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user