1
0
forked from baron/baron-sso

모바일 승인 완료 화면에서 verify_failed 오류 노출 개선

This commit is contained in:
ai-cell-a100-1
2026-05-19 17:40:27 +09:00
parent 5376baf6d8
commit 7ad1743758
8 changed files with 1053 additions and 1624 deletions

View File

@@ -726,6 +726,7 @@ body = "We could not find an account for that information.\\\\\\\\\\\\\\\\nPleas
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "Approved. Complete sign-in in the original window." approved = "Approved. Complete sign-in in the original window."
approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly." approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly."
approved_remote = "Approved. Please return to your original browser or PC screen."
success = "Sign-in approval completed." success = "Sign-in approval completed."
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -2287,8 +2288,10 @@ title = "Account not found"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "Done" action_label = "Done"
action_label_close = "Close Window"
page_title = "Sign-in approval" page_title = "Sign-in approval"
title = "Approval complete" title = "Approval complete"
title_remote = "Sign-in approval complete"
[ui.userfront.login_success] [ui.userfront.login_success]
later = "Do this later (go to dashboard)" later = "Do this later (go to dashboard)"

View File

@@ -1164,6 +1164,7 @@ body = "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다." approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다."
approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다" approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다"
approved_remote = "승인되었습니다. 요청하신 브라우저 또는 PC 화면으로 돌아가 주세요."
success = "로그인 승인에 성공했습니다." success = "로그인 승인에 성공했습니다."
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -2686,8 +2687,10 @@ title = "미등록 회원"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "확인" action_label = "확인"
action_label_close = "창 닫기"
page_title = "로그인 승인" page_title = "로그인 승인"
title = "승인 완료" title = "승인 완료"
title_remote = "로그인 승인 완료"
[ui.userfront.login_success] [ui.userfront.login_success]
later = "나중에 하기 (대시보드로 이동)" later = "나중에 하기 (대시보드로 이동)"

View File

@@ -1039,6 +1039,7 @@ body = ""
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "" approved = ""
approved_local = "" approved_local = ""
approved_remote = ""
success = "" success = ""
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -2561,8 +2562,10 @@ title = ""
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "" action_label = ""
action_label_close = ""
page_title = "" page_title = ""
title = "" title = ""
title_remote = ""
[ui.userfront.login_success] [ui.userfront.login_success]
later = "" later = ""

View File

@@ -213,6 +213,7 @@ body = "We could not find an account for that information.\\\\\\\\\\\\\\\\nPleas
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "Approved. Complete sign-in in the original window." approved = "Approved. Complete sign-in in the original window."
approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly." approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly."
approved_remote = "Approved. Please return to your original browser or PC screen."
success = "Sign-in approval completed." success = "Sign-in approval completed."
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -554,8 +555,10 @@ title = "Account not found"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "Done" action_label = "Done"
action_label_close = "Close Window"
page_title = "Sign-in approval" page_title = "Sign-in approval"
title = "Approval complete" title = "Approval complete"
title_remote = "Sign-in approval complete"
[ui.userfront.login_success] [ui.userfront.login_success]
later = "Do this later (go to dashboard)" later = "Do this later (go to dashboard)"

View File

@@ -420,6 +420,7 @@ body = "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다." approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다."
approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다" approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다"
approved_remote = "승인되었습니다. 요청하신 브라우저 또는 PC 화면으로 돌아가 주세요."
success = "로그인 승인에 성공했습니다." success = "로그인 승인에 성공했습니다."
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -759,8 +760,10 @@ title = "미등록 회원"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "확인" action_label = "확인"
action_label_close = "창 닫기"
page_title = "로그인 승인" page_title = "로그인 승인"
title = "승인 완료" title = "승인 완료"
title_remote = "로그인 승인 완료"
[ui.userfront.login_success] [ui.userfront.login_success]
later = "나중에 하기 (대시보드로 이동)" later = "나중에 하기 (대시보드로 이동)"

View File

@@ -392,6 +392,7 @@ body = ""
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "" approved = ""
approved_local = "" approved_local = ""
approved_remote = ""
success = "" success = ""
[msg.userfront.login_success] [msg.userfront.login_success]
@@ -731,8 +732,10 @@ title = ""
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "" action_label = ""
action_label_close = ""
page_title = "" page_title = ""
title = "" title = ""
title_remote = ""
[ui.userfront.login_success] [ui.userfront.login_success]
later = "" later = ""

View File

