import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '/utils/encryption/crypto_utils.dart'; import '/utils/encryption/aes_helper.dart'; import '/utils/storage/encryption_keys.dart'; import '/utils/storage/session_cookie.dart'; class LoginResponse { final String status; final String message; final String asymmetricPublicKey; final String asymmetricPrivateKey; final String userId; const LoginResponse({ required this.status, required this.message, required this.asymmetricPublicKey, required this.asymmetricPrivateKey, required this.userId, }); factory LoginResponse.fromJson(Map json) { return LoginResponse( status: json['status'], message: json['message'], asymmetricPublicKey: json['asymmetric_public_key'], asymmetricPrivateKey: json['asymmetric_private_key'], userId: json['user_id'], ); } } Future login(context, String username, String password) async { final resp = await http.post( Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/login'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode({ 'username': username, 'password': password, }), ); if (resp.statusCode != 200) { throw Exception(resp.body); } String? rawCookie = resp.headers['set-cookie']; if (rawCookie != null) { int index = rawCookie.indexOf(';'); setSessionCookie((index == -1) ? rawCookie : rawCookie.substring(0, index)); } LoginResponse response = LoginResponse.fromJson(jsonDecode(resp.body)); var rsaPrivPem = AesHelper.aesDecrypt(password, base64.decode(response.asymmetricPrivateKey)); debugPrint(rsaPrivPem); var rsaPriv = CryptoUtils.rsaPrivateKeyFromPem(rsaPrivPem); setPrivateKey(rsaPriv); final preferences = await SharedPreferences.getInstance(); preferences.setBool('islogin', true); preferences.setString('userId', response.userId); return response; } class Login extends StatelessWidget { const Login({Key? key}) : super(key: key); static const String _title = 'Envelope'; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.cyan, appBar: AppBar( title: null, automaticallyImplyLeading: true, //`true` if you want Flutter to automatically add Back Button when needed, //or `false` if you want to force your own back button every where leading: IconButton(icon: const Icon(Icons.arrow_back), onPressed:() => { Navigator.pop(context) } ) ), body: const SafeArea( child: LoginWidget(), ), ); } } class LoginWidget extends StatefulWidget { const LoginWidget({Key? key}) : super(key: key); @override State createState() => _LoginWidgetState(); } class _LoginWidgetState extends State { final _formKey = GlobalKey(); TextEditingController usernameController = TextEditingController(); TextEditingController passwordController = TextEditingController(); @override Widget build(BuildContext context) { const TextStyle _inputTextStyle = TextStyle(fontSize: 18, color: Colors.black); final ButtonStyle _buttonStyle = ElevatedButton.styleFrom( primary: Colors.white, onPrimary: Colors.cyan, minimumSize: const Size.fromHeight(50), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), textStyle: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.red, ), ); return Center( child: Form( key: _formKey, child: Center( child: Padding( padding: const EdgeInsets.all(15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text('Login', style: TextStyle(fontSize: 35, color: Colors.white),), const SizedBox(height: 30), TextFormField( controller: usernameController, decoration: const InputDecoration( hintText: 'Username', ), style: _inputTextStyle, // The validator receives the text that the user has entered. validator: (value) { if (value == null || value.isEmpty) { return 'Create a username'; } return null; }, ), const SizedBox(height: 5), TextFormField( controller: passwordController, obscureText: true, enableSuggestions: false, autocorrect: false, decoration: const InputDecoration( hintText: 'Password', ), style: _inputTextStyle, // The validator receives the text that the user has entered. validator: (value) { if (value == null || value.isEmpty) { return 'Enter a password'; } return null; }, ), const SizedBox(height: 5), ElevatedButton( style: _buttonStyle, onPressed: () { if (_formKey.currentState!.validate()) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Processing Data')), ); login( context, usernameController.text, passwordController.text, ).then((value) { Navigator. pushNamedAndRemoveUntil( context, '/home', ModalRoute.withName('/home'), ); }).catchError((error) { print(error); // TODO: Show error on interface }); } }, child: const Text('Submit'), ), ], ) ) ) ) ); } }