Merge conflict: Resolved

This commit is contained in:
Muhammed Ayimen 2023-11-14 17:47:22 +09:00
commit 1705229ad9
6 changed files with 152 additions and 30 deletions

View file

@ -1,5 +1,6 @@
{
"account": "Account",
"unlock": "Unlock",
"recoveryKey": "Recovery key",
"counterAppBarTitle": "Counter",
"@counterAppBarTitle": {
@ -97,6 +98,7 @@
"authToViewYourRecoveryKey": "Please authenticate to view your recovery key",
"authToChangeYourEmail": "Please authenticate to change your email",
"authToChangeYourPassword": "Please authenticate to change your password",
"authToViewSecrets": "Please authenticate to view your secrets",
"ok": "Ok",
"cancel": "Cancel",
"yes": "Yes",
@ -344,4 +346,56 @@
"importGoogleAuthImageButtonText": "Import from image",
"unableToRecognizeQrCodeText": "Unable to recognize a valid code from the uploaded image",
"qrCodeImageNotSelectedText": "Qr code image not selected"
"androidBiometricHint": "Verify identity",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
},
"androidBiometricNotRecognized": "Not recognized. Try again.",
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
"androidBiometricSuccess": "Success",
"@androidBiometricSuccess": {
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
},
"androidCancelButton": "Cancel",
"@androidCancelButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
},
"androidSignInTitle": "Authentication required",
"@androidSignInTitle": {
"description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters."
},
"androidBiometricRequiredTitle": "Biometric required",
"@androidBiometricRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsRequiredTitle": "Device credentials required",
"@androidDeviceCredentialsRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsSetupDescription": "Device credentials required",
"@androidDeviceCredentialsSetupDescription": {
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
},
"goToSettings": "Go to settings",
"@goToSettings": {
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
},
"androidGoToSettingsDescription": "Biometric authentication is not set up on your device. Go to 'Settings > Security' to add biometric authentication.",
"@androidGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
},
"iOSLockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.",
"@iOSLockOut": {
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
},
"iOSGoToSettingsDescription": "Biometric authentication is not set up on your device. Please either enable Touch ID or Face ID on your phone.",
"@iOSGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side."
},
"iOSOkButton": "OK",
"@iOSOkButton": {
"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."
}
}

View file

@ -1,5 +1,3 @@
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/ui/tools/app_lock.dart';
import 'package:ente_auth/utils/auth_util.dart';
@ -19,7 +17,7 @@ class LocalAuthenticationService {
) async {
if (await _isLocalAuthSupportedOnDevice()) {
AppLock.of(context)!.setEnabled(false);
final result = await requestAuthentication(infoMessage);
final result = await requestAuthentication(context, infoMessage);
AppLock.of(context)!.setEnabled(
Configuration.instance.shouldShowLockScreen(),
);
@ -43,6 +41,7 @@ class LocalAuthenticationService {
if (await LocalAuthentication().isDeviceSupported()) {
AppLock.of(context)!.disable();
final result = await requestAuthentication(
context,
infoMessage,
);
if (result) {

View file

@ -1,5 +1,4 @@
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/ui/common/gradient_button.dart';
import 'package:ente_auth/ui/tools/app_lock.dart';
import 'package:ente_auth/utils/auth_util.dart';
@ -13,13 +12,19 @@ class LockScreen extends StatefulWidget {
State<LockScreen> createState() => _LockScreenState();
}
class _LockScreenState extends State<LockScreen> {
class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
final _logger = Logger("LockScreen");
bool _isShowingLockScreen = false;
bool _hasPlacedAppInBackground = false;
bool _hasAuthenticationFailed = false;
int? lastAuthenticatingTime;
@override
void initState() {
_showLockScreen();
_logger.info("initState");
super.initState();
_showLockScreen(source: "initState");
WidgetsBinding.instance.addObserver(this);
}
@override
@ -34,16 +39,16 @@ class _LockScreenState extends State<LockScreen> {
alignment: Alignment.center,
children: [
Opacity(
opacity: 0.3,
opacity: 0.2,
child: Image.asset('assets/loading_photos_background.png'),
),
SizedBox(
width: 142,
width: 180,
child: GradientButton(
text: "Unlock",
text: context.l10n.unlock,
iconData: Icons.lock_open_outlined,
onTap: () async {
_showLockScreen();
_showLockScreen(source: "tapUnlock");
},
),
),
@ -55,16 +60,65 @@ class _LockScreenState extends State<LockScreen> {
);
}
Future<void> _showLockScreen() async {
_logger.info("Showing lockscreen");
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_logger.info(state.toString());
if (state == AppLifecycleState.resumed && !_isShowingLockScreen) {
// This is triggered either when the lock screen is dismissed or when
// the app is brought to foreground
_hasPlacedAppInBackground = false;
final bool didAuthInLast5Seconds = lastAuthenticatingTime != null &&
DateTime.now().millisecondsSinceEpoch - lastAuthenticatingTime! <
5000;
if (!_hasAuthenticationFailed && !didAuthInLast5Seconds) {
// Show the lock screen again only if the app is resuming from the
// background, and not when the lock screen was explicitly dismissed
Future.delayed(
Duration.zero, () => _showLockScreen(source: "lifeCycle"));
} else {
_hasAuthenticationFailed = false; // Reset failure state
}
} else if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive) {
// This is triggered either when the lock screen pops up or when
// the app is pushed to background
if (!_isShowingLockScreen) {
_hasPlacedAppInBackground = true;
_hasAuthenticationFailed = false; // reset failure state
}
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Future<void> _showLockScreen({String source = ''}) async {
final int id = DateTime.now().millisecondsSinceEpoch;
_logger.info("Showing lock screen $source $id");
try {
_isShowingLockScreen = true;
final result = await requestAuthentication(
"Please authenticate to view your secrets",
context,
context.l10n.authToViewSecrets,
);
_logger.finest("LockScreen Result $result $id");
_isShowingLockScreen = false;
if (result) {
lastAuthenticatingTime = DateTime.now().millisecondsSinceEpoch;
AppLock.of(context)!.didUnlock();
} else {
if (!_hasPlacedAppInBackground) {
// Treat this as a failure only if user did not explicitly
// put the app in background
_hasAuthenticationFailed = true;
_logger.info("Authentication failed");
}
}
} catch (e, s) {
_isShowingLockScreen = false;
_logger.severe(e, s);
}
}

View file

@ -1,25 +1,37 @@
import 'package:ente_auth/l10n/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:local_auth/local_auth.dart';
import 'package:local_auth_android/local_auth_android.dart';
import 'package:local_auth_ios/types/auth_messages_ios.dart';
import 'package:logging/logging.dart';
Future<bool> requestAuthentication(String reason) async {
Future<bool> requestAuthentication(BuildContext context, String reason) async {
Logger("AuthUtil").info("Requesting authentication");
await LocalAuthentication().stopAuthentication();
final l10n = context.l10n;
return await LocalAuthentication().authenticate(
localizedReason: reason,
authMessages: [
const AndroidAuthMessages(
biometricHint: "Verify identity",
biometricNotRecognized: "Not recognized, try again",
biometricRequiredTitle: "Biometric required",
biometricSuccess: "Successfully verified",
cancelButton: "Cancel",
deviceCredentialsRequiredTitle: "Device credentials required",
deviceCredentialsSetupDescription: "Device credentials required",
goToSettingsButton: "Go to settings",
goToSettingsDescription:
"Authentication is not setup on your device, go to Settings > Security to set it up",
signInTitle: "Authentication required",
AndroidAuthMessages(
biometricHint: l10n.androidBiometricHint,
biometricNotRecognized: l10n.androidBiometricNotRecognized,
biometricRequiredTitle: l10n.androidBiometricRequiredTitle,
biometricSuccess: l10n.androidBiometricSuccess,
cancelButton: l10n.androidCancelButton,
deviceCredentialsRequiredTitle:
l10n.androidDeviceCredentialsRequiredTitle,
deviceCredentialsSetupDescription:
l10n.androidDeviceCredentialsSetupDescription,
goToSettingsButton: l10n.goToSettings,
goToSettingsDescription: l10n.androidGoToSettingsDescription,
signInTitle: l10n.androidSignInTitle,
),
IOSAuthMessages(
goToSettingsButton: l10n.goToSettings,
goToSettingsDescription: l10n.goToSettings,
lockOut: l10n.iOSLockOut,
// cancelButton default value is "Ok"
cancelButton: l10n.iOSOkButton,
),
],
);

View file

@ -792,7 +792,7 @@ packages:
source: hosted
version: "2.1.7"
local_auth_android:
dependency: transitive
dependency: "direct main"
description:
name: local_auth_android
sha256: "523dd636ce061ddb296cbc3db410cb8f21efb7d8798f7b9532c8038ce2f8bad5"
@ -800,7 +800,7 @@ packages:
source: hosted
version: "1.0.31"
local_auth_ios:
dependency: transitive
dependency: "direct main"
description:
name: local_auth_ios
sha256: edc2977c5145492f3451db9507a2f2f284ee4f408950b3e16670838726761940

View file

@ -1,6 +1,6 @@
name: ente_auth
description: ente two-factor authenticator
version: 2.0.15+215
version: 2.0.17+217
publish_to: none
environment:
@ -54,6 +54,9 @@ dependencies:
intl: ^0.18.0
json_annotation: ^4.5.0
local_auth: ^2.1.7
local_auth_android: ^1.0.31
local_auth_ios: ^1.1.3
logging: ^1.0.1
mobile_scanner: ^3.5.2
modal_bottom_sheet: ^3.0.0-pre