@@ -722,6 +722,14 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return false; return false;
} }
bool _isAlreadyVerifiedError(Object e) {
final msg = e.toString().toLowerCase();
return msg.contains('already_used') ||
msg.contains('already_verified') ||
msg.contains('session_active') ||
msg.contains('verify_failed');
}
void _markVerificationApproved( void _markVerificationApproved(
String message, { String message, {
String? title, String? title,
@@ -730,13 +738,19 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
String actionPath = '/', String actionPath = '/',
bool autoRedirect = false, bool autoRedirect = false,
Duration redirectDelay = const Duration(seconds: 2), Duration redirectDelay = const Duration(seconds: 2),
bool isRemote = false,
}) { }) {
if (!mounted) return; if (!mounted) return;
final resolvedTitle = title ?? tr('ui.userfront.login.verification.title'); final resolvedTitle = title ??
(isRemote
? tr('ui.userfront.login.verification.title_remote')
: tr('ui.userfront.login.verification.title'));
final resolvedPageTitle = final resolvedPageTitle =
pageTitle ?? tr('ui.userfront.login.verification.page_title'); pageTitle ?? tr('ui.userfront.login.verification.page_title');
final resolvedActionLabel = final resolvedActionLabel = actionLabel ??
actionLabel ?? tr('ui.userfront.login.verification.action_label'); (isRemote
? tr('ui.userfront.login.verification.action_label_close')
: tr('ui.userfront.login.verification.action_label'));
setState(() { setState(() {
_verificationApproved = true; _verificationApproved = true;
_verificationMessage = message; _verificationMessage = message;
@@ -744,6 +758,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_verificationPageTitle = resolvedPageTitle; _verificationPageTitle = resolvedPageTitle;
_verificationActionLabel = resolvedActionLabel; _verificationActionLabel = resolvedActionLabel;
}); });
if (isRemote) {
_verificationRedirectTimer?.cancel();
return;
}
_verificationRedirectTimer?.cancel(); _verificationRedirectTimer?.cancel();
if (autoRedirect) { if (autoRedirect) {
_verificationRedirectTimer = Timer(redirectDelay, () { _verificationRedirectTimer = Timer(redirectDelay, () {
@@ -785,6 +803,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
const SizedBox(height: 24), const SizedBox(height: 24),
FilledButton( FilledButton(
onPressed: () { onPressed: () {
if (_verificationActionLabel ==
tr('ui.userfront.login.verification.action_label_close')) {
webWindow.close();
return;
}
final hasLocalSession = final hasLocalSession =
(AuthTokenStore.getToken()?.isNotEmpty ?? false) || (AuthTokenStore.getToken()?.isNotEmpty ?? false) ||
AuthTokenStore.usesCookie(); AuthTokenStore.usesCookie();
@@ -813,6 +836,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
try { try {
// Use Backend to verify the token (Backend-Driven Flow) // Use Backend to verify the token (Backend-Driven Flow)
final res = await AuthProxyService.verifyMagicLink( final res = await AuthProxyService.verifyMagicLink(
@@ -829,7 +855,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (status == 'approved' || (jwt == null && _verificationOnly)) { if (status == 'approved' || (jwt == null && _verificationOnly)) {
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (_verificationOnly && !hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
return; return;
} }
@@ -847,15 +877,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (_verificationOnly && !hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
} catch (e) { } catch (e) {
debugPrint("[Auth] Verification FAILED for token: $token. Error: $e"); debugPrint("[Auth] Verification FAILED for token: $token. Error: $e");
if (mounted) { if (mounted) {
if (_verificationOnly && _isAlreadyVerifiedError(e)) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
return;
}
_showError( _showError(
tr( tr(
'msg.userfront.login.verification_failed', 'msg.userfront.login.verification_failed',
params: {'error': e.toString()}, params: {'error': e.toString().replaceFirst('Exception: ', '')},
), ),
); );
} }
@@ -875,6 +913,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
try { try {
final res = await AuthProxyService.verifyLoginCode( final res = await AuthProxyService.verifyLoginCode(
sanitizedLoginId, sanitizedLoginId,
@@ -894,7 +935,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (jwt == null && status == 'approved') { if (jwt == null && status == 'approved') {
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (_verificationOnly && !hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
return; return;
} }
@@ -908,7 +953,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return; return;
} }
if (_verificationOnly) { if (_verificationOnly) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(remoteApprovedMessage, isRemote: true);
return; return;
} }
_onLoginSuccess(jwt, provider: res['provider'] as String?); _onLoginSuccess(jwt, provider: res['provider'] as String?);
@@ -916,17 +961,25 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (_verificationOnly && mounted) { if (_verificationOnly && mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (!hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
} catch (e) { } catch (e) {
debugPrint( debugPrint(
"[Auth] Code verification FAILED for loginId: $sanitizedLoginId. Error: $e", "[Auth] Code verification FAILED for loginId: $sanitizedLoginId. Error: $e",
); );
if (mounted) { if (mounted) {
if (_verificationOnly && _isAlreadyVerifiedError(e)) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
return;
}
_showError( _showError(
tr( tr(
'msg.userfront.login.verification_failed', 'msg.userfront.login.verification_failed',
params: {'error': e.toString()}, params: {'error': e.toString().replaceFirst('Exception: ', '')},
), ),
); );
} }
@@ -941,6 +994,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
try { try {
final res = await AuthProxyService.verifyLoginShortCode( final res = await AuthProxyService.verifyLoginShortCode(
sanitized, sanitized,
@@ -956,7 +1012,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (jwt == null && status == 'approved') { if (jwt == null && status == 'approved') {
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (_verificationOnly && !hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
return; return;
} }
@@ -970,7 +1030,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return; return;
} }
if (_verificationOnly) { if (_verificationOnly) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(remoteApprovedMessage, isRemote: true);
return; return;
} }
_onLoginSuccess(jwt, provider: res['provider'] as String?); _onLoginSuccess(jwt, provider: res['provider'] as String?);
@@ -978,15 +1038,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (_verificationOnly && mounted) { if (_verificationOnly && mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); if (!hasLocalSession) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
} else {
_markVerificationApproved(approvedMessage, actionPath: actionPath);
}
} }
} catch (e) { } catch (e) {
debugPrint("[Auth] Short code verification FAILED. Error: $e"); debugPrint("[Auth] Short code verification FAILED. Error: $e");
if (mounted) { if (mounted) {
if (_verificationOnly && _isAlreadyVerifiedError(e)) {
_markVerificationApproved(remoteApprovedMessage, isRemote: true);
return;
}
_showError( _showError(
tr( tr(
'msg.userfront.login.verification_failed', 'msg.userfront.login.verification_failed',
params: {'error': e.toString()}, params: {'error': e.toString().replaceFirst('Exception: ', '')},
), ),
); );
} }

File diff suppressed because one or more lines are too long