all lists on 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 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