all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH 0/2] android: register OpenID Connect callback activity for #4281
@ 2026-08-10  5:40 Azharul Haque
  2026-08-10  5:40 ` [PATCH 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10  5:40 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

The native Flutter "Proxmox VE Companion" app never implemented OpenID
Connect / OAuth realm login (bug #4281[0]): selecting an OAuth realm
just showed username/password fields that could never work.

Android requires an app to explicitly declare, in AndroidManifest.xml,
which activity should handle a given custom URL scheme. This series
registers flutter_web_auth_2's CallbackActivity for the
com.proxmox.app.openid:// redirect scheme used by the OAuth flow, and
fixes it up to match a later rename of that scheme.

No iOS-side changes are needed: ASWebAuthenticationSession resolves
the custom-scheme redirect at runtime without a static declaration
equivalent to this manifest entry.

Depends on the companion proxmox_dart_api_client and
proxmox_login_manager series, which implement the OIDC API calls and
login-form/OAuth-flow UI respectively.

Verified end-to-end against a real PVE server with an Authentik OIDC
realm, on both Android and iOS.

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=4281

Azharul Haque (2):
  fix #4281: android: register OpenID Connect callback activity
  fix #4281: android: match renamed OpenID callback scheme

 android/app/src/main/AndroidManifest.xml     |  16 ++
 linux/flutter/generated_plugin_registrant.cc |   8 +
 linux/flutter/generated_plugins.cmake        |   2 +
 pubspec.lock                                 | 184 +++++++++++--------
 4 files changed, 138 insertions(+), 72 deletions(-)

-- 
2.50.1 (Apple Git-155)




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/2] fix #4281: android: register OpenID Connect callback activity
  2026-08-10  5:40 [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Azharul Haque
@ 2026-08-10  5:40 ` 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
  2 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10  5:40 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

proxmox_login_manager's OpenID login flow (see the corresponding
patch there) opens the provider's authorization URL via
flutter_web_auth_2 and expects the provider's redirect back to
pveauth://openid-callback to be captured by the app. On Android this
requires explicitly registering flutter_web_auth_2's CallbackActivity
for that scheme; add it, following the plugin's setup instructions.
Also set android:taskAffinity="" on both activities as recommended
for this flutter_web_auth_2 version, to avoid a stray task appearing
after the callback returns control to MainActivity.

No changes needed on iOS: ASWebAuthenticationSession handles the
custom-scheme redirect without any Info.plist registration.

linux/flutter/generated_plugin_registrant.cc, generated_plugins.cmake
and pubspec.lock are regenerated as a consequence of the new
transitive dependency on flutter_web_auth_2 (and, on Linux/Windows,
its desktop_webview_window fallback).

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 android/app/src/main/AndroidManifest.xml     |  16 ++
 linux/flutter/generated_plugin_registrant.cc |   8 +
 linux/flutter/generated_plugins.cmake        |   2 +
 pubspec.lock                                 | 184 +++++++++++--------
 4 files changed, 138 insertions(+), 72 deletions(-)

diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 66135eb..884fedd 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -12,6 +12,7 @@
             android:name="com.proxmox.app.pve_flutter_frontend.MainActivity"
             android:exported="true"
             android:launchMode="singleTop"
+            android:taskAffinity=""
             android:theme="@style/LaunchTheme"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:hardwareAccelerated="true"
@@ -23,6 +24,21 @@
             </intent-filter>
         </activity>
 
+        <!-- Captures the OpenID Connect provider's redirect back into the app
+             for realms configured with an OAuth/OIDC login, see
+             flutter_web_auth_2 and ProxmoxLoginForm's OpenID login flow. -->
+        <activity
+            android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
+            android:exported="true"
+            android:taskAffinity="">
+            <intent-filter android:label="flutter_web_auth_2">
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="pveauth" />
+            </intent-filter>
+        </activity>
+
         <!-- This is used by Flutter to generate GeneratedPluginRegistrant.java -->
         <meta-data
             android:name="flutterEmbedding"
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index e3392af..5d804a0 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -7,13 +7,21 @@
 #include "generated_plugin_registrant.h"
 
 #include <biometric_storage/biometric_storage_plugin.h>
+#include <desktop_webview_window/desktop_webview_window_plugin.h>
 #include <url_launcher_linux/url_launcher_plugin.h>
+#include <window_to_front/window_to_front_plugin.h>
 
 void fl_register_plugins(FlPluginRegistry* registry) {
   g_autoptr(FlPluginRegistrar) biometric_storage_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "BiometricStoragePlugin");
   biometric_storage_plugin_register_with_registrar(biometric_storage_registrar);
+  g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
+  desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
   g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
   url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
+  g_autoptr(FlPluginRegistrar) window_to_front_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin");
+  window_to_front_plugin_register_with_registrar(window_to_front_registrar);
 }
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index 5a42b12..cc58f44 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -4,7 +4,9 @@
 
 list(APPEND FLUTTER_PLUGIN_LIST
   biometric_storage
+  desktop_webview_window
   url_launcher_linux
+  window_to_front
 )
 
 list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/pubspec.lock b/pubspec.lock
index def499d..0096409 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,18 +5,18 @@ packages:
     dependency: transitive
     description:
       name: _fe_analyzer_shared
-      sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
+      sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b
       url: "https://pub.dev"
     source: hosted
-    version: "93.0.0"
+    version: "100.0.0"
   analyzer:
     dependency: transitive
     description:
       name: analyzer
-      sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
+      sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf"
       url: "https://pub.dev"
     source: hosted
-    version: "10.0.1"
+    version: "13.0.0"
   args:
     dependency: transitive
     description:
@@ -53,34 +53,34 @@ packages:
     dependency: transitive
     description:
       name: build
-      sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c
+      sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae"
       url: "https://pub.dev"
     source: hosted
-    version: "4.0.5"
+    version: "4.0.7"
   build_config:
     dependency: transitive
     description:
       name: build_config
-      sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
+      sha256: "94eaf6708fe64408c632ef2689ca3777b112f9421306ccf4f8c84d7c5c9f83f8"
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.3.2"
   build_daemon:
     dependency: transitive
     description:
       name: build_daemon
-      sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
+      sha256: "79e05eaf15a48d7230b053a4363b8eaac0cc234bbd0134c3229455481f55cbc6"
       url: "https://pub.dev"
     source: hosted
-    version: "4.1.1"
+    version: "4.1.5"
   build_runner:
     dependency: "direct dev"
     description:
       name: build_runner
-      sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e"
+      sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16"
       url: "https://pub.dev"
     source: hosted
-    version: "2.13.1"
+    version: "2.15.1"
   built_collection:
     dependency: "direct main"
     description:
@@ -93,18 +93,18 @@ packages:
     dependency: "direct main"
     description:
       name: built_value
-      sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af"
+      sha256: "31b24be6615ec7fcf70b3aa5a7469fe35826485e639a16dd7eb83ba30e4cc6a8"
       url: "https://pub.dev"
     source: hosted
-    version: "8.12.5"
+    version: "8.12.7"
   built_value_generator:
     dependency: "direct dev"
     description:
       name: built_value_generator
-      sha256: ebdc4dbc63bcdb8c63eb39569bc1da8594d998862449b8dc0e064b7b999d7c96
+      sha256: "66091f1d4c07ed76b25a2834c49766b7baafb8843d5a1cfc1308059f898bf056"
       url: "https://pub.dev"
     source: hosted
-    version: "8.12.5"
+    version: "8.12.7"
   characters:
     dependency: transitive
     description:
@@ -133,18 +133,10 @@ packages:
     dependency: transitive
     description:
       name: code_assets
-      sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
-      url: "https://pub.dev"
-    source: hosted
-    version: "1.0.0"
-  code_builder:
-    dependency: transitive
-    description:
-      name: code_builder
-      sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
+      sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
       url: "https://pub.dev"
     source: hosted
