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.

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