public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH manager/proxmox{_dart_api_client, _login_manager} 0/3] fix #4281
@ 2025-04-29 15:07 Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_dart_api_client 1/1] fix #4281: dart_api_client: Added functions for login with Open ID Alexander Abraham
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Alexander Abraham @ 2025-04-29 15:07 UTC (permalink / raw)
  To: pve-devel

This patch series fixes the login with Open ID for the mobile
Flutter app and the mobile (web) frontend for PVE manager. A user 
reported that they could not login using Open ID on the PVE mobile
(web) frontend and in the PVE Flutter app.

proxmox_dart_api_client:

Alexander Abraham (1):
  fix #4281: dart_api_client: Added functions for login with Open ID.

 lib/src/client.dart |   4 +-
 lib/src/oidc.dart   | 148 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+), 2 deletions(-)
 create mode 100644 lib/src/oidc.dart


proxmox_login_manager:

Alexander Abraham (1):
  fix #4281: login_manager:  UI changes for enabling login with Open ID

 lib/proxmox_login_form.dart | 222 +++++++++++++++++++++++++++++++-----
 1 file changed, 192 insertions(+), 30 deletions(-)


manager:

Alexander Abraham (1):
  fix #4281: manager: Enabled logging in with Open ID

 www/mobile/Login.js | 148 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 116 insertions(+), 32 deletions(-)


Summary over all repositories:
  4 files changed, 458 insertions(+), 64 deletions(-)

-- 
Generated by git-murpp 0.8.1


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


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

* [pve-devel] [PATCH proxmox_dart_api_client 1/1] fix #4281: dart_api_client: Added functions for login with Open ID.
  2025-04-29 15:07 [pve-devel] [PATCH manager/proxmox{_dart_api_client, _login_manager} 0/3] fix #4281 Alexander Abraham
