Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
9.9 KiB

  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:http/http.dart' as http;
  4. import 'package:shared_preferences/shared_preferences.dart';
  5. import 'package:flutter_dotenv/flutter_dotenv.dart';
  6. import '/utils/encryption/crypto_utils.dart';
  7. import '/utils/encryption/aes_helper.dart';
  8. import '/utils/storage/encryption_keys.dart';
  9. import '/utils/storage/session_cookie.dart';
  10. class LoginResponse {
  11. final String status;
  12. final String message;
  13. final String asymmetricPublicKey;
  14. final String asymmetricPrivateKey;
  15. const LoginResponse({
  16. required this.status,
  17. required this.message,
  18. required this.asymmetricPublicKey,
  19. required this.asymmetricPrivateKey,
  20. });
  21. factory LoginResponse.fromJson(Map<String, dynamic> json) {
  22. return LoginResponse(
  23. status: json['status'],
  24. message: json['message'],
  25. asymmetricPublicKey: json['asymmetric_public_key'],
  26. asymmetricPrivateKey: json['asymmetric_private_key'],
  27. );
  28. }
  29. }
  30. Future<LoginResponse> login(context, String username, String password) async {
  31. final resp = await http.post(
  32. Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/login'),
  33. headers: <String, String>{
  34. 'Content-Type': 'application/json; charset=UTF-8',
  35. },
  36. body: jsonEncode(<String, String>{
  37. 'username': username,
  38. 'password': password,
  39. }),
  40. );
  41. if (resp.statusCode != 200) {
  42. throw Exception(resp.body);
  43. }
  44. String? rawCookie = resp.headers['set-cookie'];
  45. if (rawCookie != null) {
  46. int index = rawCookie.indexOf(';');
  47. setSessionCookie((index == -1) ? rawCookie : rawCookie.substring(0, index));
  48. }
  49. LoginResponse response = LoginResponse.fromJson(jsonDecode(resp.body));
  50. var rsaPrivPem = AesHelper.aesDecrypt(password, base64.decode(response.asymmetricPrivateKey));
  51. debugPrint(rsaPrivPem);
  52. var rsaPriv = CryptoUtils.rsaPrivateKeyFromPem(rsaPrivPem);
  53. setPrivateKey(rsaPriv);
  54. final preferences = await SharedPreferences.getInstance();
  55. preferences.setBool('islogin', true);
  56. return response;
  57. }
  58. class Login extends StatelessWidget {
  59. const Login({Key? key}) : super(key: key);
  60. static const String _title = 'Envelope';
  61. @override
  62. Widget build(BuildContext context) {
  63. return Scaffold(
  64. backgroundColor: Colors.cyan,
  65. appBar: AppBar(
  66. title: null,
  67. automaticallyImplyLeading: true,
  68. //`true` if you want Flutter to automatically add Back Button when needed,
  69. //or `false` if you want to force your own back button every where
  70. leading: IconButton(icon: const Icon(Icons.arrow_back),
  71. onPressed:() => {
  72. Navigator.pop(context)
  73. }
  74. )
  75. ),
  76. body: const SafeArea(
  77. child: LoginWidget(),
  78. ),
  79. );
  80. }
  81. }
  82. class LoginWidget extends StatefulWidget {
  83. const LoginWidget({Key? key}) : super(key: key);
  84. @override
  85. State<LoginWidget> createState() => _LoginWidgetState();
  86. }
  87. class _LoginWidgetState extends State<LoginWidget> {
  88. final _formKey = GlobalKey<FormState>();
  89. TextEditingController usernameController = TextEditingController();
  90. TextEditingController passwordController = TextEditingController();
  91. @override
  92. Widget build(BuildContext context) {
  93. const TextStyle _inputTextStyle = TextStyle(fontSize: 18, color: Colors.black);
  94. final ButtonStyle _buttonStyle = ElevatedButton.styleFrom(
  95. primary: Colors.white,
  96. onPrimary: Colors.cyan,
  97. minimumSize: const Size.fromHeight(50),
  98. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
  99. textStyle: const TextStyle(
  100. fontSize: 20,
  101. fontWeight: FontWeight.bold,
  102. color: Colors.red,
  103. ),
  104. );
  105. return Center(
  106. child: Form(
  107. key: _formKey,
  108. child: Center(
  109. child: Padding(
  110. padding: const EdgeInsets.all(15),
  111. child: Column(
  112. mainAxisAlignment: MainAxisAlignment.center,
  113. crossAxisAlignment: CrossAxisAlignment.center,
  114. children: [
  115. const Text('Login', style: TextStyle(fontSize: 35, color: Colors.white),),
  116. const SizedBox(height: 30),
  117. TextFormField(
  118. controller: usernameController,
  119. decoration: const InputDecoration(
  120. hintText: 'Username',
  121. ),
  122. style: _inputTextStyle,
  123. // The validator receives the text that the user has entered.
  124. validator: (value) {
  125. if (value == null || value.isEmpty) {
  126. return 'Create a username';
  127. }
  128. return null;
  129. },
  130. ),
  131. const SizedBox(height: 5),
  132. TextFormField(
  133. controller: passwordController,
  134. obscureText: true,
  135. enableSuggestions: false,
  136. autocorrect: false,
  137. decoration: const InputDecoration(
  138. hintText: 'Password',
  139. ),
  140. style: _inputTextStyle,
  141. // The validator receives the text that the user has entered.
  142. validator: (value) {
  143. if (value == null || value.isEmpty) {
  144. return 'Enter a password';
  145. }
  146. return null;
  147. },
  148. ),
  149. const SizedBox(height: 5),
  150. ElevatedButton(
  151. style: _buttonStyle,
  152. onPressed: () {
  153. if (_formKey.currentState!.validate()) {
  154. ScaffoldMessenger.of(context).showSnackBar(
  155. const SnackBar(content: Text('Processing Data')),
  156. );
  157. login(
  158. context,
  159. usernameController.text,
  160. passwordController.text,
  161. ).then((value) {
  162. Navigator.
  163. pushNamedAndRemoveUntil(
  164. context,
  165. '/home',
  166. ModalRoute.withName('/home'),
  167. );
  168. }).catchError((error) {
  169. print(error); // TODO: Show error on interface
  170. });
  171. }
  172. },
  173. child: const Text('Submit'),
  174. ),
  175. ],
  176. )
  177. )
  178. )
  179. )
  180. );
  181. }
  182. }