-    version: "4.11.1"
+    version: "1.2.1"
   collection:
     dependency: "direct main"
     description:
@@ -165,10 +157,10 @@ packages:
     dependency: transitive
     description:
       name: cronet_http
-      sha256: "8e77bc6f203e0bc9126e6a9092508a3435dbcb04da3b53ed1a358909385c5e0e"
+      sha256: "9da9860b409d71e4b8259e3dee631176d499dee23e7cd45a3024ebd5181997d8"
       url: "https://pub.dev"
     source: hosted
-    version: "1.8.0"
+    version: "1.9.0"
   crypto:
     dependency: "direct main"
     description:
@@ -197,10 +189,18 @@ packages:
     dependency: transitive
     description:
       name: dart_style
-      sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
+      sha256: "59d53ef8eaed9d288ed9767618e2b31c4fa0383a127db59d5eb2e737a7638a60"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.9"
+  desktop_webview_window:
+    dependency: transitive
+    description:
+      name: desktop_webview_window
+      sha256: b6fdae2cbf9571879b1761c12f27facaf82e22d0bdc74d049907c2a09a432957
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.7"
+    version: "0.3.0"
   fake_async:
     dependency: transitive
     description:
@@ -315,6 +315,22 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_auth_2:
+    dependency: transitive
+    description:
+      name: flutter_web_auth_2
+      sha256: "8f9303471dcd96670878c9b7c0c4e14c37595b2add67465f6a868f17a5872dfc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.3"
+  flutter_web_auth_2_platform_interface:
+    dependency: transitive
+    description:
+      name: flutter_web_auth_2_platform_interface
+      sha256: ba0fbba55bffb47242025f96852ad1ffba34bc451568f56ef36e613612baffab
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.0"
   flutter_web_plugins:
     dependency: transitive
     description: flutter
@@ -348,10 +364,10 @@ packages:
     dependency: transitive
     description:
       name: hooks
-      sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
+      sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
       url: "https://pub.dev"
     source: hosted
-    version: "1.0.2"
+    version: "2.0.2"
   http:
     dependency: transitive
     description:
@@ -388,10 +404,10 @@ packages:
     dependency: "direct main"
     description:
       name: intl
-      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+      sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867"
       url: "https://pub.dev"
     source: hosted
-    version: "0.20.2"
+    version: "0.20.3"
   io:
     dependency: transitive
     description:
@@ -404,18 +420,34 @@ packages:
     dependency: transitive
     description:
       name: jni
-      sha256: "8706a77e94c76fe9ec9315e18949cc9479cc03af97085ca9c1077b61323ea12d"
+      sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.3"
+  jni_flutter:
+    dependency: transitive
+    description:
+      name: jni_flutter
+      sha256: "7b717011ea40d04fd47c2731d3d1d36eb99eba3435c2753d62489e8c3c9991d5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.2"
+  jni_util:
+    dependency: transitive
+    description:
+      name: jni_util
+      sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f"
       url: "https://pub.dev"
     source: hosted
-    version: "0.15.2"
+    version: "1.0.0"
   json_annotation:
     dependency: transitive
     description:
       name: json_annotation
-      sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
+      sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80"
       url: "https://pub.dev"
     source: hosted
-    version: "4.11.0"
+    version: "4.12.0"
   leak_tracker:
     dependency: transitive
     description:
@@ -476,10 +508,10 @@ packages:
     dependency: "direct main"
     description:
       name: meta
-      sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+      sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
       url: "https://pub.dev"
     source: hosted
-    version: "1.17.0"
+    version: "1.18.0"
   mime:
     dependency: transitive
     description:
@@ -488,14 +520,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.0"
-  native_toolchain_c:
-    dependency: transitive
-    description:
-      name: native_toolchain_c
-      sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
-      url: "https://pub.dev"
-    source: hosted
-    version: "0.17.6"
   nested:
     dependency: transitive
     description:
@@ -508,10 +532,10 @@ packages:
     dependency: transitive
     description:
       name: objective_c
-      sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
+      sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e
       url: "https://pub.dev"
     source: hosted
-    version: "9.3.0"
+    version: "9.5.0"
   package_config:
     dependency: transitive
     description:
@@ -532,18 +556,18 @@ packages:
     dependency: "direct main"
     description:
       name: path_provider
-      sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
+      sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.5"
+    version: "2.1.6"
   path_provider_android:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
+      sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.23"
+    version: "2.3.1"
   path_provider_foundation:
     dependency: transitive
     description:
@@ -556,18 +580,18 @@ packages:
     dependency: transitive
     description:
       name: path_provider_linux
-      sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+      sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.2"
   path_provider_platform_interface:
     dependency: transitive
     description:
       name: path_provider_platform_interface
-      sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+      sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.3"
   path_provider_windows:
     dependency: transitive
     description:
@@ -638,6 +662,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.5.0"
+  record_use:
+    dependency: transitive
+    description:
+      name: record_use
+      sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.6.0"
   retry:
     dependency: transitive
     description:
@@ -666,10 +698,10 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
+      sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.23"
+    version: "2.4.27"
   shared_preferences_foundation:
     dependency: transitive
     description:
@@ -735,10 +767,10 @@ packages:
     dependency: transitive
     description:
       name: source_gen
-      sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd"
+      sha256: a603f1fb984a7391ae5978d1b92bfaaa08b350dca5c825256f925818f7943bf5
       url: "https://pub.dev"
     source: hosted
-    version: "4.2.2"
+    version: "4.2.4"
   source_span:
     dependency: transitive
     description:
@@ -791,10 +823,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
+      sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.10"
+    version: "0.7.11"
   typed_data:
     dependency: transitive
     description:
@@ -815,10 +847,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_android
-      sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
+      sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
       url: "https://pub.dev"
     source: hosted
-    version: "6.3.29"
+    version: "6.3.32"
   url_launcher_ios:
     dependency: transitive
     description:
@@ -855,10 +887,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_web
-      sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
+      sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.2"
+    version: "2.4.3"
   url_launcher_windows:
     dependency: transitive
     description:
@@ -879,10 +911,10 @@ packages:
     dependency: transitive
     description:
       name: vm_service
-      sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
+      sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
       url: "https://pub.dev"
     source: hosted
-    version: "15.0.2"
+    version: "15.2.0"
   watcher:
     dependency: transitive
     description:
@@ -923,6 +955,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.15.0"
+  window_to_front:
+    dependency: transitive
+    description:
+      name: window_to_front
+      sha256: "14fad8984db4415e2eeb30b04bb77140b180e260d6cb66b26de126a8657a9241"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.0.4"
   xdg_directories:
     dependency: transitive
     description:
@@ -940,5 +980,5 @@ packages:
     source: hosted
     version: "3.1.3"
 sdks:
-  dart: ">=3.10.3 <4.0.0"
-  flutter: ">=3.38.4"
+  dart: ">=3.12.0 <4.0.0"
+  flutter: ">=3.44.0"
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/2] fix #4281: android: match renamed OpenID callback scheme
  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 ` Azharul Haque
  2026-08-10 13:59 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
  2 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10  5:40 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

Follow-up to the proxmox_login_manager patch renaming the callback
scheme from pveauth to the namespaced com.proxmox.app.openid.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 android/app/src/main/AndroidManifest.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 884fedd..19a9362 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -35,7 +35,7 @@
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="pveauth" />
+                <data android:scheme="com.proxmox.app.openid" />
             </intent-filter>
         </activity>
 
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/2] android: register OpenID Connect callback activity for #4281
  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 ` Shan Shaji
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
  2 siblings, 1 reply; 12+ messages in thread
From: Shan Shaji @ 2026-08-10 13:59 UTC (permalink / raw)
  To: Azharul Haque, pve-devel

Hi Azharul,

Thank you so much for your patches. If you are sending patches for the
first time and haven't yet had the chance to fill out our CLA. Please
check this: https://proxmox.com/en/about/open-source/developers

Right now, AFAICT, you have sent series for each repository seperately:

- android: register OpenID Connect callback activity for #4281 (pve_flutter_frontend)
- ui: implement OpenID Connect login flow for #4281 (proxmox_login_manager)
- add OpenID Connect support for #4281 (proxmox_dart_api_client)

Instead of sending the changes seperately as different series. Please
send them as a single series including the repository name inside the patch
subject prefix [0]. It would be really helpful for us when testing and
reviewing the changes. Thus reducing the noise on mailling list as well. 

- [0] https://pve.proxmox.com/wiki/Developer_Documentation#Preparing_Patches

On Mon Aug 10, 2026 at 7:40 AM CEST, Azharul Haque wrote:
> The native Flutter "Proxmox VE Companion" app never implemented OpenID
> Connect / OAuth realm login (bug #4281[0]): selecting an OAuth realm
> just showed username/password fields that could never work.
>
> Android requires an app to explicitly declare, in AndroidManifest.xml,
> which activity should handle a given custom URL scheme. This series
> registers flutter_web_auth_2's CallbackActivity for the
> com.proxmox.app.openid:// redirect scheme used by the OAuth flow, and
> fixes it up to match a later rename of that scheme.
>
> No iOS-side changes are needed: ASWebAuthenticationSession resolves
> the custom-scheme redirect at runtime without a static declaration
> equivalent to this manifest entry.
>
> Depends on the companion proxmox_dart_api_client and
> proxmox_login_manager series, which implement the OIDC API calls and
> login-form/OAuth-flow UI respectively.
>
> Verified end-to-end against a real PVE server with an Authentik OIDC
> realm, on both Android and iOS.
>
> [0] https://bugzilla.proxmox.com/show_bug.cgi?id=4281
>
> Azharul Haque (2):
>   fix #4281: android: register OpenID Connect callback activity
>   fix #4281: android: match renamed OpenID callback scheme
>
>  android/app/src/main/AndroidManifest.xml     |  16 ++
>  linux/flutter/generated_plugin_registrant.cc |   8 +
>  linux/flutter/generated_plugins.cmake        |   2 +
>  pubspec.lock                                 | 184 +++++++++++--------
>  4 files changed, 138 insertions(+), 72 deletions(-)





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281)
  2026-08-10 13:59 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
@ 2026-08-10 14:47   ` Azharul Haque
  2026-08-10 14:47     ` [PATCH dart-api-client v2 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
                       ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

The native Flutter "Proxmox VE Companion" app never implemented OpenID
Connect / OAuth realm login (bug #4281[0]): selecting an OAuth realm
just showed username/password fields that could never work.

v2: resending as a single combined series across all three affected
repositories, per feedback -- each patch subject is now prefixed with
the (shortened) repository name it applies to. No functional changes
from v1.

This series spans three repositories and should be applied together:

  dart-api-client (2 patches):
    API-layer building blocks for OIDC login -- a `type` property on
    PveAccessDomainModel to detect OpenID realms, and helpers for
    PVE's /access/openid/auth-url and /access/openid/login endpoints.

  login-manager (3 patches):
    Login-form UI: hide username/password fields for OpenID realms
    and drive the OAuth flow via flutter_web_auth_2 (system browser /
    ASWebAuthenticationSession on iOS, Chrome Custom Tabs on Android
    -- deliberately not an in-app webview); a fix for a stale Continue
    button state when switching realms; and namespacing the OpenID
    redirect scheme under Proxmox's own reserved com.proxmox.*
    package prefix to avoid Android custom-URL-scheme collisions.

  flutter-frontend (2 patches):
    Android-side wiring: register flutter_web_auth_2's
    CallbackActivity in AndroidManifest.xml for the OpenID redirect
    scheme, and a follow-up fix to match a later rename of that
    scheme. No iOS-side changes are required here --
    ASWebAuthenticationSession resolves the custom-scheme redirect at
    runtime without a static declaration equivalent to the Android
    manifest entry.

Verified end-to-end against a real PVE server with an Authentik OIDC
realm, on both Android and iOS.

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=4281

Azharul Haque (7):
  fix #4281: access: add `type` property to `PveAccessDomainModel`
  fix #4281: access: add OpenID Connect auth-url/login helpers
  fix #4281: ui: add OpenID Connect login flow to login form
  fix #4281: ui: fix stale Continue button state on realm switch
  fix #4281: ui: use a namespaced OpenID callback scheme
  fix #4281: android: register OpenID Connect callback activity
  fix #4281: android: match renamed OpenID callback scheme

--
2.50.1 (Apple Git-155)




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH dart-api-client v2 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel`
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
@ 2026-08-10 14:47     ` Azharul Haque
  2026-08-10 14:47     ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
                       ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

The realm/domain list returned by /access/domains always includes a
`type` field (pam, pve, ldap, ad, openid, ...), but the model never
captured it. Without it, callers have no way to tell an OpenID Connect
realm apart from a password-based one, which is why the login form
falls back to showing username/password fields even for realms that
require a browser-based OpenID login.

Add the property and an `isOpenIdRealm` convenience getter, used by
the login form in the following commits.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/src/models/pve_access_domain_model.dart | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lib/src/models/pve_access_domain_model.dart b/lib/src/models/pve_access_domain_model.dart
index 6298bbe..a2fff31 100644
--- a/lib/src/models/pve_access_domain_model.dart
+++ b/lib/src/models/pve_access_domain_model.dart
@@ -7,12 +7,14 @@ abstract class PveAccessDomainModel
     implements Built<PveAccessDomainModel, PveAccessDomainModelBuilder> {
   // Fields
   String get realm;
+  String get type;
   String? get comment;
   String? get tfa;
   @BuiltValueField(wireName: 'default')
   int? get defaultValue;
 
   bool get isDefaultRealm => defaultValue == 1;
+  bool get isOpenIdRealm => type == 'openid';
 
   PveAccessDomainModel._();
 
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers
  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-10 14:47     ` 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
                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

Add openIdAuthUrl() and openIdLogin(), mirroring the existing
authenticate()/accessDomains() functions used by the login form
before an authenticated ProxmoxApiClient exists.

openIdAuthUrl() requests the provider's authorization URL for a
realm from /access/openid/auth-url. openIdLogin() exchanges the
state/code obtained from the provider's redirect for a PVE ticket
via /access/openid/login, the same way authenticate() does for
password realms.

The OpenID login response carries the authenticated username in its
body rather than it being known upfront by the caller, so
handleOpenIdLoginResponse() is added alongside the existing
handleAccessTicketResponse()/handleTfaChallengeResponse() to build
Credentials from it.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/src/authenticate.dart           | 85 +++++++++++++++++++++++++++++
 lib/src/handle_ticket_response.dart | 36 ++++++++++++
 test/test.dart                      | 20 +++++++
 3 files changed, 141 insertions(+)

diff --git a/lib/src/authenticate.dart b/lib/src/authenticate.dart
index 7bd9cef..e2d76a1 100644
--- a/lib/src/authenticate.dart
+++ b/lib/src/authenticate.dart
@@ -72,6 +72,91 @@ Future<ProxmoxApiClient> authenticate(
   }
 }
 
+/// Requests the provider's authorization URL for an OpenID Connect realm.
+///
+/// [redirectUrl] must match a redirect URI registered with the realm's
+/// OpenID provider, and is where the provider sends the user back to after
+/// they authenticate (carrying `state` and `code` query parameters).
+Future<String> openIdAuthUrl(
+  String realm,
+  Uri apiBaseUrl,
+  Uri redirectUrl,
+  bool validateSSL, {
+  http.Client? httpClient,
+}) async {
+  httpClient ??= getCustomIOHttpClient(validateSSL: validateSSL);
+
+  var body = {
+    'realm': realm,
+    'redirect-url': redirectUrl.toString(),
+  };
+
+  try {
+    final path = '/api2/json/access/openid/auth-url';
+    final response = await httpClient
+        .post(apiBaseUrl.replace(path: path), body: body)
+        .timeout(Duration(seconds: 25));
+
+    response.validate(true);
+
+    return jsonDecode(response.body)['data'] as String;
+  } on NSErrorClientException catch (e) {
+    if (e.error.code == -1202) {
+      throw HandshakeException(e.message);
+    }
+    rethrow;
+  } on http.ClientException catch (e) {
+    if (e.message.contains('net::ERR_CERT_AUTHORITY_INVALID')) {
+      throw HandshakeException(e.message);
+    }
+    rethrow;
+  }
+}
+
+/// Exchanges the `state`/`code` obtained from the OpenID provider's redirect
+/// for a Proxmox VE ticket, mirroring what [authenticate] does for password
+/// realms.
+Future<ProxmoxApiClient> openIdLogin(
+  String state,
+  String code,
+  Uri apiBaseUrl,
+  Uri redirectUrl,
+  bool validateSSL, {
+  http.Client? httpClient,
+}) async {
+  httpClient ??= getCustomIOHttpClient(validateSSL: validateSSL);
+
+  var body = {
+    'state': state,
+    'code': code,
+    'redirect-url': redirectUrl.toString(),
+  };
+
+  try {
+    final path = '/api2/json/access/openid/login';
+    final response = await httpClient
+        .post(apiBaseUrl.replace(path: path), body: body)
+        .timeout(Duration(seconds: 25));
+
+    final credentials = handleOpenIdLoginResponse(response, apiBaseUrl);
+
+    return ProxmoxApiClient(
+      credentials,
+      httpClient: httpClient,
+    );
+  } on NSErrorClientException catch (e) {
+    if (e.error.code == -1202) {
+      throw HandshakeException(e.message);
+    }
+    rethrow;
+  } on http.ClientException catch (e) {
+    if (e.message.contains('net::ERR_CERT_AUTHORITY_INVALID')) {
+      throw HandshakeException(e.message);
+    }
+    rethrow;
+  }
+}
+
 Future<List<PveAccessDomainModel?>> accessDomains(
   Uri apiBaseUrl,
   bool validateSSL, {
diff --git a/lib/src/handle_ticket_response.dart b/lib/src/handle_ticket_response.dart
index ba2128f..a43aed0 100644
--- a/lib/src/handle_ticket_response.dart
+++ b/lib/src/handle_ticket_response.dart
@@ -39,6 +39,42 @@ Credentials handleAccessTicketResponse(
   );
 }
 
+Credentials handleOpenIdLoginResponse(
+    http.Response response, Uri apiBaseUrl) {
+  response.validate(false);
+
+  final bodyJson = jsonDecode(response.body)['data'];
+
+  final ticket = bodyJson['ticket'];
+
+  final csrfToken = bodyJson['CSRFPreventionToken'];
+
+  final username = bodyJson['username'];
+
+  final ticketRegex = RegExp(r'(PVE|PMG)(?:QUAR)?:(?:(\S+):)?([A-Z0-9]{8})::')
+      .firstMatch(bodyJson['ticket'])!;
+
+  final time = DateTime.fromMillisecondsSinceEpoch(
+      int.parse(ticketRegex.group(3)!, radix: 16) * 1000);
+
+  TfaChallenge? tfa;
+  if (ticket.startsWith('PVE:!tfa!')) {
+    tfa = TfaChallenge.fromJson(
+        jsonDecode(Uri.decodeComponent(ticket.substring(9).split(':')[0])));
+  } else if (bodyJson['NeedTFA'] != null && bodyJson['NeedTFA'] == 1) {
+    tfa = TfaChallenge.legacy();
+  }
+
+  return Credentials(
+    apiBaseUrl,
+    username,
+    ticket: ticket,
+    csrfToken: csrfToken,
+    expiration: time,
+    tfa: tfa,
+  );
+}
+
 Credentials handleTfaChallengeResponse(
     http.Response response, Credentials pendingTfaCredentials) {
   response.validate(false);
diff --git a/test/test.dart b/test/test.dart
index 23368a2..86c2272 100644
--- a/test/test.dart
+++ b/test/test.dart
@@ -49,5 +49,25 @@ void main() {
                   DateTime.fromMillisecondsSinceEpoch(
                       int.parse('5DF8EC22', radix: 16) * 1000)));
     });
+
+    test('valid openid login response extraction', () {
+      final ticket =
+          'PVE:jdoe@keycloak:5DF8EC22::STV4HNO1wplmsyMDM5s6SUsU4cS7sBBBw+HOCEhSSV+6WGtz3zwIzHqBhq/ziJoBs7NqqyLXG4wn9jXJCMdYht+ndqwxtdFQsUNOF1Q/eTWwcyl+Q1fmPNOIIUoxMY8OqGBVozgIimiAJxdqm+2SJnrPEmlJge6m3yf/OEVAkKFCfRMOtSuyVnIbuLx6h6obvezBUP5+ZHzeTMmmXcH4rOsOKgW9XfwryLHbkjjq9Ennx0xjQaBD9Bo5ERquY0hNmWcdPC/p7ZzILTr4xH9sJe9Na2z6GhgJyTgOCAMengyIegySMq7IKIkmsp8odF4/iIC3005/XLF4w/DjPYQUMA==';
+      final csrfToken = '5DF8EDEC:/bb44xdHyVQDo2eD/8ty0WVXwMgwt1HjhVHLZX2YbxQ';
+      var response = http.Response(
+          '{"data":{"clustername":"testcluster","username":"jdoe@keycloak","CSRFPreventionToken":"5DF8EDEC:/bb44xdHyVQDo2eD/8ty0WVXwMgwt1HjhVHLZX2YbxQ","cap":{},"ticket":"$ticket"}}',
+          200);
+      expect(
+          handleOpenIdLoginResponse(response, dummyEndpoint),
+          isA<Credentials>()
+              .having((e) => e.username, 'Username', 'jdoe@keycloak')
+              .having((e) => e.ticket, 'Ticket', ticket)
+              .having((e) => e.csrfToken, 'CSRF Token', csrfToken)
+              .having(
+                  (e) => e.expiration,
+                  'Token expiration time',
+                  DateTime.fromMillisecondsSinceEpoch(
+                      int.parse('5DF8EC22', radix: 16) * 1000)));
+    });
   });
 }
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH login-manager v2 1/3] fix #4281: ui: add OpenID Connect login flow to login form
  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-10 14:47     ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
@ 2026-08-10 14:47     ` Azharul Haque
  2026-08-10 14:47     ` [PATCH login-manager v2 2/3] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

Realms of type openid never got a distinct login flow: the form kept
showing username/password fields, and submitting against an OpenID
realm just sent a password login request the realm can't handle.

When the selected realm is OpenID, the form now hides the
username/password fields and, on Continue, drives the browser-based
OpenID flow instead of the password one:

 - request the provider's authorization URL via openIdAuthUrl()
 - open it with flutter_web_auth_2, which uses the system browser /
   ASWebAuthenticationSession and captures the provider's redirect to
   a pveauth:// callback without needing an in-app webview
 - exchange the returned state/code for a ticket via openIdLogin()

The remainder of the login sequence (TFA challenge, fetching cluster
status, persisting the login) is identical between password and
OpenID logins, so it's factored out of _onLoginButtonPressed() into
a shared _finishLogin() used by both flows.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/proxmox_login_form.dart | 419 +++++++++++++++++++++++-------------
 pubspec.lock                | 160 ++++++++++++--
 pubspec.yaml                |   1 +
 3 files changed, 409 insertions(+), 171 deletions(-)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 5b9a64e..b73165c 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -2,6 +2,7 @@ import 'dart:io';
 import 'dart:async';
 
 import 'package:flutter/material.dart';
+import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
 import 'package:collection/collection.dart';
 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'
     as proxclient;
@@ -12,6 +13,12 @@ import 'package:proxmox_login_manager/proxmox_tfa_form.dart';
 import 'package:proxmox_login_manager/extension.dart';
 import 'package:proxmox_login_manager/proxmox_password_store.dart';
 
+/// Custom URL scheme the identity provider redirects back to once an
+/// OpenID Connect login completes. Must be registered as a valid redirect
+/// URI with the realm's provider, as well as in the platform manifests
+/// (AndroidManifest.xml / Info.plist).
+const String openIdCallbackScheme = 'pveauth';
+
 class ProxmoxProgressModel {
   int inProgress = 0;
   String message = 'Loading...';
@@ -85,6 +92,8 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
       );
     }
 
+    final isOpenId = widget.selectedDomain?.isOpenIdRealm ?? false;
+
     return AutofillGroup(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
@@ -97,20 +106,6 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
             controller: widget.originController,
             enabled: false,
           ),
-          TextFormField(
-            decoration: const InputDecoration(
-              icon: Icon(Icons.person),
-              labelText: 'Username',
-            ),
-            controller: widget.usernameController,
-            validator: (value) {
-              if (value!.isEmpty) {
-                return 'Please enter username';
-              }
-              return null;
-            },
-            autofillHints: const [AutofillHints.username],
-          ),
           DropdownButtonFormField(
             decoration: const InputDecoration(icon: Icon(Icons.domain)),
             items: widget.accessDomains!
@@ -127,54 +122,80 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
                 widget.accessDomains!.map((e) => Text(e!.realm)).toList(),
             initialValue: widget.selectedDomain,
           ),
-          Stack(
-            children: [
-              TextFormField(
-                decoration: const InputDecoration(
-                  icon: Icon(Icons.lock),
-                  labelText: 'Password',
+          if (isOpenId)
+            Padding(
+              padding: const EdgeInsets.symmetric(vertical: 16),
+              child: Text(
+                "This realm signs you in through your browser. "
+                "Tap Continue to proceed.",
+                style: Theme.of(context).textTheme.bodyMedium,
+                textAlign: TextAlign.center,
+              ),
+            )
+          else ...[
+            TextFormField(
+              decoration: const InputDecoration(
+                icon: Icon(Icons.person),
+                labelText: 'Username',
+              ),
+              controller: widget.usernameController,
+              validator: (value) {
+                if (value!.isEmpty) {
+                  return 'Please enter username';
+                }
+                return null;
+              },
+              autofillHints: const [AutofillHints.username],
+            ),
+            Stack(
+              children: [
+                TextFormField(
+                  decoration: const InputDecoration(
+                    icon: Icon(Icons.lock),
+                    labelText: 'Password',
+                  ),
+                  controller: widget.passwordController,
+                  obscureText: _obscure,
+                  autocorrect: false,
+                  focusNode: passwordFocusNode,
+                  validator: (value) {
+                    if (value!.isEmpty) {
+                      return 'Please enter password';
+                    }
+                    return null;
+                  },
+                  onFieldSubmitted: (value) => widget.onPasswordSubmitted!(),
+                  autofillHints: const [AutofillHints.password],
                 ),
-                controller: widget.passwordController,
-                obscureText: _obscure,
-                autocorrect: false,
-                focusNode: passwordFocusNode,
-                validator: (value) {
-                  if (value!.isEmpty) {
-                    return 'Please enter password';
+                Align(
+                  alignment: Alignment.bottomRight,
+                  child: IconButton(
+                    constraints: BoxConstraints.tight(const Size(58, 58)),
+                    iconSize: 24,
+                    tooltip: _obscure ? "Show password" : "Hide password",
+                    icon: Icon(
+                        _obscure ? Icons.visibility : Icons.visibility_off),
+                    onPressed: () => setState(() {
+                      _obscure = !_obscure;
+                    }),
+                  ),
+                )
+              ],
+            ),
+            if (widget.canSavePassword ?? false)
+              CheckboxListTile(
+                title: const Text('Save password in biometric storage'),
+                value: _savePwCheckbox ?? widget.passwordSaved ?? false,
+                onChanged: (value) {
+                  if (widget.onSavePasswordChanged != null) {
+                    widget.onSavePasswordChanged!(value!);
                   }
-                  return null;
+                  setState(() {
+                    _savePwCheckbox = value!;
+                  });
                 },
-                onFieldSubmitted: (value) => widget.onPasswordSubmitted!(),
-                autofillHints: const [AutofillHints.password],
-              ),
-              Align(
-                alignment: Alignment.bottomRight,
-                child: IconButton(
-                  constraints: BoxConstraints.tight(const Size(58, 58)),
-                  iconSize: 24,
-                  tooltip: _obscure ? "Show password" : "Hide password",
-                  icon:
-                      Icon(_obscure ? Icons.visibility : Icons.visibility_off),
-                  onPressed: () => setState(() {
-                    _obscure = !_obscure;
-                  }),
-                ),
               )
-            ],
-          ),
-          if (widget.canSavePassword ?? false)
-            CheckboxListTile(
-              title: const Text('Save password in biometric storage'),
-              value: _savePwCheckbox ?? widget.passwordSaved ?? false,
-              onChanged: (value) {
-                if (widget.onSavePasswordChanged != null) {
-                  widget.onSavePasswordChanged!(value!);
-                }
-                setState(() {
-                  _savePwCheckbox = value!;
-                });
-              },
-            )
+          ],
         ],
       ),
     );
@@ -418,7 +439,13 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                                 });
                                                 if (isValid) {
                                                   if (snapshot.hasData) {
-                                                    _onLoginButtonPressed();
+                                                    if (_selectedDomain
+                                                            ?.isOpenIdRealm ==
+                                                        true) {
+                                                      _onOpenIdLoginButtonPressed();
+                                                    } else {
+                                                      _onLoginButtonPressed();
+                                                    }
                                                   } else {
                                                     setState(() {
                                                       _accessDomains =
@@ -478,98 +505,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 +560,181 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     });
   }
 
+  Future<void> _onOpenIdLoginButtonPressed() async {
+    setState(() {
+      _progressModel
+        ..inProgress += 1
+        ..message = 'Connecting to identity provider...';
+    });
+
+    try {
+      final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
+      final origin = normalizeUrl(_originController.text.trim());
+      final realm = _selectedDomain!.realm;
+      final redirectUrl =
+          Uri(scheme: openIdCallbackScheme, host: 'openid-callback');
+
+      final authUrl = await proxclient.openIdAuthUrl(
+          realm, origin, redirectUrl, settings.sslValidation!);
+
+      final result = await FlutterWebAuth2.authenticate(
+        url: authUrl,
+        callbackUrlScheme: openIdCallbackScheme,
+      );
+
+      final callbackUri = Uri.parse(result);
+      final state = callbackUri.queryParameters['state'];
+      final code = callbackUri.queryParameters['code'];
+      if (state == null || code == null) {
+        throw proxclient.ProxmoxApiException(
+            'Identity provider did not return an authorization code', 400);
+      }
+
+      final client = await proxclient.openIdLogin(
+          state, code, origin, redirectUrl, settings.sslValidation!);
+
+      final fullUsername = client.credentials.username;
+      final username = fullUsername.contains('@')
+          ? fullUsername.substring(0, fullUsername.lastIndexOf('@'))
+          : fullUsername;
+
+      await _finishLogin(client, realm: realm, username: username);
+    } on proxclient.ProxmoxApiException catch (e) {
+      print(e);
+      if (mounted) {
+        showDialog(
+          context: context,
+          builder: (context) => ProxmoxApiErrorDialog(
+            exception: e,
+          ),
+        );
+      }
+    } catch (e, trace) {
+      print(e);
+      print(trace);
+      if (mounted) {
+        if (e.runtimeType == HandshakeException) {
+          showDialog(
+            context: context,
+            builder: (context) => const ProxmoxCertificateErrorDialog(),
+          );
+        } else {
+          showDialog(
+            context: context,
+            builder: (context) => ConnectionErrorDialog(exception: e),
+          );
+        }
+      }
+    }
+    setState(() {
+      _progressModel.inProgress -= 1;
+    });
+  }
+
+  /// Shared tail of both the password and OpenID login flows: 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;
diff --git a/pubspec.lock b/pubspec.lock
index ac4ba18..9250de9 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -133,10 +133,10 @@ packages:
     dependency: transitive
     description:
       name: code_assets
-      sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
+      sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
       url: "https://pub.dev"
     source: hosted
-    version: "1.0.0"
+    version: "1.2.1"
   code_builder:
     dependency: transitive
     description:
@@ -193,6 +193,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.1.7"
+  desktop_webview_window:
+    dependency: transitive
+    description:
+      name: desktop_webview_window
+      sha256: b6fdae2cbf9571879b1761c12f27facaf82e22d0bdc74d049907c2a09a432957
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.3.0"
   fake_async:
     dependency: transitive
     description:
@@ -243,6 +251,22 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_auth_2:
+    dependency: "direct main"
+    description:
+      name: flutter_web_auth_2
+      sha256: "8f9303471dcd96670878c9b7c0c4e14c37595b2add67465f6a868f17a5872dfc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.3"
+  flutter_web_auth_2_platform_interface:
+    dependency: transitive
+    description:
+      name: flutter_web_auth_2_platform_interface
+      sha256: ba0fbba55bffb47242025f96852ad1ffba34bc451568f56ef36e613612baffab
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.0"
   flutter_web_plugins:
     dependency: transitive
     description: flutter
@@ -268,10 +292,10 @@ packages:
     dependency: transitive
     description:
       name: hooks
-      sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
+      sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
       url: "https://pub.dev"
     source: hosted
-    version: "1.0.2"
+    version: "2.0.2"
   http:
     dependency: transitive
     description:
@@ -388,10 +412,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+      sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
       url: "https://pub.dev"
     source: hosted
-    version: "1.17.0"
+    version: "1.18.0"
   mime:
     dependency: transitive
     description:
@@ -400,22 +424,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.0"
-  native_toolchain_c:
-    dependency: transitive
-    description:
-      name: native_toolchain_c
-      sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
-      url: "https://pub.dev"
-    source: hosted
-    version: "0.17.6"
   objective_c:
     dependency: transitive
     description:
       name: objective_c
-      sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
+      sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e
       url: "https://pub.dev"
     source: hosted
-    version: "9.3.0"
+    version: "9.5.0"
   package_config:
     dependency: transitive
     description:
@@ -432,6 +448,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.9.1"
+  path_provider:
+    dependency: transitive
+    description:
+      name: path_provider
+      sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.6"
+  path_provider_android:
+    dependency: transitive
+    description:
+      name: path_provider_android
+      sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.23"
+  path_provider_foundation:
+    dependency: transitive
+    description:
+      name: path_provider_foundation
+      sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.6.0"
   path_provider_linux:
     dependency: transitive
     description:
@@ -503,6 +543,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.5.0"
+  record_use:
+    dependency: transitive
+    description:
+      name: record_use
+      sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.6.0"
   retry:
     dependency: transitive
     description:
@@ -648,10 +696,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
+      sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.10"
+    version: "0.7.11"
   typed_data:
     dependency: transitive
     description:
@@ -660,6 +708,70 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.4.0"
+  url_launcher:
+    dependency: transitive
+    description:
+      name: url_launcher
+      sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.3.2"
+  url_launcher_android:
+    dependency: transitive
+    description:
+      name: url_launcher_android
+      sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.3.32"
+  url_launcher_ios:
+    dependency: transitive
+    description:
+      name: url_launcher_ios
+      sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.4.1"
+  url_launcher_linux:
+    dependency: transitive
+    description:
+      name: url_launcher_linux
+      sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.2.2"
+  url_launcher_macos:
+    dependency: transitive
+    description:
+      name: url_launcher_macos
+      sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.2.5"
+  url_launcher_platform_interface:
+    dependency: transitive
+    description:
+      name: url_launcher_platform_interface
+      sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.3.2"
+  url_launcher_web:
+    dependency: transitive
+    description:
+      name: url_launcher_web
+      sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.3"
+  url_launcher_windows:
+    dependency: transitive
+    description:
+      name: url_launcher_windows
+      sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.5"
   vector_math:
     dependency: transitive
     description:
@@ -716,6 +828,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.15.0"
+  window_to_front:
+    dependency: transitive
+    description:
+      name: window_to_front
+      sha256: "14fad8984db4415e2eeb30b04bb77140b180e260d6cb66b26de126a8657a9241"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.0.4"
   xdg_directories:
     dependency: transitive
     description:
@@ -733,5 +853,5 @@ packages:
     source: hosted
     version: "3.1.3"
 sdks:
-  dart: ">=3.10.0 <4.0.0"
-  flutter: ">=3.35.6"
+  dart: ">=3.12.0 <4.0.0"
+  flutter: ">=3.44.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 4652ced..be38e6d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,6 +16,7 @@ dependencies:
   built_collection: ^5.0.0
   proxmox_dart_api_client:
     path: ../proxmox_dart_api_client
+  flutter_web_auth_2: ^5.0.3
 
 
 dev_dependencies:
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH login-manager v2 2/3] fix #4281: ui: fix stale Continue button state on realm switch
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
                       ` (2 preceding siblings ...)
  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-10 14:47     ` Azharul Haque
  2026-08-10 14:47     ` [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme Azharul Haque
                       ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

Form(onChanged: ...) revalidates against whatever fields are
currently mounted at the moment a FormField's own value changes.
For the realm dropdown that runs before the following rebuild
adds/removes the username/password fields for the newly selected
realm's type, so Continue got enabled/disabled based on the
outgoing realm's field set rather than the incoming one. Most
visibly, switching from an OpenID realm to a password realm left
Continue enabled with both fields empty, only failing validation
once actually pressed.

Recompute _submitButtonEnabled explicitly in onDomainChanged instead:
OpenID realms have nothing to validate, so enable it directly; other
realms are revalidated in a post-frame callback once the rebuild has
settled. Do the same after the initial realm auto-selection in
_getAccessDomains(), in case the default realm is an OpenID one.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/proxmox_login_form.dart | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index b73165c..002838c 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -396,6 +396,31 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                     setState(() {
                                       _selectedDomain = value;
                                     });
+                                    // The Form's onChanged callback validates
+                                    // against whatever fields are mounted at
+                                    // the moment the dropdown's own value
+                                    // changes, which runs before this rebuild
+                                    // adds/removes the username/password
+                                    // fields for the newly selected realm.
+                                    // Recompute once that rebuild has
+                                    // happened so we validate the field set
+                                    // that's actually showing.
+                                    if (value?.isOpenIdRealm == true) {
+                                      setState(() {
+                                        _submitButtonEnabled = true;
+                                      });
+                                    } else {
+                                      WidgetsBinding.instance
+                                          .addPostFrameCallback((_) {
+                                        if (!mounted) return;
+                                        setState(() {
+                                          _submitButtonEnabled = _formKey
+                                                  .currentState
+                                                  ?.validate() ??
+                                              false;
+                                        });
+                                      });
+                                    }
                                   },
                                   onOriginSubmitted: () {
                                     final isValid =
@@ -828,6 +853,9 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     setState(() {
       _progressModel.inProgress -= 1;
       _selectedDomain = selection;
+      // An OpenID realm has no username/password to fill in, so there's
+      // nothing for the form to validate before Continue is usable.
+      _submitButtonEnabled = selection?.isOpenIdRealm == true;
     });
 
     return response;
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
                       ` (3 preceding siblings ...)
  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     ` Azharul Haque
  2026-08-10 14:47     ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
  2026-08-10 14:47     ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

pveauth:// worked in testing, but on Android a custom URL scheme
isn't exclusively owned the way a verified App Link is: any other
app installed on the device could in principle also declare an
intent-filter for the same short, guessable scheme.

Use com.proxmox.app.openid instead, derived from the app's own
package/bundle identifier (com.proxmox.*, reserved for Proxmox on
both app stores), so collisions with another app's scheme are
effectively ruled out rather than merely unlikely.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 lib/proxmox_login_form.dart | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 002838c..c5ea090 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -17,7 +17,11 @@ import 'package:proxmox_login_manager/proxmox_password_store.dart';
 /// OpenID Connect login completes. Must be registered as a valid redirect
 /// URI with the realm's provider, as well as in the platform manifests
 /// (AndroidManifest.xml / Info.plist).
-const String openIdCallbackScheme = 'pveauth';
+///
+/// Derived from the app's own package/bundle identifier (`com.proxmox.*`,
+/// reserved for Proxmox on both app stores) rather than an arbitrary word,
+/// so it can't collide with another app's custom URL scheme.
+const String openIdCallbackScheme = 'com.proxmox.app.openid';
 
 class ProxmoxProgressModel {
   int inProgress = 0;
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
                       ` (4 preceding siblings ...)
  2026-08-10 14:47     ` [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme Azharul Haque
@ 2026-08-10 14:47     ` Azharul Haque
  2026-08-10 14:47     ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

proxmox_login_manager's OpenID login flow (see the corresponding
patch there) opens the provider's authorization URL via
flutter_web_auth_2 and expects the provider's redirect back to
pveauth://openid-callback to be captured by the app. On Android this
requires explicitly registering flutter_web_auth_2's CallbackActivity
for that scheme; add it, following the plugin's setup instructions.
Also set android:taskAffinity="" on both activities as recommended
for this flutter_web_auth_2 version, to avoid a stray task appearing
after the callback returns control to MainActivity.

No changes needed on iOS: ASWebAuthenticationSession handles the
custom-scheme redirect without any Info.plist registration.

linux/flutter/generated_plugin_registrant.cc, generated_plugins.cmake
and pubspec.lock are regenerated as a consequence of the new
transitive dependency on flutter_web_auth_2 (and, on Linux/Windows,
its desktop_webview_window fallback).

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 android/app/src/main/AndroidManifest.xml     |  16 ++
 linux/flutter/generated_plugin_registrant.cc |   8 +
 linux/flutter/generated_plugins.cmake        |   2 +
 pubspec.lock                                 | 184 +++++++++++--------
 4 files changed, 138 insertions(+), 72 deletions(-)

diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 66135eb..884fedd 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -12,6 +12,7 @@
             android:name="com.proxmox.app.pve_flutter_frontend.MainActivity"
             android:exported="true"
             android:launchMode="singleTop"
+            android:taskAffinity=""
             android:theme="@style/LaunchTheme"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:hardwareAccelerated="true"
@@ -23,6 +24,21 @@
             </intent-filter>
         </activity>
 
+        <!-- Captures the OpenID Connect provider's redirect back into the app
+             for realms configured with an OAuth/OIDC login, see
+             flutter_web_auth_2 and ProxmoxLoginForm's OpenID login flow. -->
+        <activity
+            android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
+            android:exported="true"
+            android:taskAffinity="">
+            <intent-filter android:label="flutter_web_auth_2">
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="pveauth" />
+            </intent-filter>
+        </activity>
+
         <!-- This is used by Flutter to generate GeneratedPluginRegistrant.java -->
         <meta-data
             android:name="flutterEmbedding"
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index e3392af..5d804a0 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -7,13 +7,21 @@
 #include "generated_plugin_registrant.h"
 
 #include <biometric_storage/biometric_storage_plugin.h>
+#include <desktop_webview_window/desktop_webview_window_plugin.h>
 #include <url_launcher_linux/url_launcher_plugin.h>
+#include <window_to_front/window_to_front_plugin.h>
 
 void fl_register_plugins(FlPluginRegistry* registry) {
   g_autoptr(FlPluginRegistrar) biometric_storage_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "BiometricStoragePlugin");
   biometric_storage_plugin_register_with_registrar(biometric_storage_registrar);
+  g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
+  desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
   g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
   url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
+  g_autoptr(FlPluginRegistrar) window_to_front_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin");
+  window_to_front_plugin_register_with_registrar(window_to_front_registrar);
 }
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index 5a42b12..cc58f44 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -4,7 +4,9 @@
 
 list(APPEND FLUTTER_PLUGIN_LIST
   biometric_storage
+  desktop_webview_window
   url_launcher_linux
+  window_to_front
 )
 
 list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/pubspec.lock b/pubspec.lock
index def499d..0096409 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,18 +5,18 @@ packages:
     dependency: transitive
     description:
       name: _fe_analyzer_shared
-      sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
+      sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b
       url: "https://pub.dev"
     source: hosted
-    version: "93.0.0"
+    version: "100.0.0"
   analyzer:
     dependency: transitive
     description:
       name: analyzer
-      sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
+      sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf"
       url: "https://pub.dev"
     source: hosted
-    version: "10.0.1"
+    version: "13.0.0"
   args:
     dependency: transitive
     description:
@@ -53,34 +53,34 @@ packages:
     dependency: transitive
     description:
       name: build
-      sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c
+      sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae"
       url: "https://pub.dev"
     source: hosted
-    version: "4.0.5"
+    version: "4.0.7"
   build_config:
     dependency: transitive
     description:
       name: build_config
-      sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
+      sha256: "94eaf6708fe64408c632ef2689ca3777b112f9421306ccf4f8c84d7c5c9f83f8"
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.3.2"
   build_daemon:
     dependency: transitive
     description:
       name: build_daemon
-      sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
+      sha256: "79e05eaf15a48d7230b053a4363b8eaac0cc234bbd0134c3229455481f55cbc6"
       url: "https://pub.dev"
     source: hosted
-    version: "4.1.1"
+    version: "4.1.5"
   build_runner:
     dependency: "direct dev"
     description:
       name: build_runner
-      sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e"
+      sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16"
       url: "https://pub.dev"
     source: hosted
-    version: "2.13.1"
+    version: "2.15.1"
   built_collection:
     dependency: "direct main"
     description:
@@ -93,18 +93,18 @@ packages:
     dependency: "direct main"
     description:
       name: built_value
-      sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af"
+      sha256: "31b24be6615ec7fcf70b3aa5a7469fe35826485e639a16dd7eb83ba30e4cc6a8"
       url: "https://pub.dev"
     source: hosted
-    version: "8.12.5"
+    version: "8.12.7"
   built_value_generator:
     dependency: "direct dev"
     description:
       name: built_value_generator
-      sha256: ebdc4dbc63bcdb8c63eb39569bc1da8594d998862449b8dc0e064b7b999d7c96
+      sha256: "66091f1d4c07ed76b25a2834c49766b7baafb8843d5a1cfc1308059f898bf056"
       url: "https://pub.dev"
     source: hosted
-    version: "8.12.5"
+    version: "8.12.7"
   characters:
     dependency: transitive
     description:
@@ -133,18 +133,10 @@ packages:
     dependency: transitive
     description:
       name: code_assets
-      sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
-      url: "https://pub.dev"
-    source: hosted
-    version: "1.0.0"
-  code_builder:
-    dependency: transitive
-    description:
-      name: code_builder
-      sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
+      sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
       url: "https://pub.dev"
     source: hosted
-    version: "4.11.1"
+    version: "1.2.1"
   collection:
     dependency: "direct main"
     description:
@@ -165,10 +157,10 @@ packages:
     dependency: transitive
     description:
       name: cronet_http
-      sha256: "8e77bc6f203e0bc9126e6a9092508a3435dbcb04da3b53ed1a358909385c5e0e"
+      sha256: "9da9860b409d71e4b8259e3dee631176d499dee23e7cd45a3024ebd5181997d8"
       url: "https://pub.dev"
     source: hosted
-    version: "1.8.0"
+    version: "1.9.0"
   crypto:
     dependency: "direct main"
     description:
@@ -197,10 +189,18 @@ packages:
     dependency: transitive
     description:
       name: dart_style
-      sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
+      sha256: "59d53ef8eaed9d288ed9767618e2b31c4fa0383a127db59d5eb2e737a7638a60"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.9"
+  desktop_webview_window:
+    dependency: transitive
+    description:
+      name: desktop_webview_window
+      sha256: b6fdae2cbf9571879b1761c12f27facaf82e22d0bdc74d049907c2a09a432957
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.7"
+    version: "0.3.0"
   fake_async:
     dependency: transitive
     description:
@@ -315,6 +315,22 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_auth_2:
+    dependency: transitive
+    description:
+      name: flutter_web_auth_2
+      sha256: "8f9303471dcd96670878c9b7c0c4e14c37595b2add67465f6a868f17a5872dfc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.3"
+  flutter_web_auth_2_platform_interface:
+    dependency: transitive
+    description:
+      name: flutter_web_auth_2_platform_interface
+      sha256: ba0fbba55bffb47242025f96852ad1ffba34bc451568f56ef36e613612baffab
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.0"
   flutter_web_plugins:
     dependency: transitive
     description: flutter
@@ -348,10 +364,10 @@ packages:
     dependency: transitive
     description:
       name: hooks
-      sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
+      sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
       url: "https://pub.dev"
     source: hosted
-    version: "1.0.2"
+    version: "2.0.2"
   http:
     dependency: transitive
     description:
@@ -388,10 +404,10 @@ packages:
     dependency: "direct main"
     description:
       name: intl
-      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+      sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867"
       url: "https://pub.dev"
     source: hosted
-    version: "0.20.2"
+    version: "0.20.3"
   io:
     dependency: transitive
     description:
@@ -404,18 +420,34 @@ packages:
     dependency: transitive
     description:
       name: jni
-      sha256: "8706a77e94c76fe9ec9315e18949cc9479cc03af97085ca9c1077b61323ea12d"
+      sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.3"
+  jni_flutter:
+    dependency: transitive
+    description:
+      name: jni_flutter
+      sha256: "7b717011ea40d04fd47c2731d3d1d36eb99eba3435c2753d62489e8c3c9991d5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.2"
+  jni_util:
+    dependency: transitive
+    description:
+      name: jni_util
+      sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f"
       url: "https://pub.dev"
     source: hosted
-    version: "0.15.2"
+    version: "1.0.0"
   json_annotation:
     dependency: transitive
     description:
       name: json_annotation
-      sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
+      sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80"
       url: "https://pub.dev"
     source: hosted
-    version: "4.11.0"
+    version: "4.12.0"
   leak_tracker:
     dependency: transitive
     description:
@@ -476,10 +508,10 @@ packages:
     dependency: "direct main"
     description:
       name: meta
-      sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+      sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
       url: "https://pub.dev"
     source: hosted
-    version: "1.17.0"
+    version: "1.18.0"
   mime:
     dependency: transitive
     description:
@@ -488,14 +520,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.0"
-  native_toolchain_c:
-    dependency: transitive
-    description:
-      name: native_toolchain_c
-      sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
-      url: "https://pub.dev"
-    source: hosted
-    version: "0.17.6"
   nested:
     dependency: transitive
     description:
@@ -508,10 +532,10 @@ packages:
     dependency: transitive
     description:
       name: objective_c
-      sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
+      sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e
       url: "https://pub.dev"
     source: hosted
-    version: "9.3.0"
+    version: "9.5.0"
   package_config:
     dependency: transitive
     description:
@@ -532,18 +556,18 @@ packages:
     dependency: "direct main"
     description:
       name: path_provider
-      sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
+      sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.5"
+    version: "2.1.6"
   path_provider_android:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
+      sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.23"
+    version: "2.3.1"
   path_provider_foundation:
     dependency: transitive
     description:
@@ -556,18 +580,18 @@ packages:
     dependency: transitive
     description:
       name: path_provider_linux
-      sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+      sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.2"
   path_provider_platform_interface:
     dependency: transitive
     description:
       name: path_provider_platform_interface
-      sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+      sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.3"
   path_provider_windows:
     dependency: transitive
     description:
@@ -638,6 +662,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.5.0"
+  record_use:
+    dependency: transitive
+    description:
+      name: record_use
+      sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.6.0"
   retry:
     dependency: transitive
     description:
@@ -666,10 +698,10 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
+      sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.23"
+    version: "2.4.27"
   shared_preferences_foundation:
     dependency: transitive
     description:
@@ -735,10 +767,10 @@ packages:
     dependency: transitive
     description:
       name: source_gen
-      sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd"
+      sha256: a603f1fb984a7391ae5978d1b92bfaaa08b350dca5c825256f925818f7943bf5
       url: "https://pub.dev"
     source: hosted
-    version: "4.2.2"
+    version: "4.2.4"
   source_span:
     dependency: transitive
     description:
@@ -791,10 +823,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
+      sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.10"
+    version: "0.7.11"
   typed_data:
     dependency: transitive
     description:
@@ -815,10 +847,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_android
-      sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
+      sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
       url: "https://pub.dev"
     source: hosted
-    version: "6.3.29"
+    version: "6.3.32"
   url_launcher_ios:
     dependency: transitive
     description:
@@ -855,10 +887,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_web
-      sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
+      sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.2"
+    version: "2.4.3"
   url_launcher_windows:
     dependency: transitive
     description:
@@ -879,10 +911,10 @@ packages:
     dependency: transitive
     description:
       name: vm_service
-      sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
+      sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
       url: "https://pub.dev"
     source: hosted
-    version: "15.0.2"
+    version: "15.2.0"
   watcher:
     dependency: transitive
     description:
@@ -923,6 +955,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.15.0"
+  window_to_front:
+    dependency: transitive
+    description:
+      name: window_to_front
+      sha256: "14fad8984db4415e2eeb30b04bb77140b180e260d6cb66b26de126a8657a9241"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.0.4"
   xdg_directories:
     dependency: transitive
     description:
@@ -940,5 +980,5 @@ packages:
     source: hosted
     version: "3.1.3"
 sdks:
-  dart: ">=3.10.3 <4.0.0"
-  flutter: ">=3.38.4"
+  dart: ">=3.12.0 <4.0.0"
+  flutter: ">=3.44.0"
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme
  2026-08-10 14:47   ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
                       ` (5 preceding siblings ...)
  2026-08-10 14:47     ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
@ 2026-08-10 14:47     ` Azharul Haque
  6 siblings, 0 replies; 12+ messages in thread
From: Azharul Haque @ 2026-08-10 14:47 UTC (permalink / raw)
  To: pve-devel; +Cc: haque

Follow-up to the proxmox_login_manager patch renaming the callback
scheme from pveauth to the namespaced com.proxmox.app.openid.

Signed-off-by: Azharul Haque <haque@azharul.com>
---
 android/app/src/main/AndroidManifest.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 884fedd..19a9362 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -35,7 +35,7 @@
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="pveauth" />
+                <data android:scheme="com.proxmox.app.openid" />
             </intent-filter>
         </activity>
 
-- 
2.50.1 (Apple Git-155)




^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-08-13 15:59 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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-10 14:47     ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers 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-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-10 14:47     ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-10 14:47     ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque

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.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal