Better 2fa verification support (#6782)

* Better 2fa verification support

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* 2FA login verification, add request filed 'tfa_code'

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* 2fa dialog

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* msgbox, title

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Feat, oidc login, 2fa

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2024-01-03 16:43:39 +08:00 committed by GitHub
parent 89150317e1
commit 7e3a0c4ded
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 176 additions and 54 deletions

View File

@ -11,9 +11,11 @@ class HttpType {
static const kAuthReqTypeMobile = "mobile";
static const kAuthReqTypeSMSCode = "sms_code";
static const kAuthReqTypeEmailCode = "email_code";
static const kAuthReqTypeTfaCode = "tfa_code";
static const kAuthResTypeToken = "access_token";
static const kAuthResTypeEmailCheck = "email_check";
static const kAuthResTypeTfaCheck = "tfa_check";
}
enum UserStatus { kDisabled, kNormal, kUnverified }
@ -118,6 +120,7 @@ class LoginRequest {
bool? autoLogin;
String? type;
String? verificationCode;
String? tfaCode;
LoginRequest(
{this.username,
@ -126,7 +129,8 @@ class LoginRequest {
this.uuid,
this.autoLogin,
this.type,
this.verificationCode});
this.verificationCode,
this.tfaCode});
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
@ -139,6 +143,7 @@ class LoginRequest {
if (verificationCode != null) {
data['verificationCode'] = verificationCode;
}
if (tfaCode != null) data['tfaCode'] = tfaCode;
Map<String, dynamic> deviceInfo = {};
try {
@ -154,13 +159,15 @@ class LoginRequest {
class LoginResponse {
String? access_token;
String? type;
String? tfa_type;
UserPayload? user;
LoginResponse({this.access_token, this.type, this.user});
LoginResponse({this.access_token, this.type, this.tfa_type, this.user});
LoginResponse.fromJson(Map<String, dynamic> json) {
access_token = json['access_token'];
type = json['type'];
tfa_type = json['tfa_type'];
user = json['user'] != null ? UserPayload.fromJson(json['user']) : null;
}
}

View File

@ -427,6 +427,54 @@ Future<bool?> loginDialog() async {
close(false);
}
handleLoginResponse(LoginResponse resp, bool storeIfAccessToken,
void Function([dynamic])? close) async {
switch (resp.type) {
case HttpType.kAuthResTypeToken:
if (resp.access_token != null) {
if (storeIfAccessToken) {
await bind.mainSetLocalOption(
key: 'access_token', value: resp.access_token!);
await bind.mainSetLocalOption(
key: 'user_info', value: jsonEncode(resp.user ?? {}));
}
if (close != null) {
close(true);
}
return;
}
break;
case HttpType.kAuthResTypeEmailCheck:
bool? isEmailVerification;
if (resp.tfa_type == null ||
resp.tfa_type == HttpType.kAuthResTypeEmailCheck) {
isEmailVerification = true;
} else if (resp.tfa_type == HttpType.kAuthResTypeTfaCheck) {
isEmailVerification = false;
} else {
passwordMsg = "Failed, bad tfa type from server";
}
if (isEmailVerification != null) {
if (isMobile) {
if (close != null) close(false);
verificationCodeDialog(resp.user, isEmailVerification);
} else {
setState(() => isInProgress = false);
final res =
await verificationCodeDialog(resp.user, isEmailVerification);
if (res == true) {
if (close != null) close(false);
return;
}
}
}
break;
default:
passwordMsg = "Failed, bad response from server";
break;
}
}
onLogin() async {
// validate
if (username.text.isEmpty) {
@ -447,35 +495,7 @@ Future<bool?> loginDialog() async {
uuid: await bind.mainGetUuid(),
autoLogin: true,
type: HttpType.kAuthReqTypeAccount));
switch (resp.type) {
case HttpType.kAuthResTypeToken:
if (resp.access_token != null) {
await bind.mainSetLocalOption(
key: 'access_token', value: resp.access_token!);
await bind.mainSetLocalOption(
key: 'user_info', value: jsonEncode(resp.user ?? {}));
close(true);
return;
}
break;
case HttpType.kAuthResTypeEmailCheck:
if (isMobile) {
close(true);
verificationCodeDialog(resp.user);
} else {
setState(() => isInProgress = false);
final res = await verificationCodeDialog(resp.user);
if (res == true) {
close(true);
return;
}
}
break;
default:
passwordMsg = "Failed, bad response from server";
break;
}
await handleLoginResponse(resp, true, close);
} on RequestException catch (err) {
passwordMsg = translate(err.cause);
} catch (err) {
@ -506,15 +526,21 @@ Future<bool?> loginDialog() async {
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
.toList(),
curOP: curOP,
cbLogin: (Map<String, dynamic> authBody) {
cbLogin: (Map<String, dynamic> authBody) async {
LoginResponse? resp;
try {
// access_token is already stored in the rust side.
resp =
gFFI.userModel.getLoginResponseFromAuthBody(authBody);
} catch (e) {
debugPrint(
'Failed to parse oidc login body: "$authBody"');
}
close(true);
if (resp != null) {
handleLoginResponse(resp, false, null);
}
},
),
],
@ -583,7 +609,8 @@ Future<bool?> loginDialog() async {
return res;
}
Future<bool?> verificationCodeDialog(UserPayload? user) async {
Future<bool?> verificationCodeDialog(
UserPayload? user, bool isEmailVerification) async {
var autoLogin = true;
var isInProgress = false;
String? errorText;
@ -614,6 +641,7 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
try {
final resp = await gFFI.userModel.login(LoginRequest(
verificationCode: code.text,
tfaCode: isEmailVerification ? null : code.text,
username: user?.name,
id: await bind.mainGetMyId(),
uuid: await bind.mainGetUuid(),
@ -648,20 +676,22 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
content: Column(
children: [
Offstage(
offstage: user?.email == null,
offstage: !isEmailVerification || user?.email == null,
child: TextField(
decoration: InputDecoration(
labelText: "Email", prefixIcon: Icon(Icons.email)),
readOnly: true,
controller: TextEditingController(text: user?.email),
)),
const SizedBox(height: 8),
isEmailVerification ? const SizedBox(height: 8) : const Offstage(),
DialogTextField(
title: '${translate("Verification code")}:',
title:
'${translate(isEmailVerification ? "Verification code" : "2FA code")}:',
controller: code,
errorText: errorText,
focusNode: focusNode,
helperText: translate('verification_tip'),
helperText: translate(
isEmailVerification ? 'verification_tip' : '2fa_tip'),
),
/*
CheckboxListTile(

View File

@ -95,6 +95,8 @@ pub struct UserPayload {
pub struct AuthBody {
pub access_token: String,
pub r#type: String,
#[serde(default)]
pub tfa_type: String,
pub user: UserPayload,
}
@ -238,6 +240,7 @@ impl OidcSession {
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
match Self::query(&api_server, &code_url.code, &id, &uuid) {
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
if auth_body.r#type == "access_token" {
if remember_me {
LocalConfig::set_option(
"access_token".to_owned(),
@ -248,6 +251,7 @@ impl OidcSession {
serde_json::json!({ "name": auth_body.user.name, "status": auth_body.user.status }).to_string(),
);
}
}
OIDC_SESSION
.write()
.unwrap()

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "用户提权后,不能显示多个显示器。若要控制多显示器,请安装后再试。"),
("Swap control-command key", "交换Control键和Command键"),
("swap-left-right-mouse", "交换鼠标左右键"),
("2FA code", "2FA code"),
("2fa_tip", "请输入授权 app 中的 2FA code"),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Snímání více displejů není podporováno v uživatelském režimu se zvýšenými oprávněními. Pokud chcete ovládat více displejů, zkuste to znovu po instalaci."),
("Swap control-command key", "Prohození klávesy control-command"),
("swap-left-right-mouse", "Prohodit levé a pravé tlačítko myši"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Das Erfassen mehrerer Bildschirme wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."),
("Swap control-command key", "Steuerungs- und Befehlstasten tauschen"),
("swap-left-right-mouse", "Linke und rechte Maustaste tauschen"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -210,5 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input_source_2_tip", "Input source 2"),
("capture_display_elevated_connections_tip", "Capturing multiple displays is not supported in the elevated user mode. Please try again after installation if you want to control multiple displays."),
("swap-left-right-mouse", "Swap left-right mouse button"),
("2FA code", "2FA code"),
("2fa_tip", "Please enter the your 2FA code in the authentication app."),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "La captura de múltiples pantallas en el modo de usaurio con privilegios elevados no está soportada. Por favor, inténtalo de nuevo tras la instalación si quieres controlar múltiples pantallas."),
("Swap control-command key", "Intercambiar teclas control-comando"),
("swap-left-right-mouse", "Intercambiar botones derecho-izquierdo del ratón"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Mitme ekraani jäädvustamine ei ole kõrgendatud kasutajarežiimis toetatud. Kui soovid juhtida mitut ekraani, palun proovi uuesti pärast paigaldamist."),
("Swap control-command key", ""),
("swap-left-right-mouse", "Vaheta vasak ja parem hiirenupp"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "La capture de plusieurs écrans n'est pas prise en charge en mode utilisateur avec privilièges. Veuillez réessayer après l'installation si vous souhaitez contrôler plusieurs écrans."),
("Swap control-command key", "Échanger la touche de controle-commande"),
("swap-left-right-mouse", "Intervertir le bouton gauche et droit de la souris"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "La cattura di più display non è supportata nella modalità utente con privilegi elevati. Se vuoi controllare più display riprova dopo l'installazione."),
("Swap control-command key", "Scambia tasto controllo-comando"),
("swap-left-right-mouse", "Scambia pulsante sinistro-destro mouse"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "권한 상승된 사용자 모드에서는 다중 디스플레이 캡처가 지원되지 않습니다. 다중 디스플레이를 제어하려면 설치 후 재시도하세요."),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Vairāku displeju uzņemšana netiek atbalstīta paaugstinātā lietotāja režīmā. Lūdzu, mēģiniet vēlreiz pēc instalēšanas, ja vēlaties kontrolēt vairākus displejus."),
("Swap control-command key", "Apmainīt vadības un komandas taustiņu"),
("swap-left-right-mouse", "Apmainīt kreiso un labo peles pogu"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Scannen van meerdere schermen wordt niet ondersteund in de bevoorrechte gebruikersmodus. Als je meerdere schermen wilt bedienen, probeer het dan opnieuw na de installatie."),
("Swap control-command key", "Wissel controle-commando toets"),
("swap-left-right-mouse", "wissel-links-rechts-muis"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Przechwytywanie wielu ekranów nie jest obsługiwane w trybie użytkownika z podwyższonym poziomem uprawnień. Jeśli chcesz sterować wieloma wyświetlaczami, spróbuj ponownie po instalacji."),
("Swap control-command key", "Zamiana przycisków sterujących myszki"),
("swap-left-right-mouse", "Zamień przyciski myszki (lewy - prawy)"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Захват экрана нескольких дисплеев не поддерживается в режиме повышенных прав. Повторите попытку после установки, если хотите управлять несколькими дисплеями."),
("Swap control-command key", "Поменять кнопки управления и команд"),
("swap-left-right-mouse", "Поменять левую и правую кнопки мыши"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "Snímanie viacerých displejov nie je podporované v režime privilegovaného používateľa. Ak chcete ovládať viac displejov, skúste to po inštalácii znova."),
("Swap control-command key", "Vymeniť kláves ovládania a príkazu"),
("swap-left-right-mouse", "Prehodiť ľavé a pravé tlačidlo myši"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", "В режимі розширених прав захоплення декількох дисплеїв не підтримується. Якщо ви хочете керувати декількома дисплеями, будь ласка, спробуйте це після встановлення."),
("Swap control-command key", "Поміняти місцями клавіші Control та Command"),
("swap-left-right-mouse", "Поміняти місцями ліву та праву кнопки миші"),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("capture_display_elevated_connections_tip", ""),
("Swap control-command key", ""),
("swap-left-right-mouse", ""),
("2FA code", ""),
("2fa_tip", ""),
].iter().cloned().collect();
}

View File

@ -1223,7 +1223,7 @@ function login() {
if (data.type == 'email_check') {
abLoading = false;
show_progress(-1);
on_email_check(data);
on_2fa_check(data);
return;
}
handler.set_local_option("access_token", data.access_token);
@ -1241,12 +1241,14 @@ function login() {
});
}
function on_email_check(last_msg) {
function on_2fa_check(last_msg) {
var isEmailCheck = !last_msg.tfa_type || last_msg.tfa_type == 'email_check';
var emailHint = last_msg.user.email;
msgbox("custom-email-verification-code", translate('Verification code'), <div .form .set-password>
<div><span>{translate('Email')}:</span><span>{emailHint}</span></div>
<div><span>{translate('Verification code')}:</span><input|text name="verification_code" .outline-focus /></div>
<div style="font-size:0.9em; margin-bottom:1em;">{translate('verification_tip')}</div>
msgbox("custom-2fa-verification-code", translate('Verification code'), <div .form .set-password>
{ isEmailCheck && <div><span>{translate('Email')}:</span><span>{emailHint}</span></div> }
<div><span>{translate(isEmailCheck ? 'Verification code' : '2FA code')}:</span><input|text name="verification_code" .outline-focus /></div>
<div style="font-size:0.9em; margin-bottom:1em;">{translate(isEmailCheck ? 'verification_tip' : '2fa_tip')}</div>
</div>, "",
function(res=null, show_progress) {
if (!res) return;
@ -1258,7 +1260,8 @@ function on_email_check(last_msg) {
}
abLoading = true;
var url = handler.get_api_server();
httpRequest(url + "/api/login", #post, {username: last_msg.user.name, id: my_id, uuid: handler.get_uuid(), type: 'email_code', autoLogin: true, verificationCode: code, deviceInfo: getDeviceInfo()},
const loginData = {username: last_msg.user.name, id: my_id, uuid: handler.get_uuid(), type: 'email_code', verificationCode: code, tfaCode: isEmailCheck ? '' : code, deviceInfo: getDeviceInfo()};
httpRequest(url + "/api/login", #post, loginData,
function(data) {
if (data.error) {
abLoading = false;