Compare commits

...

5 Commits

Author SHA1 Message Date
Neeraj Gupta
77632b3241 Bump version 2.0.26+226 2023-11-27 18:39:22 +05:30
Neeraj Gupta
d02e02a326 Sign up: Add field for 'hear us from' info (#376) 2023-11-27 18:06:49 +05:30
github-actions[bot]
7fe314a16a New Translations (#375)
New translations via [Crowdin GH
Action](https://github.com/crowdin/github-action)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-11-27 18:04:23 +05:30
green
504fa939d2 Add CLI tool + disclaimer to Authy guide (#373)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Description
Adds a hyperlink to Neeraj's CLI tool shown in ente Discord, as well as
a disclaimer to check all accounts have been imported before deleting
Authy data
<!--- Describe your changes in detail -->

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ] 🖼️ New icon
- [ ]  New feature (non-breaking change which adds functionality)
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [x] 📝 Documentation
- [ ] 🗑️ Chore
2023-11-27 08:33:49 +05:30
Neeraj Gupta
17f5a7996a Change Password: Confirm before signing out from other devices (#374) 2023-11-26 16:27:25 +05:30
7 changed files with 135 additions and 27 deletions

View File

@@ -398,5 +398,11 @@
"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",
"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",
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!"
}

View File

@@ -84,10 +84,12 @@
"importFromApp": "从 {appName} 导入代码",
"importGoogleAuthGuide": "使用“转移帐户”选项将您的帐户从 Google 身份验证器导出到二维码。然后使用另一台设备扫描二维码。\n\n提示您可以使用笔记本电脑的网络摄像头拍摄二维码的照片。",
"importSelectJsonFile": "选择 JSON 文件",
"importSelectAppExport": "选择 {appName} 的导出文件",
"importEnteEncGuide": "选择从ente导出的加密JSON文件",
"importRaivoGuide": "使用 Raivo 设置中的“将 OTP 导出到 Zip 存档”选项。\n\n解压 zip 文件并导入 JSON 文件。",
"importBitwardenGuide": "使用 Bitwarden 工具中的“导出保管库”选项并导入未加密的 JSON 文件。",
"importAegisGuide": "在Aegis的设置中使用\"导出密码库\"选项。\n\n如果您的密码库已加密您需要输入密码才能解密密码库。",
"import2FasGuide": "使用 2FAS 中的“设置 -> 备份 - 导出”选项。\n\n如果您的备份已被加密则需要输入密码才能解密备份",
"exportCodes": "导出代码",
"importLabel": "导入",
"importInstruction": "请以以下格式选择包含代码列表的文件",
@@ -395,5 +397,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."
},
"noInternetConnection": "无互联网连接",
"pleaseCheckYourInternetConnectionAndTryAgain": "请检查您的互联网连接,然后重试。"
"pleaseCheckYourInternetConnectionAndTryAgain": "请检查您的互联网连接,然后重试。",
"signOutFromOtherDevices": "从其他设备退出登录",
"signOutOtherBody": "如果你认为有人可能知道你的密码,你可以强制所有使用你账户的其他设备退出登录。",
"signOutOtherDevices": "登出其他设备",
"doNotSignOut": "不要退登"
}

View File

@@ -45,6 +45,7 @@ import "package:uuid/uuid.dart";
class UserService {
static const keyHasEnabledTwoFactor = "has_enabled_two_factor";
static const keyUserDetails = "user_details";
static const kReferralSource = "referral_source";
static const kCanDisableEmailMFA = "can_disable_email_mfa";
static const kIsEmailMFAEnabled = "is_email_mfa_enabled";
final SRP6GroupParameters kDefaultSrpGroup = SRP6StandardGroups.rfc5054_4096;
@@ -270,13 +271,17 @@ class UserService {
}) async {
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
final verifyData = {
"email": _config.getEmail(),
"ott": ott,
};
if (!_config.isLoggedIn()) {
verifyData["source"] = 'auth:' + _getRefSource();
}
try {
final response = await _dio.post(
_config.getHttpEndpoint() + "/users/verify-email",
data: {
"email": _config.getEmail(),
"ott": ott,
},
data: verifyData,
);
await dialog.hide();
if (response.statusCode == 200) {
@@ -441,6 +446,7 @@ class UserService {
Future<void> registerOrUpdateSrp(
Uint8List loginKey, {
SetKeysRequest? setKeysRequest,
bool logOutOtherDevices = false,
}) async {
try {
final String username = const Uuid().v4().toString();
@@ -496,6 +502,7 @@ class UserService {
'setupID': setupSRPResponse.setupID,
'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)),
'updatedKeyAttr': setKeysRequest.toMap(),
'logOutOtherDevices': logOutOtherDevices,
},
);
}
@@ -608,8 +615,9 @@ class UserService {
Future<void> updateKeyAttributes(
KeyAttributes keyAttributes,
Uint8List loginKey,
) async {
Uint8List loginKey, {
required bool logoutOtherDevices,
}) async {
try {
final setKeyRequest = SetKeysRequest(
kekSalt: keyAttributes.kekSalt,
@@ -618,11 +626,11 @@ class UserService {
memLimit: keyAttributes.memLimit,
opsLimit: keyAttributes.opsLimit,
);
await registerOrUpdateSrp(loginKey, setKeysRequest: setKeyRequest);
// await _enteDio.put(
// "/users/keys",
// data: setKeyRequest.toMap(),
// );
await registerOrUpdateSrp(
loginKey,
setKeysRequest: setKeyRequest,
logOutOtherDevices: logoutOtherDevices,
);
await _config.setKeyAttributes(keyAttributes);
} catch (e) {
_logger.severe(e);
@@ -887,4 +895,12 @@ class UserService {
rethrow;
}
}
Future<void> setRefSource(String refSource) async {
await _preferences.setString(kReferralSource, refSource);
}
String _getRefSource() {
return _preferences.getString(kReferralSource) ?? "";
}
}

View File

@@ -3,8 +3,10 @@ import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/common/dynamic_fab.dart';
import 'package:ente_auth/ui/common/web_page.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:password_strength/password_strength.dart';
@@ -30,6 +32,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
String? _email;
String? _password;
String _cnfPassword = '';
String _referralSource = '';
double _passwordStrength = 0.0;
bool _emailIsValid = false;
bool _hasAgreedToTOS = true;
@@ -104,6 +107,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
onPressedFunction: () {
_config.setVolatilePassword(_passwordController1.text);
UserService.instance.setEmail(_email!);
UserService.instance.setRefSource(_referralSource);
UserService.instance
.sendOtt(context, _email!, isCreateAccountScreen: true);
FocusScope.of(context).unfocus();
@@ -325,6 +329,51 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
),
),
const SizedBox(height: 4),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 0, horizontal: 20),
child: Text(
context.l10n.hearUsWhereTitle,
style: getEnteTextTheme(context).smallFaint,
),
),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: TextFormField(
style: Theme.of(context).textTheme.titleMedium,
decoration: InputDecoration(
fillColor: null,
filled: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
border: UnderlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(6),
),
suffixIcon: InkWell(
onTap: () {
showToast(
context,
context.l10n.hearUsExplanation,
);
},
child: Icon(
Icons.info_outline_rounded,
color: getEnteColorScheme(context).strokeMuted,
),
),
),
onChanged: (value) {
_referralSource = value.trim();
},
autocorrect: false,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.next,
),
),
const Divider(thickness: 1),
const SizedBox(height: 12),
_getAgreement(),

View File

@@ -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/common/dynamic_fab.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/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
@@ -24,8 +25,7 @@ enum PasswordEntryMode {
class PasswordEntryPage extends StatefulWidget {
final PasswordEntryMode mode;
const PasswordEntryPage({required this.mode, Key? key})
: super(key: key);
const PasswordEntryPage({required this.mode, Key? key}) : super(key: key);
@override
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
@@ -180,10 +180,11 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
.copyWith(fontSize: 14),
tags: {
'underline': StyledTextTag(
style: Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
decoration: TextDecoration.underline,
),
style:
Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
decoration: TextDecoration.underline,
),
),
},
),
@@ -356,10 +357,11 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
child: RichText(
text: TextSpan(
text: context.l10n.howItWorks,
style: Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
decoration: TextDecoration.underline,
),
style:
Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
decoration: TextDecoration.underline,
),
),
),
),
@@ -374,13 +376,18 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
}
void _updatePassword() async {
final logOutFromOthers = await logOutFromOtherDevices(context);
final dialog =
createProgressDialog(context, context.l10n.generatingEncryptionKeys);
await dialog.show();
try {
final result = await Configuration.instance
.getAttributesForNewPassword(_passwordController1.text);
await UserService.instance.updateKeyAttributes(result.item1, result.item2);
await UserService.instance.updateKeyAttributes(
result.item1,
result.item2,
logoutOtherDevices: logOutFromOthers,
);
await dialog.hide();
showShortToast(context, context.l10n.passwordChangedSuccessfully);
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 {
final l10n = context.l10n;
final dialog =
createProgressDialog(context, l10n.generatingEncryptionKeysTitle);
await dialog.show();
try {
final KeyGenResult result = await Configuration.instance.generateKey(password);
final KeyGenResult result =
await Configuration.instance.generateKey(password);
Configuration.instance.setVolatilePassword(null);
await dialog.hide();
onDone() async {

View File

@@ -10,6 +10,9 @@ A user on GitHub has written a guide to export our data from Authy (morpheus on
## Exporting from Authy
To export your data, please follow [this guide](https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93). This will create a new JSON file with all your Authy TOTP data in it. **Do not share this file with anyone!**
Or, you can [use this tool by Neeraj](https://github.com/ua741/authy-export/releases/tag/v0.0.4) to simplify things and skip to importing to ente Authenticator.
### *Do note that these tools may not export ALL of your codes. Make sure that all your accounts have been imported successfully before deleting any codes from your Authy account!*
## Converting the export for ente Authenticator
So now that you have the JSON file, does that mean it can be imported into ente Authenticator? Nope.
@@ -17,7 +20,7 @@ This is because the code in the guide exports your Authy data for Bitwarden, not
<img width="454" alt="ente Authenticator Screenshot" src="https://github.com/gweeeen/auth/assets/41323182/30566a69-cfa0-4de0-9f0d-95967d4c5cad">
So, this means that even if you try to import this file, nothing will happen. But don't worry, I've written a program in Python that converts the JSON file into a TXT file that ente Authenticator can use!
So, this means that even if you try to import this file, nothing will happen. But don't worry, I've written a program in Python that converts the JSON file into a TXT file that ente Authenticator can use! (It's definitely not written **professionaly**, but hey it gets the job done so I'm happy with that.)
You can download my program [here](https://github.com/gweeeen/ducky/blob/main/duckys_other_stuff/authy_to_ente.py). Or if you **really like making life hard**, then you can make a new Python file and copy this code to it:

View File

@@ -1,6 +1,6 @@
name: ente_auth
description: ente two-factor authenticator
version: 2.0.25+225
version: 2.0.26+226
publish_to: none
environment: