From 7aa820b0099735961c215a3105a0121a9df1003f Mon Sep 17 00:00:00 2001 From: chonghorizons <36284839+chonghorizons@users.noreply.github.com> Date: Sun, 29 Mar 2020 23:35:01 -0400 Subject: [PATCH] Added some buttons on the "secret data" page to delete the jwt key and reload It's instructional. Combined with the backend change, this shows what happens when there is an expired token (its not handled prettily here) And, it allows for deleting the key and going back to the login screen. --- lib/main.dart | 263 +++++++++++++++++++++++++------------------------- 1 file changed, 134 insertions(+), 129 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 92f4571..6831210 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,17 +3,17 @@ import 'package:http/http.dart' as http; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'dart:convert' show json, base64, ascii; -const SERVER_IP = 'http://192.168.1.167:5000'; +const SERVER_IP = 'http://27706f93.ngrok.io'; final storage = FlutterSecureStorage(); -void main() { +void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { Future get jwtOrEmpty async { var jwt = await storage.read(key: "jwt"); - if(jwt == null) return ""; + if (jwt == null) return ""; return jwt; } @@ -25,28 +25,29 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, ), home: FutureBuilder( - future: jwtOrEmpty, - builder: (context, snapshot) { - if(!snapshot.hasData) return CircularProgressIndicator(); - if(snapshot.data != "") { - var str = snapshot.data; - var jwt = str.split("."); + future: jwtOrEmpty, + builder: (context, snapshot) { + if (!snapshot.hasData) return CircularProgressIndicator(); + if (snapshot.data != "") { + var str = snapshot.data; + var jwt = str.split("."); - if(jwt.length !=3) { - return LoginPage(); - } else { - var payload = json.decode(ascii.decode(base64.decode(base64.normalize(jwt[1])))); - if(DateTime.fromMillisecondsSinceEpoch(payload["exp"]*1000).isAfter(DateTime.now())) { - return HomePage(str, payload); - } else { + if (jwt.length != 3) { return LoginPage(); + } else { + var payload = json.decode( + ascii.decode(base64.decode(base64.normalize(jwt[1])))); + if (DateTime.fromMillisecondsSinceEpoch(payload["exp"] * 1000) + .isAfter(DateTime.now())) { + return HomePage(str, payload); + } else { + return LoginPage(); + } } + } else { + return LoginPage(); } - } else { - return LoginPage(); - } - } - ), + }), ); } } @@ -56,139 +57,143 @@ class LoginPage extends StatelessWidget { final TextEditingController _passwordController = TextEditingController(); void displayDialog(context, title, text) => showDialog( - context: context, - builder: (context) => - AlertDialog( - title: Text(title), - content: Text(text) - ), - ); + context: context, + builder: (context) => + AlertDialog(title: Text(title), content: Text(text)), + ); Future attemptLogIn(String username, String password) async { - var res = await http.post( - "$SERVER_IP/login", - body: { - "username": username, - "password": password - } - ); - if(res.statusCode == 200) return res.body; + var res = await http.post("$SERVER_IP/login", + body: {"username": username, "password": password}); + if (res.statusCode == 200) return res.body; return null; } Future attemptSignUp(String username, String password) async { - var res = await http.post( - '$SERVER_IP/signup', - body: { - "username": username, - "password": password - } - ); + var res = await http.post('$SERVER_IP/signup', + body: {"username": username, "password": password}); return res.statusCode; - } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Log In"),), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextField( - controller: _usernameController, - decoration: InputDecoration( - labelText: 'Username' + appBar: AppBar( + title: Text("Log In"), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + TextField( + controller: _usernameController, + decoration: InputDecoration(labelText: 'Username'), ), - ), - TextField( - controller: _passwordController, - obscureText: true, - decoration: InputDecoration( - labelText: 'Password' + TextField( + controller: _passwordController, + obscureText: true, + decoration: InputDecoration(labelText: 'Password'), ), - ), - FlatButton( - onPressed: () async { - var username = _usernameController.text; - var password = _passwordController.text; - var jwt = await attemptLogIn(username, password); - if(jwt != null) { - storage.write(key: "jwt", value: jwt); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => HomePage.fromBase64(jwt) - ) - ); - } else { - displayDialog(context, "An Error Occurred", "No account was found matching that username and password"); - } - }, - child: Text("Log In") - ), - FlatButton( - onPressed: () async { - var username = _usernameController.text; - var password = _passwordController.text; + FlatButton( + onPressed: () async { + var username = _usernameController.text; + var password = _passwordController.text; + var jwt = await attemptLogIn(username, password); + if (jwt != null) { + storage.write(key: "jwt", value: jwt); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => HomePage.fromBase64(jwt))); + } else { + displayDialog(context, "An Error Occurred", + "No account was found matching that username and password"); + } + }, + child: Text("Log In")), + FlatButton( + onPressed: () async { + var username = _usernameController.text; + var password = _passwordController.text; - if(username.length < 4) - displayDialog(context, "Invalid Username", "The username should be at least 4 characters long"); - else if(password.length < 4) - displayDialog(context, "Invalid Password", "The password should be at least 4 characters long"); - else{ - var res = await attemptSignUp(username, password); - if(res == 201) - displayDialog(context, "Success", "The user was created. Log in now."); - else if(res == 409) - displayDialog(context, "That username is already registered", "Please try to sign up using another username or log in if you already have an account."); - else { - displayDialog(context, "Error", "An unknown error occurred."); - } - } - }, - child: Text("Sign Up") - ) - ], - ), - ) - ); + if (username.length < 4) + displayDialog(context, "Invalid Username", + "The username should be at least 4 characters long"); + else if (password.length < 4) + displayDialog(context, "Invalid Password", + "The password should be at least 4 characters long"); + else { + var res = await attemptSignUp(username, password); + if (res == 201) + displayDialog(context, "Success", + "The user was created. Log in now."); + else if (res == 409) + displayDialog( + context, + "That username is already registered", + "Please try to sign up using another username or log in if you already have an account."); + else { + displayDialog( + context, "Error", "An unknown error occurred."); + } + } + }, + child: Text("Sign Up")) + ], + ), + )); } } class HomePage extends StatelessWidget { HomePage(this.jwt, this.payload); - - factory HomePage.fromBase64(String jwt) => - HomePage( + + factory HomePage.fromBase64(String jwt) => HomePage( jwt, json.decode( - ascii.decode( - base64.decode(base64.normalize(jwt.split(".")[1])) - ) - ) - ); + ascii.decode(base64.decode(base64.normalize(jwt.split(".")[1]))))); final String jwt; final Map payload; @override - Widget build(BuildContext context) => - Scaffold( - appBar: AppBar(title: Text("Secret Data Screen")), - body: Center( - child: FutureBuilder( - future: http.read('$SERVER_IP/data', headers: {"Authorization": jwt}), - builder: (context, snapshot) => - snapshot.hasData ? - Column(children: [ - Text("${payload['username']}, here's the data:"), - Text(snapshot.data, style: Theme.of(context).textTheme.display1) - ],) - : - snapshot.hasError ? Text("An error occurred") : CircularProgressIndicator() + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: Text("Secret Data Screen")), + body: Center( + child: FutureBuilder( + future: + http.read('$SERVER_IP/data', headers: {"Authorization": jwt}), + builder: (context, snapshot) => snapshot.hasData + ? Column( + children: [ + Text("${payload['username']}, here's the data:"), + Text(snapshot.data, + style: Theme.of(context).textTheme.display1), + RaisedButton( + child: Text('Reload'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + HomePage.fromBase64(jwt))); + }, + ), + RaisedButton( + child: Text('Delete Key and BackToLogin'), + onPressed: () async { + await storage.deleteAll(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LoginPage())); + }, + ), + ], + ) + : snapshot.hasError + ? Text("An error occurred") + : CircularProgressIndicator()), ), - ), - ); -} \ No newline at end of file + ); +}