LoginV2 changes
This commit is contained in:
parent
37ce8a55db
commit
a88cd75a5c
|
@ -1,12 +1,15 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/core/errors.dart';
|
import 'package:ente_auth/core/errors.dart';
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/account/recovery_page.dart';
|
import 'package:ente_auth/ui/account/recovery_page.dart';
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||||
import 'package:ente_auth/ui/home_page.dart';
|
import 'package:ente_auth/ui/home_page.dart';
|
||||||
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/email_util.dart';
|
import 'package:ente_auth/utils/email_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -26,11 +29,20 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
String? email;
|
String? email;
|
||||||
bool _passwordInFocus = false;
|
bool _passwordInFocus = false;
|
||||||
bool _passwordVisible = false;
|
bool _passwordVisible = false;
|
||||||
|
String? _volatilePassword;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
email = Configuration.instance.getEmail();
|
email = Configuration.instance.getEmail();
|
||||||
|
_volatilePassword = Configuration.instance.getVolatilePassword();
|
||||||
|
if (_volatilePassword != null) {
|
||||||
|
_passwordController.text = _volatilePassword!;
|
||||||
|
Future.delayed(
|
||||||
|
Duration.zero,
|
||||||
|
() => verifyPassword(_volatilePassword!),
|
||||||
|
);
|
||||||
|
}
|
||||||
_passwordFocusNode.addListener(() {
|
_passwordFocusNode.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_passwordInFocus = _passwordFocusNode.hasFocus;
|
_passwordInFocus = _passwordFocusNode.hasFocus;
|
||||||
|
@ -64,68 +76,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
),
|
),
|
||||||
body: _getBody(),
|
body: _getBody(),
|
||||||
floatingActionButton: DynamicFAB(
|
floatingActionButton: DynamicFAB(
|
||||||
|
key: const ValueKey("verifyPasswordButton"),
|
||||||
isKeypadOpen: isKeypadOpen,
|
isKeypadOpen: isKeypadOpen,
|
||||||
isFormValid: _passwordController.text.isNotEmpty,
|
isFormValid: _passwordController.text.isNotEmpty,
|
||||||
buttonText: context.l10n.verifyPassword,
|
buttonText: context.l10n.verifyPassword,
|
||||||
onPressedFunction: () async {
|
onPressedFunction: () async {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
|
await verifyPassword(_passwordController.text);
|
||||||
await dialog.show();
|
|
||||||
try {
|
|
||||||
await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
|
||||||
_passwordController.text,
|
|
||||||
Configuration.instance.getKeyAttributes()!,
|
|
||||||
);
|
|
||||||
} on KeyDerivationError catch (e, s) {
|
|
||||||
_logger.severe("Password verification failed", e, s);
|
|
||||||
await dialog.hide();
|
|
||||||
final dialogChoice = await showChoiceDialog(
|
|
||||||
context,
|
|
||||||
title: context.l10n.recreatePasswordTitle,
|
|
||||||
body: context.l10n.recreatePasswordBody,
|
|
||||||
firstButtonLabel: context.l10n.useRecoveryKey,
|
|
||||||
);
|
|
||||||
if (dialogChoice!.action == ButtonAction.first) {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const RecoveryPage();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} catch (e, s) {
|
|
||||||
_logger.severe("Password verification failed", e, s);
|
|
||||||
await dialog.hide();
|
|
||||||
final dialogChoice = await showChoiceDialog(
|
|
||||||
context,
|
|
||||||
title: context.l10n.incorrectPasswordTitle,
|
|
||||||
body: context.l10n.pleaseTryAgain,
|
|
||||||
firstButtonLabel: context.l10n.contactSupport,
|
|
||||||
secondButtonLabel: context.l10n.ok,
|
|
||||||
);
|
|
||||||
if (dialogChoice!.action == ButtonAction.first) {
|
|
||||||
await sendLogs(
|
|
||||||
context,
|
|
||||||
context.l10n.contactSupport,
|
|
||||||
"support@ente.io",
|
|
||||||
postShare: () {},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await dialog.hide();
|
|
||||||
unawaited(
|
|
||||||
Navigator.of(context).pushAndRemoveUntil(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const HomePage();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(route) => false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: fabLocation(),
|
floatingActionButtonLocation: fabLocation(),
|
||||||
|
@ -133,6 +90,90 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> verifyPassword(String password) async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
final dialog =
|
||||||
|
createProgressDialog(context, context.l10n.pleaseWait);
|
||||||
|
await dialog.show();
|
||||||
|
try {
|
||||||
|
final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
|
||||||
|
password,
|
||||||
|
Configuration.instance.getKeyAttributes()!,
|
||||||
|
);
|
||||||
|
_registerSRPForExistingUsers(kek).ignore();
|
||||||
|
} on KeyDerivationError catch (e, s) {
|
||||||
|
_logger.severe("Password verification failed", e, s);
|
||||||
|
await dialog.hide();
|
||||||
|
final dialogChoice = await showChoiceDialog(
|
||||||
|
context,
|
||||||
|
title: context.l10n.recreatePasswordTitle,
|
||||||
|
body: context.l10n.recreatePasswordBody,
|
||||||
|
firstButtonLabel: context.l10n.useRecoveryKey,
|
||||||
|
);
|
||||||
|
if (dialogChoice!.action == ButtonAction.first) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const RecoveryPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe("Password verification failed", e, s);
|
||||||
|
await dialog.hide();
|
||||||
|
final dialogChoice = await showChoiceDialog(
|
||||||
|
context,
|
||||||
|
title: context.l10n.incorrectPasswordTitle,
|
||||||
|
body: context.l10n.pleaseTryAgain,
|
||||||
|
firstButtonLabel: context.l10n.contactSupport,
|
||||||
|
secondButtonLabel: context.l10n.ok,
|
||||||
|
);
|
||||||
|
if (dialogChoice!.action == ButtonAction.first) {
|
||||||
|
await sendLogs(
|
||||||
|
context,
|
||||||
|
context.l10n.contactSupport,
|
||||||
|
"support@ente.io",
|
||||||
|
postShare: () {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await dialog.hide();
|
||||||
|
Configuration.instance.setVolatilePassword(null);
|
||||||
|
unawaited(
|
||||||
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const HomePage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(route) => false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _registerSRPForExistingUsers(Uint8List key) async {
|
||||||
|
bool shouldSetupSRP = false;
|
||||||
|
try {
|
||||||
|
// ignore: unused_local_variable
|
||||||
|
final attr = await UserService.instance.getSrpAttributes(email!);
|
||||||
|
} on SrpSetupNotCompleteError {
|
||||||
|
shouldSetupSRP = true;
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe("error while fetching attr", e, s);
|
||||||
|
}
|
||||||
|
if (shouldSetupSRP) {
|
||||||
|
try {
|
||||||
|
final Uint8List loginKey = await CryptoUtil.deriveLoginKey(key);
|
||||||
|
await UserService.instance.registerOrUpdateSrp(loginKey);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe("error while setting up srp for existing users", e, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _getBody() {
|
Widget _getBody() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -142,10 +183,10 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.welcomeBack,
|
context.l10n.welcomeBack,
|
||||||
style: Theme.of(context).textTheme.headline4,
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
|
@ -165,6 +206,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
key: const ValueKey("passwordInputField"),
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: context.l10n.enterYourPasswordHint,
|
hintText: context.l10n.enterYourPasswordHint,
|
||||||
|
@ -176,19 +218,19 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
),
|
),
|
||||||
suffixIcon: _passwordInFocus
|
suffixIcon: _passwordInFocus
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_passwordVisible
|
_passwordVisible
|
||||||
? Icons.visibility
|
? Icons.visibility
|
||||||
: Icons.visibility_off,
|
: Icons.visibility_off,
|
||||||
color: Theme.of(context).iconTheme.color,
|
color: Theme.of(context).iconTheme.color,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_passwordVisible = !_passwordVisible;
|
_passwordVisible = !_passwordVisible;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
|
@ -230,11 +272,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.forgotPassword,
|
context.l10n.forgotPassword,
|
||||||
style:
|
style: Theme.of(context)
|
||||||
Theme.of(context).textTheme.subtitle1!.copyWith(
|
.textTheme
|
||||||
fontSize: 14,
|
.titleMedium!
|
||||||
decoration: TextDecoration.underline,
|
.copyWith(
|
||||||
),
|
fontSize: 14,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -254,11 +298,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.changeEmail,
|
context.l10n.changeEmail,
|
||||||
style:
|
style: Theme.of(context)
|
||||||
Theme.of(context).textTheme.subtitle1!.copyWith(
|
.textTheme
|
||||||
fontSize: 14,
|
.titleMedium!
|
||||||
decoration: TextDecoration.underline,
|
.copyWith(
|
||||||
),
|
fontSize: 14,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue