public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Azharul Haque <haque@azharul.com>
To: pve-devel@lists.proxmox.com
Cc: haque@azharul.com
Subject: [PATCH login-manager v3 2/5] refactor: ui: factor out shared login tail into _finishLogin
Date: Thu, 20 Aug 2026 23:41:41 -0400	[thread overview]
Message-ID: <20260821034147.30194-5-haque@azharul.com> (raw)
In-Reply-To: <20260821034147.30194-1-haque@azharul.com>

Move the code that runs after an authenticated API client exists --
handling a pending TFA challenge, fetching cluster status, persisting
the login and closing the login page -- out of _onLoginButtonPressed
into a _finishLogin method. No functional change.

This prepares for the OpenID Connect login flow added in a following
commit, which obtains its client through a browser-based flow instead
of a password but finishes the login the same way.

Suggested-by: Shan Shaji <s.shaji@proxmox.com>
Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/proxmox_login_form.dart | 203 ++++++++++++++++++++----------------
 1 file changed, 111 insertions(+), 92 deletions(-)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 5b9a64e..6039407 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -478,98 +478,13 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       var client = await proxclient.authenticate(
           '$username@$realm', password, origin, settings.sslValidation!);
 
-      if (client.credentials.tfa != null &&
-          client.credentials.tfa!.kinds().isNotEmpty) {
-        if (!mounted) return;
-        ProxmoxApiClient? tfaclient =
-            await Navigator.of(context).push(MaterialPageRoute(
-          builder: (context) => ProxmoxTfaForm(
-            apiClient: client,
-          ),
-        ));
-
-        if (tfaclient != null) {
-          client = tfaclient;
-        } else {
-          setState(() {
-            _progressModel.inProgress -= 1;
-          });
-          return;
-        }
-      }
-
-      final status = await client.getClusterStatus();
-      final hostname =
-          status.singleWhereOrNull((element) => element.local ?? false)?.name;
-      var loginStorage = await ProxmoxLoginStorage.fromLocalStorage();
-
-      final savePW = enteredPassword != '' &&
-          _savePasswordCB &&
-          enteredPassword != savedPassword;
-      final deletePW = enteredPassword != '' && !savePW && !_savePasswordCB;
-      String? id;
-
-      if (widget.isCreate!) {
-        final newLogin = ProxmoxLoginModel((b) => b
-          ..origin = origin
-          ..username = username
-          ..realm = realm
-          ..productType = ProxmoxProductType.pve
-          ..ticket = client.credentials.ticket
-          ..passwordSaved = savePW
-          ..hostname = hostname);
-
-        loginStorage = loginStorage!.rebuild((b) => b..logins.add(newLogin));
-        id = newLogin.identifier;
-      } else {
-        loginStorage = loginStorage!.rebuild((b) => b
-          ..logins.rebuildWhere(
-              (m) => m == widget.userModel,
-              (b) => b
-                ..ticket = client.credentials.ticket
-                ..passwordSaved =
-                    savePW || (deletePW ? false : b.passwordSaved ?? false)
-                ..hostname = hostname));
-        id = widget.userModel!.identifier;
-      }
-
-      if (id != null) {
-        try {
-          if (savePW) {
-            await savePassword(id, enteredPassword);
-          } else if (deletePW) {
-            await deletePassword(id);
-          }
-        } catch (e) {
-          if (!mounted) return;
-          await showDialog(
-              context: context,
-              builder: (context) => AlertDialog(
-                    title: const Text('Password saving error'),
-                    scrollable: true,
-                    content: Column(
-                      mainAxisSize: MainAxisSize.min,
-                      children: [
-                        const Text('Could not save or delete password.'),
-                        ExpansionTile(
-                          title: const Text('Details'),
-                          children: [Text(e.toString())],
-                        )
-                      ],
-                    ),
-                    actions: [
-                      TextButton(
-                          onPressed: () => Navigator.of(context).pop(),
-                          child: const Text('Continue')),
-                    ],
-                  ));
-        }
-      }
-      await loginStorage.saveToDisk();
-
-      if (mounted) {
-        Navigator.of(context).pop(client);
-      }
+      await _finishLogin(
+        client,
+        realm: realm!,
+        username: username,
+        enteredPassword: enteredPassword,
+        savedPassword: savedPassword,
+      );
     } on proxclient.ProxmoxApiException catch (e) {
       print(e);
       if (!mounted) return;
@@ -618,6 +533,110 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     });
   }
 