@ 2025-04-29 15:07 ` Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_login_manager 1/1] fix #4281: login_manager: UI changes for enabling " Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in " Alexander Abraham
  2 siblings, 0 replies; 5+ messages in thread
From: Alexander Abraham @ 2025-04-29 15:07 UTC (permalink / raw)
  To: pve-devel

 This commit contains a module for the Dart API client for
 the PVE Flutter app focussed on logging in with Open ID.
 The module provides a function for obtaining an authorization
 URL from an Open ID provider and a function for obtaining 
 a ticket from the PVE API from the redirect URL an Open ID
 provider sends the user to once they have logged into their
 Open ID provider.

Signed-off-by: Alexander Abraham <a.abraham@proxmox.com>
---
 lib/src/client.dart |   4 +-
 lib/src/oidc.dart   | 148 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+), 2 deletions(-)
 create mode 100644 lib/src/oidc.dart

diff --git a/lib/src/client.dart b/lib/src/client.dart
index f597c28..fdf5395 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -675,10 +675,10 @@ class ProxmoxApiClient extends http.BaseClient {
 
   Future<List<PveAccessDomainModel>> getAccessDomainsList() async {
     final path = '/api2/json/access/domains';
-    final response = await _getWithValidation(path, null);
+    final response = await _getWithValidation(path, null); 
     var data = (json.decode(response.body)['data'] as List).map((f) {
       return serializers.deserializeWith(PveAccessDomainModel.serializer, f);
     });
     return data.whereType<PveAccessDomainModel>().toList();
-  }
+  } 
 }
diff --git a/lib/src/oidc.dart b/lib/src/oidc.dart
new file mode 100644
index 0000000..fb42f0b
--- /dev/null
+++ b/lib/src/oidc.dart
@@ -0,0 +1,148 @@
+import 'dart:io';
+import 'dart:convert';
+import 'package:http/io_client.dart';
+import 'package:http/http.dart' as http;
+
+/// Returns the fetched authentication
+/// or an empty string if the request
+/// fails for any reason.
+Future<String> fetchOIDCAuthUrl(
+  String realm,
+  String host,
+  String redirectUrl
+) async {
+  String result = "";
+  Map<String,String> params = {
+    "realm": realm,
+    "redirect-url":"https://$redirectUrl"
+  };
+  String urlPath = "https://$host/api2/json/access/openid/auth-url";
+  try {
+    http.Client client = IOClient(
+      HttpClient()
+        ..badCertificateCallback = (
+          (
+            X509Certificate cert, 
+            String host, 
+            int port
+          ){
+            return true;
+          }
+      )
+    );
+    http.Response resp = await client.post(
+      Uri.parse(urlPath),
+      headers: {
+        'Content-Type': 'application/json'
+      },
+      body: jsonEncode(params)
+    );
+    if (resp.statusCode == 200) {
+      result = (jsonDecode(resp.body) as Map<String,dynamic>)["data"];
+    }
+    else {
+      result = resp.body;
+    }
+  }
+  catch(e) {
+    result = e.toString();
+  }
+  return result;
+}
+
+/// Attempts to fetch the login credentials
+/// for Open ID logins from the PVE API.
+Future<String> fetchCredsWithPort(
+  String host, 
+  String port,
+  String code,
+  String state,
+)async {
+  var result = "";
+  Map<String,String> params = {
+      "code": code,
+      "redirect-url":"https://$host:$port",
+      "state": state
+    };
+    String urlPath = "https://$host:$port/api2/json/access/openid/login";
+    http.Client client = IOClient(
+      HttpClient()
+        ..badCertificateCallback = (
+          (
+            X509Certificate cert, 
+            String host, 
+            int port
+          ){
+            return true;
+          }
+      )
+    );
+    http.Response resp = await client.post(
+      Uri.parse(urlPath),
+      headers: {
+        'Content-Type': 'application/json'
+      },
+      body: jsonEncode(params)
+    );
+    if (resp.statusCode == 200) {
+      result = resp.body;
+    }
+    else {
+      result = resp.body;
+    }
+    return result;
+}
+
+/// Returns the fetched credentials
+/// inside a JSON string. An empty
+/// string is returned if the
+/// request fails for any reason.
+Future<String> fetchOIDCCredentials(
+  String code,
+  String state,
+  String redirectUrl
+) async {
+  String result = ""; 
+  try {
+    result = await fetchCredsWithPort(
+      redirectUrl, 
+      "8006", 
+      code, 
+      state
+    );
+  }
+  catch(e) {
+    try {
+      result = await fetchCredsWithPort(
+        redirectUrl, 
+        "443", 
+        code, 
+        state
+      );
+    }
+    catch(e){
+      result = e.toString();
+    }
+  }
+  return result;
+}
+
+/// Parses the URL an Open ID provider
+/// redirects to post-login and returns
+/// the parameters extracted from the URL
+/// for obtaining a ticket from the PVE
+/// API as key-value pairs.
+Map<String,String> parseUrl(String url){
+  Uri parsed = Uri.parse(url);
+  Map<String, String> params = parsed.queryParameters; 
+  String redirectUrl = parsed.host;
+  String stateCode = params["state"]!;
+  String statusCode = params["code"]!;
+  Map<String, String> result = Map();
+  result["state"] = stateCode;
+  result["code"] = statusCode;
+  result["host"] = redirectUrl;
+  return result;
+}
+
+
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


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

* [pve-devel] [PATCH proxmox_login_manager 1/1] fix #4281: login_manager: UI changes for enabling login with Open ID
  2025-04-29 15:07 [pve-devel] [PATCH manager/proxmox{_dart_api_client, _login_manager} 0/3] fix #4281 Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_dart_api_client 1/1] fix #4281: dart_api_client: Added functions for login with Open ID Alexander Abraham
@ 2025-04-29 15:07 ` Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in " Alexander Abraham
  2 siblings, 0 replies; 5+ messages in thread
From: Alexander Abraham @ 2025-04-29 15:07 UTC (permalink / raw)
  To: pve-devel

This commit adds an authorization flow for logging a user in
with Open ID in the Flutter App's UI. An authorization URL
is obtained and opened in a webview. From there, the user
types in their credentials and the login is processed and
the user is logged in to the PVE app.

Signed-off-by: Alexander Abraham <a.abraham@proxmox.com>
---
 lib/proxmox_login_form.dart | 222 +++++++++++++++++++++++++++++++-----
 1 file changed, 192 insertions(+), 30 deletions(-)

diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 735bd42..7dfba9f 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -1,6 +1,7 @@
 import 'dart:io';
 import 'dart:async';
-
+import 'dart:convert';
+import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:collection/collection.dart';
 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'
@@ -12,6 +13,12 @@ import 'package:proxmox_login_manager/proxmox_login_model.dart';
 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';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+
+typedef AuthCallBack = Future<void> Function(
+  InAppWebViewController controller, 
+  NavigationAction navAction
+);
 
 class ProxmoxProgressModel {
   int inProgress = 0;
@@ -42,9 +49,12 @@ class ProxmoxLoginForm extends StatefulWidget {
   final Function? onSavePasswordChanged;
   final bool? canSavePassword;
   final bool? passwordSaved;
+  final bool isOIDC;
+  final bool showOIDCAuth;
 
   const ProxmoxLoginForm({
     super.key,
+    required this.isOIDC,
     required this.originController,
     required this.usernameController,
     required this.passwordController,
@@ -57,6 +67,7 @@ class ProxmoxLoginForm extends StatefulWidget {
     this.onSavePasswordChanged,
     this.canSavePassword,
     this.passwordSaved,
+    required this.showOIDCAuth
   });
 
   @override
@@ -97,7 +108,8 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
             controller: widget.originController,
             enabled: false,
           ),
-          TextFormField(
+          
+          if (widget.isOIDC == false) TextFormField(
             decoration: const InputDecoration(
               icon: Icon(Icons.person),
               labelText: 'Username',
@@ -123,11 +135,11 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
                     ))
                 .toList(),
             onChanged: widget.onDomainChanged,
-            selectedItemBuilder: (context) =>
-                widget.accessDomains!.map((e) => Text(e!.realm)).toList(),
+            selectedItemBuilder: (context) => widget.accessDomains!.map((e) =>
+                Text(e!.realm)).toList(),
             value: widget.selectedDomain,
           ),
-          Stack(
+          if (widget.isOIDC == false) Stack(
             children: [
               TextFormField(
                 decoration: const InputDecoration(
@@ -150,14 +162,18 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
               Align(
                 alignment: Alignment.bottomRight,
                 child: IconButton(
-                  constraints: BoxConstraints.tight(const Size(58, 58)),
+                  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;
-                  }),
+                    Icon(_obscure ? Icons.visibility : Icons.visibility_off),
+                    onPressed: () => setState(
+                      () {
+                       
+                        _obscure = !_obscure;
+                      }
+                    ),
                 ),
               )
             ],
@@ -169,12 +185,12 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
               onChanged: (value) {
                 if (widget.onSavePasswordChanged != null) {
                   widget.onSavePasswordChanged!(value!);
-                }
-                setState(() {
-                  _savePwCheckbox = value!;
-                });
-              },
-            )
+              }
+              setState(() {
+                _savePwCheckbox = value!;
+              });
+            },
+          )
         ],
       ),
     );
@@ -215,6 +231,11 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
   bool _submittButtonEnabled = true;
   bool _canSavePassword = false;
   bool _savePasswordCB = false;
