From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 9ECDF84784 for ; Tue, 14 Dec 2021 10:08:22 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 91DD721550 for ; Tue, 14 Dec 2021 10:08:22 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 341262153B for ; Tue, 14 Dec 2021 10:08:21 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id C3C3144F64 for ; Tue, 14 Dec 2021 10:08:15 +0100 (CET) From: Wolfgang Bumiller To: pve-devel@lists.proxmox.com Date: Tue, 14 Dec 2021 10:08:14 +0100 Message-Id: <20211214090814.45225-2-w.bumiller@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211214090814.45225-1-w.bumiller@proxmox.com> References: <20211214090814.45225-1-w.bumiller@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.415 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH v2 dart-login-manager] support new TFA login flow X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Dec 2021 09:08:22 -0000 For now this just shows a simple dialog to select the TFA type, this can probably be switched to using tabs or a dropdown or something. Signed-off-by: Wolfgang Bumiller --- Changes to v1: * Support the old TFA API by checking if the tfa challenge's `oldApi` property is set. Therefore the tfaType property of the tfa form is now optional. lib/proxmox_login_form.dart | 92 +++++++++++++++++++++++++++++++++---- lib/proxmox_tfa_form.dart | 17 +++++-- 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart index 59a9f61..32f8741 100644 --- a/lib/proxmox_login_form.dart +++ b/lib/proxmox_login_form.dart @@ -398,12 +398,85 @@ class _ProxmoxLoginPageState extends State { var client = await proxclient.authenticate( '$username@$realm', password, origin, settings.sslValidation!); - if (client.credentials.tfa) { - client = await Navigator.of(context).push(MaterialPageRoute( - builder: (context) => ProxmoxTfaForm( - apiClient: client, - ), - )); + if (client.credentials.tfa != null) { + var tfa = client.credentials.tfa!; + + if (tfa.oldApi) { + client = await Navigator.of(context).push(MaterialPageRoute( + builder: (context) => ProxmoxTfaForm( + apiClient: client, + tfaType: null, + message: 'Enter your TFA code', + ), + )); + } else { + if (!tfa.totp && !tfa.yubico && tfa.recovery.isEmpty) { + return await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('TFA Error'), + content: Text('No supported TFA method available.'), + actions: [ + FlatButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Close'), + ), + ], + ), + ); + } + + final route = (await showDialog( + context: context, + builder: (BuildContext context) { + var buttons = []; + + void simpleTfa(String label, String tfaType, String message) { + buttons.add( + SimpleDialogOption( + onPressed: () => Navigator.pop( + context, + MaterialPageRoute( + builder: (context) => ProxmoxTfaForm( + apiClient: client, + tfaType: tfaType, + message: message, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ Text(label) ], + ), + ), + ); + }; + + if (tfa.totp) { + simpleTfa('TOTP', 'totp', 'Enter your TOTP code'); + } + + if (tfa.yubico) { + simpleTfa('Yubico OTP', 'yubico', 'Enter your Yubico OTP code'); + } + + if (!tfa.recovery.isEmpty) { + simpleTfa('Recovery Code', 'recovery', 'Enter a Recovery Code'); + } + + return SimpleDialog( + title: const Text("Select 2nd Factor"), + children: buttons, + ); + } + )); + + if (route == null) + return; + + client = await Navigator.of(context).push(route!); + } } final status = await client.getClusterStatus(); @@ -466,10 +539,11 @@ class _ProxmoxLoginPageState extends State { builder: (context) => ProxmoxCertificateErrorDialog(), ); } + } finally { + setState(() { + _progressModel.inProgress = false; + }); } - setState(() { - _progressModel.inProgress = false; - }); } Future?> _getAccessDomains() async { diff --git a/lib/proxmox_tfa_form.dart b/lib/proxmox_tfa_form.dart index db3cfa7..fcfb0e2 100644 --- a/lib/proxmox_tfa_form.dart +++ b/lib/proxmox_tfa_form.dart @@ -1,11 +1,19 @@ import 'package:flutter/material.dart'; import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'; +import 'package:proxmox_dart_api_client/src/tfa_challenge.dart'; import 'package:proxmox_login_manager/proxmox_login_form.dart'; class ProxmoxTfaForm extends StatefulWidget { final ProxmoxApiClient? apiClient; + final String? tfaType; + final String message; - const ProxmoxTfaForm({Key? key, this.apiClient}) : super(key: key); + const ProxmoxTfaForm({ + Key? key, + this.apiClient, + required this.tfaType, + required this.message, + }) : super(key: key); @override _ProxmoxTfaFormState createState() => _ProxmoxTfaFormState(); @@ -57,7 +65,7 @@ class _ProxmoxTfaFormState extends State { fontWeight: FontWeight.bold), ), Text( - 'Check your second factor provider', + widget.message, style: TextStyle( color: Colors.white38, fontWeight: FontWeight.bold), ), @@ -108,7 +116,10 @@ class _ProxmoxTfaFormState extends State { }); try { final client = - await widget.apiClient!.finishTfaChallenge(_codeController.text); + await widget.apiClient!.finishTfaChallenge( + widget.tfaType, + _codeController.text, + ); Navigator.of(context).pop(client); } on ProxmoxApiException catch (e) { showDialog( -- 2.30.2