Change Password: Confirm before signing out from other devices (#374)
This commit is contained in:
parent
cf6b4f5423
commit
17f5a7996a
3 changed files with 55 additions and 20 deletions
|
@ -398,5 +398,9 @@
|
||||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||||
},
|
},
|
||||||
"noInternetConnection": "No internet connection",
|
"noInternetConnection": "No internet connection",
|
||||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again."
|
"pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again.",
|
||||||
|
"signOutFromOtherDevices": "Sign out from other devices",
|
||||||
|
"signOutOtherBody": "If you think someone might know your password, you can force all other devices using your account to sign out.",
|
||||||
|
"signOutOtherDevices": "Sign out other devices",
|
||||||
|
"doNotSignOut": "Do not sign out"
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,6 +441,7 @@ class UserService {
|
||||||
Future<void> registerOrUpdateSrp(
|
Future<void> registerOrUpdateSrp(
|
||||||
Uint8List loginKey, {
|
Uint8List loginKey, {
|
||||||
SetKeysRequest? setKeysRequest,
|
SetKeysRequest? setKeysRequest,
|
||||||
|
bool logOutOtherDevices = false,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final String username = const Uuid().v4().toString();
|
final String username = const Uuid().v4().toString();
|
||||||
|
@ -496,6 +497,7 @@ class UserService {
|
||||||
'setupID': setupSRPResponse.setupID,
|
'setupID': setupSRPResponse.setupID,
|
||||||
'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)),
|
'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)),
|
||||||
'updatedKeyAttr': setKeysRequest.toMap(),
|
'updatedKeyAttr': setKeysRequest.toMap(),
|
||||||
|
'logOutOtherDevices': logOutOtherDevices,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -608,8 +610,9 @@ class UserService {
|
||||||
|
|
||||||
Future<void> updateKeyAttributes(
|
Future<void> updateKeyAttributes(
|
||||||
KeyAttributes keyAttributes,
|
KeyAttributes keyAttributes,
|
||||||
Uint8List loginKey,
|
Uint8List loginKey, {
|
||||||
) async {
|
required bool logoutOtherDevices,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final setKeyRequest = SetKeysRequest(
|
final setKeyRequest = SetKeysRequest(
|
||||||
kekSalt: keyAttributes.kekSalt,
|
kekSalt: keyAttributes.kekSalt,
|
||||||
|
@ -618,11 +621,11 @@ class UserService {
|
||||||
memLimit: keyAttributes.memLimit,
|
memLimit: keyAttributes.memLimit,
|
||||||
opsLimit: keyAttributes.opsLimit,
|
opsLimit: keyAttributes.opsLimit,
|
||||||
);
|
);
|
||||||
await registerOrUpdateSrp(loginKey, setKeysRequest: setKeyRequest);
|
await registerOrUpdateSrp(
|
||||||
// await _enteDio.put(
|
loginKey,
|
||||||
// "/users/keys",
|
setKeysRequest: setKeyRequest,
|
||||||
// data: setKeyRequest.toMap(),
|
logOutOtherDevices: logoutOtherDevices,
|
||||||
// );
|
);
|
||||||
await _config.setKeyAttributes(keyAttributes);
|
await _config.setKeyAttributes(keyAttributes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/account/recovery_key_page.dart';
|
import 'package:ente_auth/ui/account/recovery_key_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/common/web_page.dart';
|
import 'package:ente_auth/ui/common/web_page.dart';
|
||||||
|
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||||
import 'package:ente_auth/ui/home_page.dart';
|
import 'package:ente_auth/ui/home_page.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/navigation_util.dart';
|
import 'package:ente_auth/utils/navigation_util.dart';
|
||||||
|
@ -24,8 +25,7 @@ enum PasswordEntryMode {
|
||||||
class PasswordEntryPage extends StatefulWidget {
|
class PasswordEntryPage extends StatefulWidget {
|
||||||
final PasswordEntryMode mode;
|
final PasswordEntryMode mode;
|
||||||
|
|
||||||
const PasswordEntryPage({required this.mode, Key? key})
|
const PasswordEntryPage({required this.mode, Key? key}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
||||||
|
@ -180,7 +180,8 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
||||||
.copyWith(fontSize: 14),
|
.copyWith(fontSize: 14),
|
||||||
tags: {
|
tags: {
|
||||||
'underline': StyledTextTag(
|
'underline': StyledTextTag(
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
style:
|
||||||
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
|
@ -356,7 +357,8 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: context.l10n.howItWorks,
|
text: context.l10n.howItWorks,
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
style:
|
||||||
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
|
@ -374,13 +376,18 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updatePassword() async {
|
void _updatePassword() async {
|
||||||
|
final logOutFromOthers = await logOutFromOtherDevices(context);
|
||||||
final dialog =
|
final dialog =
|
||||||
createProgressDialog(context, context.l10n.generatingEncryptionKeys);
|
createProgressDialog(context, context.l10n.generatingEncryptionKeys);
|
||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
final result = await Configuration.instance
|
final result = await Configuration.instance
|
||||||
.getAttributesForNewPassword(_passwordController1.text);
|
.getAttributesForNewPassword(_passwordController1.text);
|
||||||
await UserService.instance.updateKeyAttributes(result.item1, result.item2);
|
await UserService.instance.updateKeyAttributes(
|
||||||
|
result.item1,
|
||||||
|
result.item2,
|
||||||
|
logoutOtherDevices: logOutFromOthers,
|
||||||
|
);
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
showShortToast(context, context.l10n.passwordChangedSuccessfully);
|
showShortToast(context, context.l10n.passwordChangedSuccessfully);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -394,13 +401,34 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> logOutFromOtherDevices(BuildContext context) async {
|
||||||
|
bool logOutFromOther = true;
|
||||||
|
await showChoiceDialog(
|
||||||
|
context,
|
||||||
|
title: context.l10n.signOutFromOtherDevices,
|
||||||
|
body: context.l10n.signOutOtherBody,
|
||||||
|
isDismissible: false,
|
||||||
|
firstButtonLabel: context.l10n.signOutOtherDevices,
|
||||||
|
firstButtonType: ButtonType.critical,
|
||||||
|
firstButtonOnTap: () async {
|
||||||
|
logOutFromOther = true;
|
||||||
|
},
|
||||||
|
secondButtonLabel: context.l10n.doNotSignOut,
|
||||||
|
secondButtonOnTap: () async {
|
||||||
|
logOutFromOther = false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return logOutFromOther;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _showRecoveryCodeDialog(String password) async {
|
Future<void> _showRecoveryCodeDialog(String password) async {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final dialog =
|
final dialog =
|
||||||
createProgressDialog(context, l10n.generatingEncryptionKeysTitle);
|
createProgressDialog(context, l10n.generatingEncryptionKeysTitle);
|
||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
final KeyGenResult result = await Configuration.instance.generateKey(password);
|
final KeyGenResult result =
|
||||||
|
await Configuration.instance.generateKey(password);
|
||||||
Configuration.instance.setVolatilePassword(null);
|
Configuration.instance.setVolatilePassword(null);
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
onDone() async {
|
onDone() async {
|
||||||
|
|
Loading…
Reference in a new issue