+  bool isOIDC = false;
+  bool showOIDCAuth = false;
+  late String oidcUserName;
+  late String oidcTicket;
+  late String oidcCRSF;
 
   @override
   void initState() {
@@ -327,6 +348,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                     child: FutureBuilder<List<PveAccessDomainModel?>?>(
                         future: _accessDomains,
                         builder: (context, snapshot) {
+                          
                           return Form(
                             key: _formKey,
                             onChanged: () {
@@ -338,7 +360,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                             child: Column(
                               mainAxisAlignment: MainAxisAlignment.center,
                               children: [
-                                Expanded(
+                                  Expanded(
                                   child: Column(
                                     mainAxisAlignment: MainAxisAlignment.center,
                                     children: [
@@ -350,6 +372,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                   ),
                                 ),
                                 ProxmoxLoginForm(
+                                  showOIDCAuth: showOIDCAuth,
                                   originController: _originController,
                                   originValidator: (value) {
                                     if (value == null || value.isEmpty) {
@@ -364,6 +387,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                       return 'Invalid URI: $e';
                                     }
                                   },
+                                  isOIDC: isOIDC,
                                   usernameController: _usernameController,
                                   passwordController: _passwordController,
                                   accessDomains: snapshot.data,
@@ -376,6 +400,16 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                   onDomainChanged: (value) {
                                     setState(() {
                                       _selectedDomain = value;
+                                      if (_selectedDomain!.comment.toString() == "null"){
+                                        setState((){
+                                          isOIDC = true;
+                                          _submittButtonEnabled = true;
+                                          _canSavePassword = false;
+                                        });
+                                      }
+                                      else {
+                                        setState(() => isOIDC = false);
+                                       }
                                     });
                                   },
                                   onOriginSubmitted: () {
@@ -392,6 +426,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                   },
                                   onPasswordSubmitted: _submittButtonEnabled
                                       ? () {
+                                          
                                           final isValid =
                                               _formKey.currentState!.validate();
                                           setState(() {
@@ -411,6 +446,46 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                       child: TextButton(
                                         onPressed: _submittButtonEnabled
                                             ? () {
+                                                if (isOIDC) {
+                                                  Navigator.push(
+                                                   context,
+                                                    MaterialPageRoute(
+                                                      builder: (context) => 
+                                                        OIDCAuthWidget(
+                                                          realm:_selectedDomain!.realm,
+                                                          redirectUrl: _originController.text, 
+                                                          host:_originController.text,
+                                                          authHandler: (controller,navAction) async {
+                                                            Map<String,
+                                                            String> creds = parseUrl(
+                                                              navAction
+                                                                .request
+                                                                .url
+                                                                .toString()
+                                                            );
+                                                            
+                                                            String pveAuth = await fetchOIDCCredentials(
+                                                              creds["code"]!, 
+                                                              creds["state"]!, 
+                                                              creds["host"]!
+                                                            );
+                                                            Map<String,dynamic> serverCreds = jsonDecode(
+                                                              pveAuth
+                                                            )["data"]!;
+                                                            String username = serverCreds["username"]!
+                                                              .split("@")[0];
+                                                            String ticket = serverCreds["ticket"]!;
+                                                            setState((){
+                                                              _usernameController.text = username;
+                                                              _passwordController.text = ticket;
+                                                            });
+                                                            _onLoginButtonPressed();
+                                                          }
+                                                        )
+                                                      )
+                                                  );
+                                                }
+                                                else {
                                                 final isValid = _formKey
                                                     .currentState!
                                                     .validate();
@@ -428,17 +503,19 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                                     });
                                                   }
                                                 }
-                                              }
+                                              }}
                                             : null,
                                         child: const Text('Continue'),
                                       ),
                                     ),
-                                  ),
-                                ),
+                                  ),                                
+                                ), 
                               ],
                             ),
                           );
-                        }),
+                         
+                        }
+                    ),
                   ),
                 ),
               ),
@@ -460,19 +537,15 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     });
 
     try {
-      final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
-      //cleaned form fields
       final origin = normalizeUrl(_originController.text.trim());
-      final username = _usernameController.text.trim();
       final String enteredPassword = _passwordController.text.trim();
       final String? savedPassword = widget.password;
-
+      final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
+      final username = _usernameController.text.trim();
       final password = ticket.isNotEmpty ? ticket : enteredPassword;
       final realm = _selectedDomain?.realm ?? mRealm;
-
       var client = await proxclient.authenticate(
-          '$username@$realm', password, origin, settings.sslValidation!);
-
+            '$username@$realm', password, origin, settings.sslValidation!);
       if (client.credentials.tfa != null &&
           client.credentials.tfa!.kinds().isNotEmpty) {
         if (!mounted) return;
@@ -566,7 +639,6 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
         Navigator.of(context).pop(client);
       }
     } on proxclient.ProxmoxApiException catch (e) {
-      print(e);
       if (!mounted) return;
       if (e.message.contains('No ticket')) {
         showDialog(
@@ -703,8 +775,12 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
 
     setState(() {
       _progressModel.inProgress -= 1;
+      if(response![0]!.comment == null){
+        isOIDC = true;
+      }
+      else {}
       _selectedDomain = selection;
-    });
+    }); 
 
     return response;
   }
@@ -847,3 +923,89 @@ Uri normalizeUrl(String urlText) {
 
   return Uri.https(urlText, '');
 }
+
+
+class OIDCAuthWidget extends StatefulWidget {
+  final String host;
+  final String realm;
+  final String redirectUrl;
+  final AuthCallBack authHandler;
+
+  OIDCAuthWidget(
+    {
+      required this.host,
+      required this.realm,
+      required this.authHandler,
+      required this.redirectUrl
+    }
+  );
+
+  OIDCAuthWidgetState createState() => OIDCAuthWidgetState();
+}
+class OIDCAuthWidgetState extends State<OIDCAuthWidget>{
+  final GlobalKey webViewKey = GlobalKey();
+  late InAppWebViewController webController;
+  late Future<String> authUrl;
+  InAppWebViewSettings settings = InAppWebViewSettings(
+    useShouldOverrideUrlLoading: true
+  );
+
+  void initState(){
+    super.initState();
+    authUrl = fetchOIDCAuthUrl(
+      widget.realm,
+      widget.host,
+      widget.redirectUrl
+    );
+  }
+
+  Widget build(BuildContext context){
+    return FutureBuilder<String>(
+      future: this.authUrl,
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.done){
+            String data = snapshot.data!;
+            if (data == ""){
+              return Scaffold(
+                body: Center(
+                  child: Text(
+                    "Data could not be loaded."
+                  )
+                )
+              );
+            }
+            else {
+              String fetchedUrl = snapshot.data!; 
+              return Scaffold(body:InAppWebView(
+                key: webViewKey,
+                initialUrlRequest: URLRequest(
+                  url: WebUri(
+                    fetchedUrl
+                  )
+                ),
+                initialSettings: settings,
+                onWebViewCreated: (controller){
+                  webController = controller;
+                },                 
+                shouldOverrideUrlLoading: (controller, navAction) async{
+                  await widget.authHandler(controller, navAction);
+                  Navigator.pop(context);
+                }
+              ));
+            }
+        }
+        return Scaffold(
+          body: Center(
+            child: Text(
+              "Loading..",
+              style: TextStyle(
+                fontSize: (MediaQuery.of(context).size.width/100.0)*5,
+                fontWeight: FontWeight.bold,
+              )
+            )
+          )
+        ); 
+      } 
+    );
+  }
+}
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


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

* [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in with Open ID
  2025-04-29 15:07 [pve-devel] [PATCH manager/proxmox{_dart_api_client, _login_manager} 0/3] fix #4281 Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_dart_api_client 1/1] fix #4281: dart_api_client: Added functions for login with Open ID Alexander Abraham
  2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_login_manager 1/1] fix #4281: login_manager: UI changes for enabling " Alexander Abraham
@ 2025-04-29 15:07 ` Alexander Abraham
  2025-04-30  8:32   ` Christoph Heiss
  2 siblings, 1 reply; 5+ messages in thread
From: Alexander Abraham @ 2025-04-29 15:07 UTC (permalink / raw)
  To: pve-devel

This commit adds an authorization flow for logging
in a user using Open ID on the PVE web frontend in
the mobile view.

Signed-off-by: Alexander Abraham <a.abraham@proxmox.com>
---
 www/mobile/Login.js | 148 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 116 insertions(+), 32 deletions(-)

diff --git a/www/mobile/Login.js b/www/mobile/Login.js
index 06c8b3d4..05038036 100644
--- a/www/mobile/Login.js
+++ b/www/mobile/Login.js
@@ -1,8 +1,10 @@
+/ https://git.proxmox.com/?p=pve-manager.git;a=blob;f=www/manager6/Workspace.js;h=922e01df86718e8fc89ce5835f81d21f6d849b81;hb=HEAD
+
 Ext.define('PVE.Login', {
     extend: 'Ext.form.Panel',
     alias: "widget.pveLogin",
-
-    handleTFA: function(username, ticketResponse) {
+    id: 'loginFormPanel',
+       handleTFA: function(username, ticketResponse) {
 	let me = this;
 	let errlabel = me.down('#signInFailedLabel');
 
@@ -32,8 +34,7 @@ Ext.define('PVE.Login', {
 		    } else {
 			me.mask({
 			    xtype: 'loadmask',
-			    message: 'Loading...',
-			});
+			    message: 'Loading...', });
 			Proxmox.Utils.API2Request({
 			    url: '/api2/extjs/access/ticket',
 			    params: {
@@ -57,16 +58,16 @@ Ext.define('PVE.Login', {
 				Proxmox.Utils.authClear();
 				errlabel.show();
 			    },
-			});
+			})
 		    }
 		},
 	    });
 	}
     },
-
     config: {
 	title: 'Login',
 	padding: 10,
+        itemId: 'loginForm',
 	appUrl: 'login',
 	items: [
 	    {
@@ -78,7 +79,7 @@ Ext.define('PVE.Login', {
 	    {
 	        xtype: 'fieldset',
 	        title: 'Proxmox VE Login',
-	        items: [
+	        items:[
 	            {
 	                xtype: 'textfield',
 	                placeHolder: gettext('User name'),
@@ -97,8 +98,19 @@ Ext.define('PVE.Login', {
 			xtype: 'pveRealmSelector',
 	                itemId: 'realmSelectorField',
 			name: 'realm',
+                        listeners: {
+                          change: function(field, newValue){
+                            let record = field.record;
+                            let realmType = record.data.type;
+                            let currForm = this.up("formpanel");
+                            let usernameField = currForm.down("#userNameTextField");
+                            let passwordField = currForm.down("#passwordTextField");
+                            usernameField.setHidden(realmType === "openid");
+                            passwordField.setHidden(realmType === "openid");
+                          }                        
+                        }
 		    },
-	        ],
+	          ],
 	    },
 	    {
 	        xtype: 'label',
@@ -124,35 +136,107 @@ Ext.define('PVE.Login', {
 
 		    errlabel.hide();
 
+                    
 		    var username = usernameField.getValue();
 	            var password = passwordField.getValue();
-	            var realm = realmField.getValue();
+ 	            var realm = realmField.getValue();
 
-		    Proxmox.Utils.API2Request({
-			url: '/access/ticket',
-			method: 'POST',
-			waitMsgTarget: form,
-			params: { username: username, password: password, realm: realm },
-			failure: function(response, options) {
-			    errlabel.show();
-			},
-			success: function(response, options) {
-			    passwordField.setValue('');
 
-			    let data = response.result.data;
-			    if (Ext.isDefined(data.NeedTFA)) {
-				form.handleTFA(username, data);
-			    } else {
-				PVE.Workspace.updateLoginData(data);
-			    }
-			},
-		    });
-		},
-	    },
-	],
-    },
-});
+                    if (realmField.record.data.type === "openid"){
+                      const redirectUrl = location.origin;
+                      const realmName = realmField.record.data.realm;
+                      Proxmox.Utils.API2Request(
+                        {
+                          url: '/access/openid/auth-url',
+                          method: 'POST',
+                          waitMsgTarget: form,
+                          params: {
+                            realm: realmName,
+                            "redirect-url": redirectUrl
+                          },
+                          success: (resp, opts) => {
+                            window.location = resp.result.data;
+                          },
+                          failure: (resp, opts) => {
+                            Proxmox.Utils.authClear();
+                            form.unmask();
+                            Ext.Msg.alert(
+                               gettext('Error'),
+                               gettext('OpenID redirect failed.') + `<br>${resp.htmlStatus}`,
+                            );
+                          }
+                        }
+                      );
+                      return;
+                    }
+                    else {
+		      Proxmox.Utils.API2Request({
+			  url: '/access/ticket',
+			  method: 'POST',
+			  waitMsgTarget: form,
+			  params: { username: username, password: password, realm: realm },
+			  failure: function(response, options) {
+			      errlabel.show();
+			  },
+			  success: function(response, options) {
+			      passwordField.setValue('');
 
+			      let data = response.result.data;
+			      if (Ext.isDefined(data.NeedTFA)) {
+			  	  form.handleTFA(username, data);
+			      } else {
+				  PVE.Workspace.updateLoginData(data);
+			      }
+                           },
+                      });
+                    }
+                 },
+              },
+          ],
+      },
+      show: function(){
+      let auth = Proxmox.Utils.getOpenIDRedirectionAuthorization();
+      let lForm = this.callParent();
+      if (auth !== undefined){
+        let state = auth.state;
+        let code = auth.code;
+        Proxmox.Utils.authClear()
+        lForm.setMasked(
+          {
+            xtype: 'loadmask',
+            message: gettext('OpenID login - please wait..')
+          }
+        );
+        const redirectUrl = location.origin;
+        Proxmox.Utils.API2Request({
+          url: '/access/openid/login',
+          params: {
+            "state": auth.state,
+            "code": auth.code,
+            "redirect-url": redirectUrl,
+          },
+         method: 'POST',
+          failure: function(response) {
+            lForm.unmask();
+            let error = response.htmlStatus;
+            Ext.Msg.alert(
+              gettext('Error'),
+              gettext('OpenID login failed, please try again') + `<br>${error}`,
+              () => { window.location = redirectUrl; },
+            );
+          },
+          success: function(response, options) {
+            lForm.unmask();
+            let data = response.result.data;
+            history.replaceState(null, '', redirectUrl);
+            lForm.success(data)
+            PVE.Workspace.updateLoginData(data);
+          },
+        });
+      }
+    }
+}); 
+   
 Ext.define('PVE.field.TFACode', {
     extend: 'Ext.field.Text',
     xtype: 'tfacode',
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


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

* Re: [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in with Open ID
  2025-04-29 15:07 ` [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in " Alexander Abraham
@ 2025-04-30  8:32   ` Christoph Heiss
  0 siblings, 0 replies; 5+ messages in thread
From: Christoph Heiss @ 2025-04-30  8:32 UTC (permalink / raw)
  To: Alexander Abraham; +Cc: Proxmox VE development discussion

The commit message title should always be in present tense (handy
shortcut to remember: the commit/patch will *change* it, but it hasn't
been changed *before* the commit/patch).

Applies to the other patches in the series as well - see also our
developer documentation on that matter [0].

On Tue Apr 29, 2025 at 5:07 PM CEST, Alexander Abraham wrote:
> This commit adds an authorization flow for logging
> in a user using Open ID on the PVE web frontend in
> the mobile view.

"This commit [..]" is redundant, since the context is already clear :^)

Didn't really review the patch further, so if there's some other
important detail, it should be mentioned as well.

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

>
> Signed-off-by: Alexander Abraham <a.abraham@proxmox.com>
> ---
>  www/mobile/Login.js | 148 ++++++++++++++++++++++++++++++++++----------
>  1 file changed, 116 insertions(+), 32 deletions(-)
>
> diff --git a/www/mobile/Login.js b/www/mobile/Login.js
> index 06c8b3d4..05038036 100644
> --- a/www/mobile/Login.js
> +++ b/www/mobile/Login.js
> @@ -1,8 +1,10 @@
> +/ https://git.proxmox.com/?p=pve-manager.git;a=blob;f=www/manager6/Workspace.js;h=922e01df86718e8fc89ce5835f81d21f6d849b81;hb=HEAD
> +

Unintentional copy-paste I assume?



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


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

end of thread, other threads:[~2025-04-30  8:32 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-04-29 15:07 [pve-devel] [PATCH manager/proxmox{_dart_api_client, _login_manager} 0/3] fix #4281 Alexander Abraham
2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_dart_api_client 1/1] fix #4281: dart_api_client: Added functions for login with Open ID Alexander Abraham
2025-04-29 15:07 ` [pve-devel] [PATCH proxmox_login_manager 1/1] fix #4281: login_manager: UI changes for enabling " Alexander Abraham
2025-04-29 15:07 ` [pve-devel] [PATCH manager 1/1] fix #4281: manager: Enabled logging in " Alexander Abraham
2025-04-30  8:32   ` Christoph Heiss

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal