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)
next prev 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.