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.

223 lines
6.7 KiB

  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:http/http.dart' as http;
  4. import '/components/flash_message.dart';
  5. import '/models/my_profile.dart';
  6. import '/utils/storage/session_cookie.dart';
  7. class LoginResponse {
  8. final String userId;
  9. final String username;
  10. final String publicKey;
  11. final String privateKey;
  12. final String symmetricKey;
  13. final String? imageLink;
  14. const LoginResponse({
  15. required this.publicKey,
  16. required this.privateKey,
  17. required this.symmetricKey,
  18. required this.userId,
  19. required this.username,
  20. this.imageLink,
  21. });
  22. factory LoginResponse.fromJson(Map<String, dynamic> json) {
  23. return LoginResponse(
  24. userId: json['user_id'],
  25. username: json['username'],
  26. publicKey: json['asymmetric_public_key'],
  27. privateKey: json['asymmetric_private_key'],
  28. symmetricKey: json['symmetric_key'],
  29. imageLink: json['image_link'],
  30. );
  31. }
  32. }
  33. class Login extends StatelessWidget {
  34. const Login({Key? key}) : super(key: key);
  35. @override
  36. Widget build(BuildContext context) {
  37. return Scaffold(
  38. appBar: AppBar(
  39. title: null,
  40. automaticallyImplyLeading: true,
  41. leading: IconButton(icon: const Icon(Icons.arrow_back),
  42. onPressed:() => {
  43. Navigator.pop(context)
  44. }
  45. ),
  46. backgroundColor: Colors.transparent,
  47. shadowColor: Colors.transparent,
  48. ),
  49. body: const SafeArea(
  50. child: LoginWidget(),
  51. ),
  52. );
  53. }
  54. }
  55. class LoginWidget extends StatefulWidget {
  56. const LoginWidget({Key? key}) : super(key: key);
  57. @override
  58. State<LoginWidget> createState() => _LoginWidgetState();
  59. }
  60. class _LoginWidgetState extends State<LoginWidget> {
  61. final _formKey = GlobalKey<FormState>();
  62. final TextEditingController _usernameController = TextEditingController();
  63. final TextEditingController _passwordController = TextEditingController();
  64. @override
  65. Widget build(BuildContext context) {
  66. const TextStyle inputTextStyle = TextStyle(
  67. fontSize: 18,
  68. );
  69. final OutlineInputBorder inputBorderStyle = OutlineInputBorder(
  70. borderRadius: BorderRadius.circular(5),
  71. borderSide: const BorderSide(
  72. color: Colors.transparent,
  73. )
  74. );
  75. final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
  76. primary: Theme.of(context).colorScheme.surface,
  77. onPrimary: Theme.of(context).colorScheme.onSurface,
  78. minimumSize: const Size.fromHeight(50),
  79. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
  80. textStyle: TextStyle(
  81. fontSize: 20,
  82. fontWeight: FontWeight.bold,
  83. color: Theme.of(context).colorScheme.error,
  84. ),
  85. );
  86. return Center(
  87. child: Form(
  88. key: _formKey,
  89. child: Center(
  90. child: Padding(
  91. padding: const EdgeInsets.only(
  92. left: 20,
  93. right: 20,
  94. top: 0,
  95. bottom: 80,
  96. ),
  97. child: Column(
  98. mainAxisAlignment: MainAxisAlignment.center,
  99. crossAxisAlignment: CrossAxisAlignment.center,
  100. children: [
  101. Text(
  102. 'Login',
  103. style: TextStyle(
  104. fontSize: 35,
  105. color: Theme.of(context).colorScheme.onBackground,
  106. ),
  107. ),
  108. const SizedBox(height: 30),
  109. TextFormField(
  110. controller: _usernameController,
  111. decoration: InputDecoration(
  112. hintText: 'Username',
  113. enabledBorder: inputBorderStyle,
  114. focusedBorder: inputBorderStyle,
  115. ),
  116. style: inputTextStyle,
  117. // The validator receives the text that the user has entered.
  118. validator: (value) {
  119. if (value == null || value.isEmpty) {
  120. return 'Enter a username';
  121. }
  122. return null;
  123. },
  124. ),
  125. const SizedBox(height: 10),
  126. TextFormField(
  127. controller: _passwordController,
  128. obscureText: true,
  129. enableSuggestions: false,
  130. autocorrect: false,
  131. decoration: InputDecoration(
  132. hintText: 'Password',
  133. enabledBorder: inputBorderStyle,
  134. focusedBorder: inputBorderStyle,
  135. ),
  136. style: inputTextStyle,
  137. // The validator receives the text that the user has entered.
  138. validator: (value) {
  139. if (value == null || value.isEmpty) {
  140. return 'Enter a password';
  141. }
  142. return null;
  143. },
  144. ),
  145. const SizedBox(height: 15),
  146. ElevatedButton(
  147. style: buttonStyle,
  148. onPressed: () {
  149. if (_formKey.currentState!.validate()) {
  150. ScaffoldMessenger.of(context).showSnackBar(
  151. const SnackBar(content: Text('Processing Data')),
  152. );
  153. login()
  154. .then((val) {
  155. Navigator.
  156. pushNamedAndRemoveUntil(
  157. context,
  158. '/home',
  159. ModalRoute.withName('/home'),
  160. );
  161. }).catchError((error) {
  162. print(error);
  163. showMessage(
  164. 'Could not login to Envelope, please try again later.',
  165. context,
  166. );
  167. });
  168. }
  169. },
  170. child: const Text('Submit'),
  171. ),
  172. ],
  173. )
  174. )
  175. )
  176. )
  177. );
  178. }
  179. Future<dynamic> login() async {
  180. final resp = await http.post(
  181. await MyProfile.getServerUrl('api/v1/login'),
  182. headers: <String, String>{
  183. 'Content-Type': 'application/json; charset=UTF-8',
  184. },
  185. body: jsonEncode(<String, String>{
  186. 'username': _usernameController.text,
  187. 'password': _passwordController.text,
  188. }),
  189. );
  190. if (resp.statusCode != 200) {
  191. throw Exception(resp.body);
  192. }
  193. String? rawCookie = resp.headers['set-cookie'];
  194. if (rawCookie != null) {
  195. int index = rawCookie.indexOf(';');
  196. setSessionCookie((index == -1) ? rawCookie : rawCookie.substring(0, index));
  197. }
  198. return await MyProfile.login(
  199. json.decode(resp.body),
  200. _passwordController.text,
  201. );
  202. }
  203. }