From 973e4a1c0780a951c6687e9eb6805d485cbe370c Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:47:04 +0930 Subject: [PATCH 1/7] use OAuth account management api --- .../matrix_uia_request_view.dart | 1 - .../security/matrix/matrix_security_tab.dart | 89 +++++++++++++++---- .../matrix/session/matrix_session.dart | 13 +-- .../matrix/session/matrix_session_view.dart | 2 +- 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index 2fe25f03f..36c845137 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -57,7 +57,6 @@ class _MatrixUIARequestViewState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( - width: 100, height: 40, child: Button( text: CommonStrings.promptSubmit, diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart index ce48dd168..a73e51ce0 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart @@ -1,7 +1,11 @@ +import 'package:commet/client/alert.dart'; import 'package:commet/client/matrix/matrix_client.dart'; +import 'package:commet/debug/log.dart'; +import 'package:commet/ui/molecules/alert_view.dart'; import 'package:commet/ui/navigation/adaptive_dialog.dart'; import 'package:commet/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart'; import 'package:commet/utils/common_strings.dart'; +import 'package:commet/utils/links/link_utils.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:matrix/matrix.dart'; @@ -20,8 +24,24 @@ class MatrixSecurityTab extends StatefulWidget { class _MatrixSecurityTabState extends State { bool crossSigningEnabled = false; bool? messageBackupEnabled; + bool isVerified = false; List? devices; + Map? authMetadata; + + Uri? get accountManagementUri { + var str = authMetadata?.tryGet("account_management_uri"); + if (str == null) return null; + return Uri.tryParse(str); + } + + List? get supportedActions => + authMetadata?.tryGetList("account_management_actions_supported"); + + bool get canRemoveDeviceOAuth => + supportedActions?.contains("org.matrix.device_delete") == true && + accountManagementUri != null; + String get labelMatrixCrossSigning => Intl.message("Cross signing", desc: "Title label for matrix cross signing", name: "labelMatrixCrossSigning"); @@ -74,12 +94,22 @@ class _MatrixSecurityTabState extends State { var encryption = widget.client.getMatrixClient().encryption; crossSigningEnabled = encryption?.crossSigning.enabled ?? false; messageBackupEnabled = encryption?.keyManager.enabled ?? false; + isVerified = widget.client.matrixClient.isUnknownSession == false; }); } void getDevices() async { var gotDevices = await widget.client.getMatrixClient().getDevices(); + if (authMetadata == null) { + try { + authMetadata = await widget.client.matrixClient + .request(RequestType.GET, "/client/v1/auth_metadata"); + } catch (e, s) { + Log.onError(e, s); + } + } + gotDevices ?.sort((a, b) => (b.lastSeenTs ?? 0).compareTo(a.lastSeenTs ?? 0)); @@ -95,16 +125,8 @@ class _MatrixSecurityTabState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, - children: [ - const SizedBox( - height: 4, - ), - crossSigningPanel(), - const SizedBox( - height: 4, - ), - sessionsPanel() - ], + spacing: 4, + children: [crossSigningPanel(), sessionsPanel()], ); } @@ -113,21 +135,31 @@ class _MatrixSecurityTabState extends State { header: labelMatrixAccountSessions, mode: TileType.surfaceContainerLow, child: devices == null - ? const CircularProgressIndicator() + ? SizedBox( + height: 300, + child: Center(child: const CircularProgressIndicator())) : ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { + var device = devices![index]; return Padding( padding: const EdgeInsets.all(2.0), child: MatrixSession( - devices![index], + device, widget.client.getMatrixClient(), - onUpdated: () { - setState(() { - getDevices(); - }); - }, + removeSession: canRemoveDeviceOAuth + ? () async { + LinkUtils.open( + accountManagementUri!.replace(queryParameters: { + "action": "org.matrix.device_delete", + "device_id": device.deviceId + }), + context: context, + filterTrackingParameters: false, + bypassConfirmation: false); + } + : null, ), ); }, @@ -136,6 +168,12 @@ class _MatrixSecurityTabState extends State { ); } + void onUpdated() { + setState(() { + getDevices(); + }); + } + Panel crossSigningPanel() { return Panel( header: labelMatrixCrossSigningAndBackup, @@ -143,7 +181,24 @@ class _MatrixSecurityTabState extends State { child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: Column( + spacing: 4, children: [ + if (!isVerified) + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 8), + child: Container( + decoration: BoxDecoration( + color: ColorScheme.of(context).surfaceContainerLowest, + borderRadius: BorderRadius.circular(8)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: AlertView(Alert(AlertType.warning, + messageGetter: () => + "Your current session is not verified. You will not be able to participate in encrypted chats.", + titleGetter: () => "Unverified")), + ), + ), + ), crossSigning(), const tiamat.Seperator(), messageBackup() diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart index b8f14ce73..0b5e6856d 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart @@ -8,10 +8,11 @@ import '../../../../../../matrix/verification/matrix_verification_page.dart'; class MatrixSession extends StatefulWidget { const MatrixSession(this.device, this.matrixClient, - {super.key, this.onUpdated}); + {super.key, this.onUpdated, this.removeSession}); final Device device; final Client matrixClient; final Function? onUpdated; + final Function? removeSession; @override State createState() => _MatrixSessionState(); @@ -30,7 +31,7 @@ class _MatrixSessionState extends State { verified: isVerified(), isThisDevice: isCurrentDevice(), beginVerification: beginVerification, - removeSession: removeSession, + removeSession: widget.removeSession, ); } @@ -61,12 +62,4 @@ class _MatrixSessionState extends State { previousOnUpdate?.call(); setState(() {}); } - - void removeSession() async { - await widget.matrixClient.uiaRequestBackground((auth) async { - await widget.matrixClient - .deleteDevice(widget.device.deviceId, auth: auth); - widget.onUpdated?.call(); - }); - } } diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart index 2e37121fa..c881f530c 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart @@ -104,7 +104,7 @@ class MatrixSessionView extends StatelessWidget { text: promptMatrixVerifySession, onTap: () => beginVerification?.call(), ), - if (!isThisDevice) + if (!isThisDevice && removeSession != null) Padding( padding: const EdgeInsets.fromLTRB(8, 0, 4, 0), child: tiamat.IconButton( From db804bf9c6493e4e6d23d76be84e9f7c149e7362 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:09:39 +0930 Subject: [PATCH 2/7] improve UIA flows --- .../authentication/matrix_uia_request.dart | 30 ++- .../matrix_uia_request_view.dart | 79 +++++++- .../security/matrix/matrix_security_tab.dart | 171 +++++++++++++++--- .../matrix/session/matrix_session.dart | 5 +- .../matrix/session/matrix_session_view.dart | 11 ++ 5 files changed, 259 insertions(+), 37 deletions(-) diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request.dart index 1bd762131..f430ae985 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request.dart @@ -1,4 +1,6 @@ import 'package:commet/ui/pages/matrix/authentication/matrix_uia_request_view.dart'; +import 'package:commet/utils/error_utils.dart'; +import 'package:commet/utils/links/link_utils.dart'; import 'package:flutter/widgets.dart'; import 'package:matrix/matrix.dart'; @@ -15,6 +17,7 @@ class MatrixUIARequest extends StatefulWidget { class _MatrixUIARequestState extends State { void Function(UiaRequestState)? originalOnUpdate; late UiaRequestState state; + late Set steps; @override void initState() { @@ -22,6 +25,7 @@ class _MatrixUIARequestState extends State { originalOnUpdate = widget.request.onUpdate; widget.request.onUpdate = onUpdate; + steps = widget.request.nextStages; super.initState(); } @@ -29,6 +33,7 @@ class _MatrixUIARequestState extends State { void onUpdate(UiaRequestState state) { setState(() { this.state = state; + this.steps = widget.request.nextStages; }); originalOnUpdate?.call(state); @@ -38,15 +43,34 @@ class _MatrixUIARequestState extends State { Widget build(BuildContext context) { return MatrixUIARequestView( state, + nextSteps: steps, onSubmitAuthentication: submitAuthentication, + onSubmitSso: submitSso, onSuccess: () => Navigator.of(context).pop(), onFail: () => Navigator.of(context).pop(), ); } void submitAuthentication(String password) { - widget.request.completeStage(AuthenticationPassword( - password: password, - identifier: AuthenticationUserIdentifier(user: "alice"))); + ErrorUtils.tryRun(context, () async { + await widget.request.completeStage(AuthenticationPassword( + password: password, + identifier: AuthenticationUserIdentifier( + user: widget.client.matrixClient.userID!))); + }); + } + + submitSso() { + // https://spec.matrix.org/v1.15/client-server-api/#client-behaviour-21 + var hs = widget.client.matrixClient.homeserver!; + var url = hs.replace( + path: "/_matrix/client/v3/auth/m.login.sso/fallback/web", + queryParameters: {"session": widget.request.session}); + + LinkUtils.open(url, + bypassConfirmation: true, + filterTrackingParameters: false, + context: context); + Navigator.of(context).pop(); } } diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index 36c845137..1af521a50 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -1,5 +1,5 @@ import 'package:commet/utils/common_strings.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; // have to do it this way to avoid some widgetbook codegen issue // ignore: implementation_imports import 'package:matrix/src/utils/uia_request.dart'; @@ -9,9 +9,16 @@ import 'package:flutter/material.dart' as material; class MatrixUIARequestView extends StatefulWidget { const MatrixUIARequestView(this.state, - {this.onSubmitAuthentication, super.key, this.onFail, this.onSuccess}); + {this.onSubmitAuthentication, + required this.nextSteps, + super.key, + this.onFail, + this.onSubmitSso, + this.onSuccess}); final UiaRequestState state; + final Set nextSteps; final Function(String password)? onSubmitAuthentication; + final Function()? onSubmitSso; final Function()? onSuccess; final Function()? onFail; @@ -19,8 +26,27 @@ class MatrixUIARequestView extends StatefulWidget { State createState() => _MatrixUIARequestViewState(); } +enum UIAStep { + password, + sso, +} + class _MatrixUIARequestViewState extends State { TextEditingController passwordFieldController = TextEditingController(); + bool get canUsePassword => widget.nextSteps.contains("m.login.password"); + bool get canUseSso => widget.nextSteps.contains("m.login.sso"); + + UIAStep? pickedStep; + + @override + void initState() { + if (canUsePassword && !canUseSso) { + pickedStep = UIAStep.password; + } + + super.initState(); + } + @override Widget build(BuildContext context) { return SizedBox( @@ -30,6 +56,8 @@ class _MatrixUIARequestViewState extends State { ); } + bool get canUseAnyNextStep => canUsePassword || canUseSso; + Widget buildView() { switch (widget.state) { case UiaRequestState.done: @@ -39,10 +67,57 @@ class _MatrixUIARequestViewState extends State { case UiaRequestState.loading: return loading(); case UiaRequestState.waitForUser: + return pickedStep == null ? showAvailableSteps() : showPickedStep(); + } + } + + Widget showAvailableSteps() { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (canUsePassword) + tiamat.Button( + text: "Continue with password", + onTap: () => setState(() { + pickedStep = UIAStep.password; + }), + ), + if (canUseSso) + tiamat.Button( + text: "Continue with SSO", + onTap: () { + widget.onSubmitSso?.call(); + setState(() { + pickedStep = UIAStep.sso; + }); + }) + ], + ); + } + + Widget showPickedStep() { + if (pickedStep == UIAStep.password) { + return userPasswordInput(); + } + + switch (pickedStep!) { + case UIAStep.password: return userPasswordInput(); + case UIAStep.sso: + return showSsoStep(); } } + Widget showSsoStep() { + return SizedBox( + height: 300, + width: 300, + child: Center( + child: CircularProgressIndicator(), + )); + } + Widget userPasswordInput() { return Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart index a73e51ce0..844546515 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart @@ -26,9 +26,12 @@ class _MatrixSecurityTabState extends State { bool? messageBackupEnabled; bool isVerified = false; List? devices; + late DateTime time; Map? authMetadata; + bool? supportsLegacyUIA; + Uri? get accountManagementUri { var str = authMetadata?.tryGet("account_management_uri"); if (str == null) return null; @@ -84,6 +87,7 @@ class _MatrixSecurityTabState extends State { @override void initState() { + time = DateTime.now(); checkState(); getDevices(); super.initState(); @@ -101,12 +105,33 @@ class _MatrixSecurityTabState extends State { void getDevices() async { var gotDevices = await widget.client.getMatrixClient().getDevices(); - if (authMetadata == null) { + if (supportsLegacyUIA == null) { try { - authMetadata = await widget.client.matrixClient - .request(RequestType.GET, "/client/v1/auth_metadata"); - } catch (e, s) { - Log.onError(e, s); + await widget.client.matrixClient + .request(RequestType.DELETE, "/client/v3/devices/_", data: { + "auth": { + "type": "m.login.dummy", + } + }); + } catch (e) { + if (e case MatrixException mx) { + if (mx.error == MatrixError.M_FORBIDDEN) { + supportsLegacyUIA = true; + } else { + supportsLegacyUIA = false; + } + } + } + } + + if (supportsLegacyUIA != true) { + if (authMetadata == null) { + try { + authMetadata = await widget.client.matrixClient + .request(RequestType.GET, "/client/v1/auth_metadata"); + } catch (e, s) { + Log.onError(e, s); + } } } @@ -130,6 +155,9 @@ class _MatrixSecurityTabState extends State { ); } + Iterable get inactiveDevices => + devices?.where((i) => isDeviceInactive(i)) ?? []; + Panel sessionsPanel() { return Panel( header: labelMatrixAccountSessions, @@ -138,36 +166,110 @@ class _MatrixSecurityTabState extends State { ? SizedBox( height: 300, child: Center(child: const CircularProgressIndicator())) - : ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - var device = devices![index]; - return Padding( - padding: const EdgeInsets.all(2.0), - child: MatrixSession( - device, - widget.client.getMatrixClient(), - removeSession: canRemoveDeviceOAuth - ? () async { - LinkUtils.open( - accountManagementUri!.replace(queryParameters: { - "action": "org.matrix.device_delete", - "device_id": device.deviceId - }), - context: context, - filterTrackingParameters: false, - bypassConfirmation: false); - } - : null, + : Column( + children: [ + if (supportsLegacyUIA == true && inactiveDevices.isNotEmpty) + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 8), + child: Container( + decoration: BoxDecoration( + color: ColorScheme.of(context).surfaceContainerLowest, + borderRadius: BorderRadius.circular(8)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible( + child: AlertView( + Alert( + AlertType.warning, + messageGetter: () => + "You have ${inactiveDevices.length} sessions which have not been used recently", + titleGetter: () => "Inactive Devices", + ), + ), + ), + SizedBox( + width: 40, + height: 40, + child: tiamat.IconButton( + size: 15, + icon: Icons.logout, + onPressed: () { + removeInactiveDevices(); + }, + ), + ) + ], + ), + ), + ), ), - ); - }, - itemCount: devices!.length, + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var device = devices![index]; + return Padding( + padding: const EdgeInsets.all(2.0), + child: MatrixSession( + device, + widget.client.getMatrixClient(), + inactive: isDeviceInactive(device), + removeSession: supportsLegacyUIA == true + ? () => removeDeviceUIA(device.deviceId) + : canRemoveDeviceOAuth + ? () => removeDeviceOAuth(device.deviceId) + : null, + ), + ); + }, + itemCount: devices!.length, + ), + ], ), ); } + void removeDeviceUIA(String deviceID) async { + await widget.client.matrixClient.uiaRequestBackground((auth) async { + await widget.client.matrixClient.deleteDevice(deviceID, auth: auth); + + getDevices(); + }); + } + + void removeInactiveDevices() async { + var confirm = await AdaptiveDialog.confirmation(context, + dangerous: true, + prompt: + "Are you sure you want to log out of ${inactiveDevices.length} sessions?"); + + if (confirm != true) return; + + await widget.client.matrixClient.uiaRequestBackground((auth) async { + await widget.client.matrixClient.deleteDevices( + inactiveDevices.map((i) => i.deviceId).toList(), + auth: auth); + }); + + getDevices(); + } + + void removeDeviceOAuth(String deviceID) { + LinkUtils.open( + accountManagementUri!.replace(queryParameters: { + "action": "org.matrix.device_delete", + "device_id": deviceID + }), + context: context, + filterTrackingParameters: false, + bypassConfirmation: false); + } + void onUpdated() { setState(() { getDevices(); @@ -293,4 +395,13 @@ class _MatrixSecurityTabState extends State { ], ); } + + bool isDeviceInactive(Device device) { + if (device.lastSeenTs == null) return false; + + var date = DateTime.fromMillisecondsSinceEpoch(device.lastSeenTs!); + var diff = time.difference(date); + + return diff.inDays > 60; + } } diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart index 0b5e6856d..5ed6f7906 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session.dart @@ -8,12 +8,12 @@ import '../../../../../../matrix/verification/matrix_verification_page.dart'; class MatrixSession extends StatefulWidget { const MatrixSession(this.device, this.matrixClient, - {super.key, this.onUpdated, this.removeSession}); + {super.key, this.onUpdated, this.inactive = false, this.removeSession}); final Device device; final Client matrixClient; final Function? onUpdated; final Function? removeSession; - + final bool inactive; @override State createState() => _MatrixSessionState(); } @@ -30,6 +30,7 @@ class _MatrixSessionState extends State { lastSeenTimestamp: widget.device.lastSeenTs, verified: isVerified(), isThisDevice: isCurrentDevice(), + inactive: widget.inactive, beginVerification: beginVerification, removeSession: widget.removeSession, ); diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart index c881f530c..9eb2000ce 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/session/matrix_session_view.dart @@ -13,6 +13,7 @@ class MatrixSessionView extends StatelessWidget { super.key, this.verified = false, this.isThisDevice = false, + this.inactive = false, this.beginVerification, this.removeSession}); @@ -22,6 +23,7 @@ class MatrixSessionView extends StatelessWidget { final int? lastSeenTimestamp; final bool verified; final bool isThisDevice; + final bool inactive; final Function? beginVerification; final Function? removeSession; @@ -64,6 +66,15 @@ class MatrixSessionView extends StatelessWidget { padding: EdgeInsets.fromLTRB(0, 0, 8, 0), child: TinyPill("This Device"), ), + if (inactive) + Padding( + padding: EdgeInsets.fromLTRB(0, 0, 8, 0), + child: TinyPill( + "Inactive", + background: ColorScheme.of(context).error, + foreground: ColorScheme.of(context).onError, + ), + ), tiamat.Text.labelLow(deviceId), ], ), From 8b0ec7c33480889d0671ff7940da0a4e589525d9 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:10:48 +0930 Subject: [PATCH 3/7] Update matrix_uia_request_view.dart --- .../pages/matrix/authentication/matrix_uia_request_view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index 1af521a50..63be3faee 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -91,7 +91,9 @@ class _MatrixUIARequestViewState extends State { setState(() { pickedStep = UIAStep.sso; }); - }) + }), + if(canUseAnyNextStep == false) + tiamat.Text.labelLow("Sorry, none of the authentication methods provided by the server are supported."), ], ); } From c19ba10860a55e4e31c8534b35a6396d27e2ee76 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:13:21 +0930 Subject: [PATCH 4/7] small ui improvements --- .../matrix_uia_request_view.dart | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index 63be3faee..bff496330 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -51,8 +51,10 @@ class _MatrixUIARequestViewState extends State { Widget build(BuildContext context) { return SizedBox( width: 500, - height: 200, - child: buildView(), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: buildView(), + ), ); } @@ -72,29 +74,34 @@ class _MatrixUIARequestViewState extends State { } Widget showAvailableSteps() { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (canUsePassword) - tiamat.Button( - text: "Continue with password", - onTap: () => setState(() { - pickedStep = UIAStep.password; - }), - ), - if (canUseSso) - tiamat.Button( - text: "Continue with SSO", - onTap: () { - widget.onSubmitSso?.call(); - setState(() { - pickedStep = UIAStep.sso; - }); + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 12, + children: [ + if (canUsePassword) + tiamat.Button( + text: "Continue with password", + onTap: () => setState(() { + pickedStep = UIAStep.password; }), - if(canUseAnyNextStep == false) - tiamat.Text.labelLow("Sorry, none of the authentication methods provided by the server are supported."), - ], + ), + if (canUseSso) + tiamat.Button( + text: "Continue with SSO", + onTap: () { + widget.onSubmitSso?.call(); + setState(() { + pickedStep = UIAStep.sso; + }); + }), + if (canUseAnyNextStep == false) + tiamat.Text.labelLow( + "Sorry, none of the authentication methods provided by the server are supported."), + ], + ), ); } @@ -123,23 +130,20 @@ class _MatrixUIARequestViewState extends State { Widget userPasswordInput() { return Column( mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 12, children: [ TextInput( placeholder: "Account Password", obscureText: true, controller: passwordFieldController, ), - Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - height: 40, - child: Button( - text: CommonStrings.promptSubmit, - onTap: () => widget.onSubmitAuthentication - ?.call(passwordFieldController.text), - )), + SizedBox( + height: 40, + child: Button( + text: CommonStrings.promptSubmit, + onTap: () => widget.onSubmitAuthentication + ?.call(passwordFieldController.text), )) ], ); From f17a26193d0cef70099fb7746374dc4e06e4d16d Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:13:34 +0930 Subject: [PATCH 5/7] Update matrix_uia_request_view.dart --- .../ui/pages/matrix/authentication/matrix_uia_request_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index bff496330..4b964abc8 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -81,7 +81,7 @@ class _MatrixUIARequestViewState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, spacing: 12, children: [ - if (canUsePassword) + if (canUsePassword) tiamat.Button( text: "Continue with password", onTap: () => setState(() { From c81f89a291479492f3d9cbedaf0e11cd1f8cd594 Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 01:21:15 +0930 Subject: [PATCH 6/7] Update cross_signing_view.dart --- .../cross_signing/cross_signing_view.dart | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart index 2b2693b21..0c1d3514e 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart @@ -279,19 +279,33 @@ class _MatrixCrossSigningViewState extends State { return m.Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, + spacing: 12, children: [ m.Column( crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 12, children: [ tiamat.Text.label(labelMatrixRecoveryKeyExplanation), - const SizedBox(height: 8), - m.SelectionArea( - child: Codeblock( - text: widget.recoveryKey!, + m.Container( + decoration: BoxDecoration( + color: m.ColorScheme.of(context).surfaceContainerLowest, + borderRadius: BorderRadiusGeometry.circular(8), + ), + child: m.SelectionArea( + child: m.Padding( + padding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + child: m.Center( + child: Text( + widget.recoveryKey!, + style: const TextStyle( + fontSize: 12, + fontFamily: "Code", + fontFeatures: [FontFeature.disable("calt")]), + )), + ), ), ), - const SizedBox(height: 8), - tiamat.Button.secondary( + tiamat.Button( text: copyBackupCodeText, onTap: () { services.Clipboard.setData( @@ -302,10 +316,7 @@ class _MatrixCrossSigningViewState extends State { }), ], ), - const SizedBox( - height: 50, - ), - tiamat.Button( + tiamat.Button.secondary( text: CommonStrings.promptConfirm, isLoading: isActionLoading, onTap: () async { From 9d691f43e93a8a913e8fc5af20d294b6904a1a6a Mon Sep 17 00:00:00 2001 From: Airyzz <36567925+Airyzz@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:13:18 +0930 Subject: [PATCH 7/7] verification status ui improvement, fix verification bug --- commet/lib/client/client.dart | 3 +++ commet/lib/client/matrix/matrix_client.dart | 17 ++++++++++++++ .../matrix_background_client.dart | 6 +++++ .../matrix_uia_request_view.dart | 8 ++++--- .../cross_signing/cross_signing_view.dart | 1 - .../security/matrix/matrix_security_tab.dart | 22 +++++++++++++++++++ commet/pubspec.yaml | 2 +- pubspec.lock | 4 ++-- 8 files changed, 56 insertions(+), 7 deletions(-) diff --git a/commet/lib/client/client.dart b/commet/lib/client/client.dart index ee2c8dd07..c87c252f1 100644 --- a/commet/lib/client/client.dart +++ b/commet/lib/client/client.dart @@ -150,6 +150,9 @@ abstract class Client { Future executeLoginFlow(LoginFlow flow); + // returns true if the homeserver is configured in a way to disable end to end encryption + Future hasServerDisabledEncryption(); + /// Logout and invalidate the current session Future logout(); diff --git a/commet/lib/client/matrix/matrix_client.dart b/commet/lib/client/matrix/matrix_client.dart index 1330a465a..c059857e0 100644 --- a/commet/lib/client/matrix/matrix_client.dart +++ b/commet/lib/client/matrix/matrix_client.dart @@ -898,6 +898,23 @@ class MatrixClient extends Client { }); } + @override + Future hasServerDisabledEncryption() async { + var data = await matrixClient.getWellknown(); + Log.i(data); + + var prop = + data.additionalProperties.tryGetMap("io.element.e2ee"); + + var value = prop?.tryGet("force_disable"); + + if (value == true) { + return true; + } + + return false; + } + @override Future joinRoomFromPreview(RoomPreview preview) { String roomId = preview.roomId; diff --git a/commet/lib/client/matrix_background/matrix_background_client.dart b/commet/lib/client/matrix_background/matrix_background_client.dart index 5353305cd..45d7f9f63 100644 --- a/commet/lib/client/matrix_background/matrix_background_client.dart +++ b/commet/lib/client/matrix_background/matrix_background_client.dart @@ -271,4 +271,10 @@ class MatrixBackgroundClient implements Client { @override // TODO: implement favoriteRooms NotifyingListFilter get favoriteRooms => throw UnimplementedError(); + + @override + Future hasServerDisabledEncryption() { + // TODO: implement hasServerDisabledEncryption + throw UnimplementedError(); + } } diff --git a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart index 4b964abc8..3b31273d1 100644 --- a/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart +++ b/commet/lib/ui/pages/matrix/authentication/matrix_uia_request_view.dart @@ -36,6 +36,8 @@ class _MatrixUIARequestViewState extends State { bool get canUsePassword => widget.nextSteps.contains("m.login.password"); bool get canUseSso => widget.nextSteps.contains("m.login.sso"); + bool get canUseAnyNextStep => canUsePassword || canUseSso; + UIAStep? pickedStep; @override @@ -58,8 +60,6 @@ class _MatrixUIARequestViewState extends State { ); } - bool get canUseAnyNextStep => canUsePassword || canUseSso; - Widget buildView() { switch (widget.state) { case UiaRequestState.done: @@ -97,9 +97,11 @@ class _MatrixUIARequestViewState extends State { pickedStep = UIAStep.sso; }); }), - if (canUseAnyNextStep == false) + if (canUseAnyNextStep == false) ...[ tiamat.Text.labelLow( "Sorry, none of the authentication methods provided by the server are supported."), + tiamat.Text.labelLow(widget.nextSteps.toString()), + ] ], ), ); diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart index 0c1d3514e..2a81ffb8a 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/cross_signing/cross_signing_view.dart @@ -1,5 +1,4 @@ import 'package:commet/main.dart'; -import 'package:commet/ui/atoms/code_block.dart'; import 'package:commet/utils/common_strings.dart'; import 'package:commet/utils/error_utils.dart'; import 'package:commet/utils/text_utils.dart'; diff --git a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart index 844546515..863a63e65 100644 --- a/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart +++ b/commet/lib/ui/pages/settings/categories/account/security/matrix/matrix_security_tab.dart @@ -25,6 +25,7 @@ class _MatrixSecurityTabState extends State { bool crossSigningEnabled = false; bool? messageBackupEnabled; bool isVerified = false; + bool encryptionDisabled = false; List? devices; late DateTime time; @@ -90,6 +91,11 @@ class _MatrixSecurityTabState extends State { time = DateTime.now(); checkState(); getDevices(); + + widget.client.hasServerDisabledEncryption().then((v) => setState(() { + encryptionDisabled = v; + })); + super.initState(); } @@ -285,6 +291,22 @@ class _MatrixSecurityTabState extends State { child: Column( spacing: 4, children: [ + if (encryptionDisabled) + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 8), + child: Container( + decoration: BoxDecoration( + color: ColorScheme.of(context).surfaceContainerLowest, + borderRadius: BorderRadius.circular(8)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: AlertView(Alert(AlertType.info, + messageGetter: () => + "Your homeserver has force-disabled encryption", + titleGetter: () => "Encryption Disabled")), + ), + ), + ), if (!isVerified) Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 8), diff --git a/commet/pubspec.yaml b/commet/pubspec.yaml index 5db1e96b3..e9529d93d 100644 --- a/commet/pubspec.yaml +++ b/commet/pubspec.yaml @@ -72,7 +72,7 @@ dependencies: matrix_dart_sdk_drift_db: git: url: https://github.com/commetchat/matrix-dart-sdk-drift-db.git - ref: upstream-v6.1.1+patch.1 + ref: upstream-v6.1.1+patch.3 signal_sticker_api: git: diff --git a/pubspec.lock b/pubspec.lock index 8b57a4921..0c6c4103a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1292,8 +1292,8 @@ packages: dependency: transitive description: path: "." - ref: "upstream-v6.1.1+patch.1" - resolved-ref: bb1abba161bd0e7438aa50e4ba8fb46408b69e67 + ref: "upstream-v6.1.1+patch.3" + resolved-ref: "95f8198557b73000d72642875a6d1c429b816c0b" url: "https://github.com/commetchat/matrix-dart-sdk-drift-db.git" source: git version: "0.0.1"