+  /// Common tail of the login flow once an authenticated client exists:
+  /// handles a pending TFA challenge, fetches cluster status, persists the
+  /// login and closes the login page. Returns early (without closing the
+  /// page) if the user cancels a TFA challenge.
+  Future<void> _finishLogin(
+    ProxmoxApiClient client, {
+    required String realm,
+    required String username,
+    String enteredPassword = '',
+    String? savedPassword,
+  }) async {
+    if (client.credentials.tfa != null &&
+        client.credentials.tfa!.kinds().isNotEmpty) {
+      if (!mounted) return;
+      ProxmoxApiClient? tfaclient =
+          await Navigator.of(context).push(MaterialPageRoute(
+        builder: (context) => ProxmoxTfaForm(
+          apiClient: client,
+        ),
+      ));
+
+      if (tfaclient != null) {
+        client = tfaclient;
+      } else {
+        return;
+      }
+    }
+
+    final status = await client.getClusterStatus();
+    final hostname =
+        status.singleWhereOrNull((element) => element.local ?? false)?.name;
+    var loginStorage = await ProxmoxLoginStorage.fromLocalStorage();
+
+    final savePW = enteredPassword != '' &&
+        _savePasswordCB &&
+        enteredPassword != savedPassword;
+    final deletePW = enteredPassword != '' && !savePW && !_savePasswordCB;
+    String? id;
+
+    final origin = normalizeUrl(_originController.text.trim());
+
+    if (widget.isCreate!) {
+      final newLogin = ProxmoxLoginModel((b) => b
+        ..origin = origin
+        ..username = username
+        ..realm = realm
+        ..productType = ProxmoxProductType.pve
+        ..ticket = client.credentials.ticket
+        ..passwordSaved = savePW
+        ..hostname = hostname);
+
+      loginStorage = loginStorage!.rebuild((b) => b..logins.add(newLogin));
+      id = newLogin.identifier;
+    } else {
+      loginStorage = loginStorage!.rebuild((b) => b
+        ..logins.rebuildWhere(
+            (m) => m == widget.userModel,
+            (b) => b
+              ..ticket = client.credentials.ticket
+              ..passwordSaved =
+                  savePW || (deletePW ? false : b.passwordSaved ?? false)
+              ..hostname = hostname));
+      id = widget.userModel!.identifier;
+    }
+
+    if (id != null) {
+      try {
+        if (savePW) {
+          await savePassword(id, enteredPassword);
+        } else if (deletePW) {
+          await deletePassword(id);
+        }
+      } catch (e) {
+        if (!mounted) return;
+        await showDialog(
+            context: context,
+            builder: (context) => AlertDialog(
+                  title: const Text('Password saving error'),
+                  scrollable: true,
+                  content: Column(
+                    mainAxisSize: MainAxisSize.min,
+                    children: [
+                      const Text('Could not save or delete password.'),
+                      ExpansionTile(
+                        title: const Text('Details'),
+                        children: [Text(e.toString())],
+                      )
+                    ],
+                  ),
+                  actions: [
+                    TextButton(
+                        onPressed: () => Navigator.of(context).pop(),
+                        child: const Text('Continue')),
+                  ],
+                ));
+      }
+    }
+    await loginStorage.saveToDisk();
+
+    if (mounted) {
+      Navigator.of(context).pop(client);
+    }
+  }
+
   Future<List<PveAccessDomainModel?>?> _loadAccessDomains(Uri uri) async {
     final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
     List<PveAccessDomainModel?>? response;
-- 
2.50.1 (Apple Git-155)




  parent reply	other threads:[~2026-08-25  8:09 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-10  5:40 [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Azharul Haque
2026-08-10  5:40 ` [PATCH 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-10  5:40 ` [PATCH 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-10 13:59 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-10 14:47     ` [PATCH dart-api-client v2 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-20  9:17       ` Shan Shaji
2026-08-20 13:37         ` Azharul Haque
2026-08-20 14:39           ` Shan Shaji
2026-08-21  2:56             ` Azharul Haque
2026-08-21  8:00               ` Shan Shaji
2026-08-10 14:47     ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-20  9:48       ` Shan Shaji
2026-08-20 13:41         ` Azharul Haque
2026-08-10 14:47     ` [PATCH login-manager v2 1/3] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-20 12:31       ` Shan Shaji
2026-08-10 14:47     ` [PATCH login-manager v2 2/3] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-10 14:47     ` [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme Azharul Haque
2026-08-20 14:28       ` Shan Shaji
2026-08-10 14:47     ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-20 14:01       ` Shan Shaji
2026-08-10 14:47     ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-21  3:41     ` [PATCH v3 00/10] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-21  3:41       ` [PATCH dart-api-client v3 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-21  3:41       ` [PATCH dart-api-client v3 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-21  3:41       ` [PATCH login-manager v3 1/5] fix #4281: deps: add flutter_web_auth_2 dependency Azharul Haque
2026-08-21  3:41       ` Azharul Haque [this message]
2026-08-21  3:41       ` [PATCH login-manager v3 3/5] refactor: ui: split password form into its own widget Azharul Haque
2026-08-21  3:41       ` [PATCH login-manager v3 4/5] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-21  3:41       ` [PATCH login-manager v3 5/5] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-21  3:41       ` [PATCH flutter-frontend v3 1/3] chore: regenerate plugin registrant for flutter_web_auth_2 Azharul Haque
2026-08-21  3:41       ` [PATCH flutter-frontend v3 2/3] fix #4281: android: set taskAffinity="" on MainActivity Azharul Haque
2026-08-21  3:41       ` [PATCH flutter-frontend v3 3/3] fix #4281: android: register OpenID Connect callback activity Azharul Haque

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260821034147.30194-5-haque@azharul.com \
    --to=haque@azharul.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal