]> git.proxmox.com Git - flutter/proxmox_login_manager.git/blame - lib/proxmox_tfa_form.dart
tree-wide: use super-initializer parameters where possible
[flutter/proxmox_login_manager.git] / lib / proxmox_tfa_form.dart
CommitLineData
0e468546
TM
1import 'package:flutter/material.dart';
2import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
3import 'package:proxmox_login_manager/proxmox_login_form.dart';
4
5class ProxmoxTfaForm extends StatefulWidget {
a9d1ee22 6 final ProxmoxApiClient? apiClient;
0e468546 7
39597066 8 const ProxmoxTfaForm({super.key, this.apiClient});
0e468546
TM
9
10 @override
f82ea945 11 State<ProxmoxTfaForm> createState() => _ProxmoxTfaFormState();
0e468546
TM
12}
13
14class _ProxmoxTfaFormState extends State<ProxmoxTfaForm> {
15 final TextEditingController _codeController = TextEditingController();
16 bool _isLoading = false;
44515cff
TL
17 List<String> _tfaKinds = [];
18 String _selectedTfaKind = "";
eb675700
TL
19
20 @override
21 void initState() {
22 super.initState();
44515cff
TL
23 _tfaKinds = widget.apiClient!.credentials.tfa!.kinds().toList();
24 _selectedTfaKind = _tfaKinds[0];
eb675700
TL
25 }
26
0e468546
TM
27 @override
28 Widget build(BuildContext context) {
29 return Theme(
291fc65f 30 data: ThemeData.dark().copyWith(
7c043135 31 colorScheme: const ColorScheme.dark().copyWith(
291fc65f
DC
32 secondary: ProxmoxColors.orange,
33 onSecondary: ProxmoxColors.supportGrey)),
79ed04db 34 child: Scaffold(
291fc65f 35 backgroundColor: ProxmoxColors.supportBlue,
79ed04db
TM
36 extendBodyBehindAppBar: true,
37 appBar: AppBar(
38 elevation: 0.0,
39 backgroundColor: Colors.transparent,
40 leading: IconButton(
7c043135 41 icon: const Icon(Icons.close),
79ed04db
TM
42 onPressed: () => Navigator.of(context).pop(),
43 ),
44 ),
45 body: Stack(
0e468546
TM
46 alignment: Alignment.center,
47 children: [
48 SingleChildScrollView(
49 child: ConstrainedBox(
50 constraints: BoxConstraints.tightFor(
51 height: MediaQuery.of(context).size.height),
0de76be7
TL
52 child: SafeArea(
53 child: Padding(
54 padding: const EdgeInsets.all(8.0),
55 child: Column(
56 mainAxisAlignment: MainAxisAlignment.start,
57 crossAxisAlignment: CrossAxisAlignment.center,
58 children: <Widget>[
7c043135
TL
59 const Padding(
60 padding: EdgeInsets.fromLTRB(0, 100.0, 0, 30.0),
0de76be7
TL
61 child: Icon(
62 Icons.lock,
63 size: 48,
64 ),
0e468546 65 ),
7c043135 66 const Text(
0de76be7
TL
67 'Verify',
68 style: TextStyle(
69 fontSize: 36,
70 color: Colors.white,
71 fontWeight: FontWeight.bold),
72 ),
7c043135 73 const Text(
0de76be7
TL
74 'Check your second factor provider',
75 style: TextStyle(
76 color: Colors.white38,
77 fontWeight: FontWeight.bold),
78 ),
79 Padding(
80 padding: const EdgeInsets.fromLTRB(0, 50.0, 0, 8.0),
98bcbcae 81 child: SizedBox(
0de76be7
TL
82 width: 175,
83 child: Column(
84 children: <Widget>[
85 DropdownButtonFormField(
7c043135 86 decoration: const InputDecoration(
0de76be7
TL
87 labelText: 'Method',
88 icon: Icon(Icons.input)),
44515cff 89 items: _tfaKinds
0de76be7 90 .map((e) => DropdownMenuItem(
0de76be7 91 value: e,
7824e4fc 92 child: ListTile(title: Text(e)),
0de76be7
TL
93 ))
94 .toList(),
95 onChanged: (String? value) {
96 setState(() {
44515cff 97 _selectedTfaKind = value!;
0de76be7
TL
98 });
99 },
100 selectedItemBuilder: (context) =>
44515cff
TL
101 _tfaKinds.map((e) => Text(e)).toList(),
102 value: _selectedTfaKind,
0de76be7
TL
103 ),
104 TextField(
105 controller: _codeController,
106 textAlign: TextAlign.center,
7c043135 107 decoration: const InputDecoration(
0de76be7
TL
108 labelText: 'Code',
109 icon: Icon(Icons.pin)),
44515cff 110 keyboardType: _selectedTfaKind == 'totp'
ccefe9ed
TL
111 ? TextInputType.number
112 : TextInputType.visiblePassword,
0de76be7
TL
113 autofocus: true,
114 onSubmitted: (value) => _submitTfaCode()),
115 ],
116 ),
eb675700 117 ),
0e468546 118 ),
0de76be7
TL
119 Expanded(
120 child: Align(
121 alignment: Alignment.bottomCenter,
98bcbcae 122 child: SizedBox(
0de76be7
TL
123 width: MediaQuery.of(context).size.width,
124 child: TextButton(
125 style: TextButton.styleFrom(
126 foregroundColor: Colors.white,
7c043135 127 backgroundColor: const Color(0xFFE47225),
0de76be7
TL
128 disabledBackgroundColor: Colors.grey,
129 ),
130 onPressed: () => _submitTfaCode(),
7c043135 131 child: const Text('Continue'),
fcdfb148 132 ),
0e468546
TM
133 ),
134 ),
135 ),
0de76be7
TL
136 ],
137 ),
0e468546
TM
138 ),
139 ),
140 ),
141 ),
142 if (_isLoading)
7c043135 143 const ProxmoxProgressOverlay(
aa8e7870 144 message: 'Verifying second-factor...',
0e468546
TM
145 )
146 ],
147 ),
148 ),
149 );
150 }
151
152 Future<void> _submitTfaCode() async {
153 setState(() {
154 _isLoading = true;
155 });
156 try {
eb675700 157 final client = await widget.apiClient!
44515cff 158 .finishTfaChallenge(_selectedTfaKind, _codeController.text);
10b47afc 159 if (mounted) Navigator.of(context).pop(client);
0e468546 160 } on ProxmoxApiException catch (e) {
10b47afc
TL
161 if (mounted) {
162 showDialog(
163 context: context,
164 builder: (context) => ProxmoxApiErrorDialog(
165 exception: e,
166 ),
167 );
168 }
0e468546
TM
169 } catch (e, trace) {
170 print(e);
171 print(trace);
10b47afc
TL
172 if (mounted) {
173 showDialog(
174 context: context,
175 builder: (context) => ConnectionErrorDialog(
176 exception: e,
177 ),
178 );
179 }
0e468546
TM
180 }
181 setState(() {
182 _isLoading = false;
183 });
184 }
185}