Let users set their passphrase on sign up
This commit is contained in:
parent
355d87c3f2
commit
9220f2bef8
3 changed files with 209 additions and 1 deletions
1
assets/vault.svg
Normal file
1
assets/vault.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.7 KiB |
159
lib/ui/passphrase_entry_page.dart
Normal file
159
lib/ui/passphrase_entry_page.dart
Normal file
|
@ -0,0 +1,159 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:photos/user_authenticator.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class PassphraseEntryPage extends StatefulWidget {
|
||||
PassphraseEntryPage({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PassphraseEntryPageState createState() => _PassphraseEntryPageState();
|
||||
}
|
||||
|
||||
class _PassphraseEntryPageState extends State<PassphraseEntryPage> {
|
||||
final _passphraseController1 = TextEditingController(),
|
||||
_passphraseController2 = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Encryption Passphrase"),
|
||||
),
|
||||
body: _getBody(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getBody() {
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(16, 40, 16, 16),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/vault.svg",
|
||||
width: 196,
|
||||
height: 196,
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(12)),
|
||||
Text(
|
||||
"Please enter a passphrase that we can use to encrypt your data.",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(4)),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text:
|
||||
"We don't store your passphrase, so if you forget, "),
|
||||
TextSpan(
|
||||
text: "we will not be able to help you",
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
TextSpan(text: " recover your data."),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(12)),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "something you'll never forget",
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
controller: _passphraseController1,
|
||||
autofocus: false,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "something you'll never ever forget",
|
||||
contentPadding: EdgeInsets.all(20),
|
||||
),
|
||||
controller: _passphraseController2,
|
||||
autofocus: false,
|
||||
autocorrect: false,
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: RaisedButton(
|
||||
onPressed: _passphraseController1.text.isNotEmpty &&
|
||||
_passphraseController2.text.isNotEmpty
|
||||
? () {
|
||||
if (_passphraseController1.text !=
|
||||
_passphraseController2.text) {
|
||||
showErrorDialog(context, "Uhm...",
|
||||
"The passphrases you entered don't match.");
|
||||
} else {
|
||||
_showPassphraseConfirmationDialog();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
child: Text("Set Passphrase"),
|
||||
color: Theme.of(context).buttonColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPassphraseConfirmationDialog() {
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text("Confirmation"),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(children: [
|
||||
Text("The passphrase you are promising to never forget is"),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
Text(_passphraseController1.text,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 28,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
actions: [
|
||||
FlatButton(
|
||||
child: Text("Change"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
FlatButton(
|
||||
child: Text("Confirm"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
UserAuthenticator.instance
|
||||
.setPassphrase(context, _passphraseController1.text);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import 'package:photos/core/event_bus.dart';
|
|||
|
||||
import 'package:photos/events/user_authenticated_event.dart';
|
||||
import 'package:photos/ui/ott_verification_page.dart';
|
||||
import 'package:photos/ui/passphrase_entry_page.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class UserAuthenticator {
|
||||
|
@ -63,7 +64,19 @@ class UserAuthenticator {
|
|||
await dialog.hide();
|
||||
if (response != null && response.statusCode == 200) {
|
||||
_saveConfiguration(response);
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
if (Configuration.instance.getEncryptedKey() != null) {
|
||||
// TODO: Passphrase re-enter to decrypt
|
||||
Bus.instance.fire(UserAuthenticatedEvent());
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return PassphraseEntryPage();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showErrorDialog(
|
||||
context, "Oops.", "Verification failed, please try again.");
|
||||
|
@ -71,6 +84,41 @@ class UserAuthenticator {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> setPassphrase(BuildContext context, String passphrase) async {
|
||||
final dialog = createProgressDialog(context, "Please wait...");
|
||||
await dialog.show();
|
||||
await Configuration.instance.generateAndSaveKey(passphrase);
|
||||
await _dio
|
||||
.put(
|
||||
Configuration.instance.getHttpEndpoint() + "/users/encrypted-key",
|
||||
data: {
|
||||
"encryptedKey": Configuration.instance.getEncryptedKey(),
|
||||
},
|
||||
options: Options(
|
||||
headers: {
|
||||
"X-Auth-Token": Configuration.instance.getToken(),
|
||||
},
|
||||
),
|
||||
)
|
||||
.catchError((e) async {
|
||||
await dialog.hide();
|
||||
Configuration.instance.setKey(null);
|
||||
Configuration.instance.setEncryptedKey(null);
|
||||
_logger.severe(e);
|
||||
showGenericErrorDialog(context);
|
||||
}).then((response) async {
|
||||
await dialog.hide();
|
||||
if (response != null && response.statusCode == 200) {
|
||||
Bus.instance.fire(UserAuthenticatedEvent());
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
} else {
|
||||
Configuration.instance.setKey(null);
|
||||
Configuration.instance.setEncryptedKey(null);
|
||||
showGenericErrorDialog(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@deprecated
|
||||
Future<bool> login(String username, String password) {
|
||||
return _dio.post(
|
||||
|
|
Loading…
Add table
Reference in a new issue