[auth] Enable login via totp when both passkey & totp are enabled

This commit is contained in:
Neeraj Gupta 2024-11-27 15:25:45 +05:30
parent 08160f251a
commit 14fe9bcb72
4 changed files with 59 additions and 12 deletions

View File

@ -156,6 +156,7 @@
"twoFactorAuthTitle": "Two-factor authentication",
"passkeyAuthTitle": "Passkey verification",
"verifyPasskey": "Verify passkey",
"loginWithTOTP": "Login with TOTP",
"recoverAccount": "Recover account",
"enterRecoveryKeyHint": "Enter your recovery key",
"recover": "Recover",

View File

@ -1,5 +1,6 @@
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:url_launcher/url_launcher_string.dart';
@ -24,6 +25,10 @@ class PasskeyService {
return response.data!["isPasskeyRecoveryEnabled"] as bool;
}
String get accountsUrl {
return kDebugMode ? "http://localhost:3001" : "https://accounts.ente.io";
}
Future<void> configurePasskeyRecovery(
String secret,
String userEncryptedSecret,
@ -42,7 +47,7 @@ class PasskeyService {
Future<void> openPasskeyPage(BuildContext context) async {
try {
final jwtToken = await getJwtToken();
final url = "https://accounts.ente.io/passkeys?token=$jwtToken";
final url = "$accountsUrl/passkeys?token=$jwtToken";
await launchUrlString(
url,
mode: LaunchMode.externalApplication,

View File

@ -84,7 +84,7 @@ class UserService {
"${_config.getHttpEndpoint()}/users/ott",
data: {
"email": email,
"purpose": isChangeEmail ? "change" : purpose ?? "",
"purpose": isChangeEmail ? "change" : purpose ?? "",
},
);
await dialog.hide();
@ -379,11 +379,17 @@ class UserService {
if (response.statusCode == 200) {
Widget page;
final String passkeySessionID = response.data["passkeySessionID"];
final String twoFASessionID = response.data["twoFactorSessionID"];
if (twoFASessionID.isNotEmpty) {
String twoFASessionID = response.data["twoFactorSessionID"];
if (twoFASessionID.isEmpty) {
twoFASessionID = response.data["twoFactorSessionIDV2"];
}
if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(
passkeySessionID,
totp2FASessionID: twoFASessionID,
);
} else if (twoFASessionID.isNotEmpty) {
page = TwoFactorAuthenticationPage(twoFASessionID);
} else if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(passkeySessionID);
} else {
await _saveConfiguration(response);
if (Configuration.instance.getEncryptedToken() != null) {
@ -685,13 +691,18 @@ class UserService {
if (response.statusCode == 200) {
Widget? page;
final String passkeySessionID = response.data["passkeySessionID"];
final String twoFASessionID = response.data["twoFactorSessionID"];
String twoFASessionID = response.data["twoFactorSessionID"];
if (twoFASessionID.isEmpty) {
twoFASessionID = response.data["twoFactorSessionIDV2"];
}
Configuration.instance.setVolatilePassword(userPassword);
if (twoFASessionID.isNotEmpty) {
if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(
passkeySessionID,
totp2FASessionID: twoFASessionID,
);
} else if (twoFASessionID.isNotEmpty) {
page = TwoFactorAuthenticationPage(twoFASessionID);
} else if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(passkeySessionID);
} else {
await _saveConfiguration(response);
if (Configuration.instance.getEncryptedToken() != null) {

View File

@ -5,10 +5,13 @@ import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/services/passkey_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/two_factor_authentication_page.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
@ -16,9 +19,11 @@ import 'package:url_launcher/url_launcher_string.dart';
class PasskeyPage extends StatefulWidget {
final String sessionID;
final String totp2FASessionID;
const PasskeyPage(
this.sessionID, {
required this.totp2FASessionID,
super.key,
});
@ -42,8 +47,9 @@ class _PasskeyPageState extends State<PasskeyPage> {
}
Future<void> launchPasskey() async {
final String accountsUrl = PasskeyService.instance.accountsUrl;
await launchUrlString(
"https://accounts.ente.io/passkeys/verify?"
"$accountsUrl/passkeys/verify?"
"passkeySessionID=${widget.sessionID}"
"&redirect=enteauth://passkey"
"&clientPackage=io.ente.auth",
@ -175,6 +181,30 @@ class _PasskeyPageState extends State<PasskeyPage> {
shouldSurfaceExecutionStates: true,
),
const Padding(padding: EdgeInsets.all(30)),
if (widget.totp2FASessionID.isNotEmpty)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
routeToPage(
context,
TwoFactorAuthenticationPage(
widget.totp2FASessionID,
),
);
},
child: Container(
padding: const EdgeInsets.all(10),
child: Center(
child: Text(
context.l10n.loginWithTOTP,
style: const TextStyle(
decoration: TextDecoration.underline,
fontSize: 12,
),
),
),
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {