1
0
forked from baron/baron-sso

Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2026-05-20 11:19:30 +09:00
2 changed files with 120 additions and 8 deletions

View File

@@ -730,6 +730,7 @@ 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),
VoidCallback? onAction,
}) { }) {
if (!mounted) return; if (!mounted) return;
final resolvedTitle = title ?? tr('ui.userfront.login.verification.title'); final resolvedTitle = title ?? tr('ui.userfront.login.verification.title');
@@ -743,16 +744,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_verificationTitle = resolvedTitle; _verificationTitle = resolvedTitle;
_verificationPageTitle = resolvedPageTitle; _verificationPageTitle = resolvedPageTitle;
_verificationActionLabel = resolvedActionLabel; _verificationActionLabel = resolvedActionLabel;
_onVerificationAction = onAction;
}); });
_verificationRedirectTimer?.cancel(); _verificationRedirectTimer?.cancel();
if (autoRedirect) { if (autoRedirect) {
_verificationRedirectTimer = Timer(redirectDelay, () { _verificationRedirectTimer = Timer(redirectDelay, () {
if (!mounted) return; if (!mounted) return;
context.go(actionPath); if (onAction != null) {
onAction();
} else {
context.go(actionPath);
}
}); });
} }
} }
VoidCallback? _onVerificationAction;
Widget _buildVerificationResultView() { Widget _buildVerificationResultView() {
return Center( return Center(
child: Padding( child: Padding(
@@ -785,6 +793,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
const SizedBox(height: 24), const SizedBox(height: 24),
FilledButton( FilledButton(
onPressed: () { onPressed: () {
if (_onVerificationAction != null) {
_onVerificationAction!();
return;
}
final hasLocalSession = final hasLocalSession =
(AuthTokenStore.getToken()?.isNotEmpty ?? false) || (AuthTokenStore.getToken()?.isNotEmpty ?? false) ||
AuthTokenStore.usesCookie(); AuthTokenStore.usesCookie();
@@ -810,6 +822,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
Future<void> _verifyToken(String token) async { Future<void> _verifyToken(String token) async {
debugPrint("[Auth] Starting verification for token: $token"); debugPrint("[Auth] Starting verification for token: $token");
final approvedMessage = tr('msg.userfront.login.verification.approved'); final approvedMessage = tr('msg.userfront.login.verification.approved');
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
@@ -829,7 +843,12 @@ 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); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
} }
return; return;
} }
@@ -851,6 +870,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
} catch (e) { } catch (e) {
debugPrint("[Auth] Verification FAILED for token: $token. Error: $e"); debugPrint("[Auth] Verification FAILED for token: $token. Error: $e");
// Handle the case where the token is already verified/used (common in remote flows)
final errorStr = e.toString();
if (errorStr.contains('already_used') ||
errorStr.contains('already_verified') ||
errorStr.contains('session_active')) {
if (mounted) {
_markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
}
return;
}
if (mounted) { if (mounted) {
_showError( _showError(
tr( tr(
@@ -872,6 +908,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
"[Auth] Starting code verification for loginId: $sanitizedLoginId", "[Auth] Starting code verification for loginId: $sanitizedLoginId",
); );
final approvedMessage = tr('msg.userfront.login.verification.approved'); final approvedMessage = tr('msg.userfront.login.verification.approved');
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
@@ -894,7 +932,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (jwt == null && status == 'approved') { if (jwt == null && status == 'approved') {
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
} }
return; return;
} }
@@ -908,7 +951,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return; return;
} }
if (_verificationOnly) { if (_verificationOnly) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
return; return;
} }
_onLoginSuccess(jwt, provider: res['provider'] as String?); _onLoginSuccess(jwt, provider: res['provider'] as String?);
@@ -916,12 +964,34 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (_verificationOnly && mounted) { if (_verificationOnly && mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
} }
} catch (e) { } catch (e) {
debugPrint( debugPrint(
"[Auth] Code verification FAILED for loginId: $sanitizedLoginId. Error: $e", "[Auth] Code verification FAILED for loginId: $sanitizedLoginId. Error: $e",
); );
// Handle the case where the code is already verified/used (common in remote flows)
final errorStr = e.toString();
if (errorStr.contains('already_used') ||
errorStr.contains('already_verified') ||
errorStr.contains('session_active')) {
if (mounted) {
_markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
}
return;
}
if (mounted) { if (mounted) {
_showError( _showError(
tr( tr(
@@ -938,6 +1008,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (sanitized.isEmpty) return; if (sanitized.isEmpty) return;
debugPrint("[Auth] Starting short code verification for code: $sanitized"); debugPrint("[Auth] Starting short code verification for code: $sanitized");
final approvedMessage = tr('msg.userfront.login.verification.approved'); final approvedMessage = tr('msg.userfront.login.verification.approved');
final remoteApprovedMessage =
tr('msg.userfront.login.verification.approved_remote');
final localSessionMessage = tr( final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
); );
@@ -956,7 +1028,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (jwt == null && status == 'approved') { if (jwt == null && status == 'approved') {
if (mounted) { if (mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
} }
return; return;
} }
@@ -970,7 +1047,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return; return;
} }
if (_verificationOnly) { if (_verificationOnly) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
return; return;
} }
_onLoginSuccess(jwt, provider: res['provider'] as String?); _onLoginSuccess(jwt, provider: res['provider'] as String?);
@@ -978,10 +1060,32 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (_verificationOnly && mounted) { if (_verificationOnly && mounted) {
_markVerificationApproved(approvedMessage, actionPath: actionPath); _markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
} }
} catch (e) { } catch (e) {
debugPrint("[Auth] Short code verification FAILED. Error: $e"); debugPrint("[Auth] Short code verification FAILED. Error: $e");
// Handle the case where the code is already verified/used (common in remote flows)
final errorStr = e.toString();
if (errorStr.contains('already_used') ||
errorStr.contains('already_verified') ||
errorStr.contains('session_active')) {
if (mounted) {
_markVerificationApproved(
remoteApprovedMessage,
title: tr('ui.userfront.login.verification.title_remote'),
actionLabel: tr('ui.userfront.login.verification.action_label_close'),
onAction: () => webWindow.close(),
);
}
return;
}
if (mounted) { if (mounted) {
_showError( _showError(
tr( tr(

View File

@@ -645,6 +645,8 @@ const Map<String, String> koStrings = {
"msg.userfront.login.token_missing": "로그인 토큰을 확인할 수 없습니다.", "msg.userfront.login.token_missing": "로그인 토큰을 확인할 수 없습니다.",
"msg.userfront.login.unregistered.body": "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주세요.", "msg.userfront.login.unregistered.body": "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주세요.",
"msg.userfront.login.verification.approved": "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다.", "msg.userfront.login.verification.approved": "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다.",
"msg.userfront.login.verification.approved_remote":
"승인되었습니다. 요청하신 브라우저 또는 PC 화면으로 돌아가 주세요.",
"msg.userfront.login.verification.approved_local": "msg.userfront.login.verification.approved_local":
"승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다", "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다",
"msg.userfront.login.verification.success": "로그인 승인에 성공했습니다.", "msg.userfront.login.verification.success": "로그인 승인에 성공했습니다.",
@@ -1914,8 +1916,10 @@ const Map<String, String> koStrings = {
"ui.userfront.login.unregistered.action": "회원가입 하기", "ui.userfront.login.unregistered.action": "회원가입 하기",
"ui.userfront.login.unregistered.title": "미등록 회원", "ui.userfront.login.unregistered.title": "미등록 회원",
"ui.userfront.login.verification.action_label": "확인", "ui.userfront.login.verification.action_label": "확인",
"ui.userfront.login.verification.action_label_close": "창 닫기",
"ui.userfront.login.verification.page_title": "로그인 승인", "ui.userfront.login.verification.page_title": "로그인 승인",
"ui.userfront.login.verification.title": "승인 완료", "ui.userfront.login.verification.title": "승인 완료",
"ui.userfront.login.verification.title_remote": "로그인 승인 완료",
"ui.userfront.login_success.later": "나중에 하기 (대시보드로 이동)", "ui.userfront.login_success.later": "나중에 하기 (대시보드로 이동)",
"ui.userfront.login_success.qr": "QR 인증 (카메라 켜기)", "ui.userfront.login_success.qr": "QR 인증 (카메라 켜기)",
"ui.userfront.login_success.title": "로그인 완료", "ui.userfront.login_success.title": "로그인 완료",
@@ -2777,6 +2781,8 @@ const Map<String, String> enStrings = {
"We could not find an account for that information.\\\\\\\\\\\\\\\\nPlease sign up before continuing.", "We could not find an account for that information.\\\\\\\\\\\\\\\\nPlease sign up before continuing.",
"msg.userfront.login.verification.approved": "msg.userfront.login.verification.approved":
"Approved. Complete sign-in in the original window.", "Approved. Complete sign-in in the original window.",
"msg.userfront.login.verification.approved_remote":
"Approved. Please return to the original browser or PC screen.",
"msg.userfront.login.verification.approved_local": "msg.userfront.login.verification.approved_local":
"Approved. This device is already signed in, and the remote window will be signed in shortly.", "Approved. This device is already signed in, and the remote window will be signed in shortly.",
"msg.userfront.login.verification.success": "Sign-in approval completed.", "msg.userfront.login.verification.success": "Sign-in approval completed.",
@@ -4110,8 +4116,10 @@ const Map<String, String> enStrings = {
"ui.userfront.login.unregistered.action": "Create an account", "ui.userfront.login.unregistered.action": "Create an account",
"ui.userfront.login.unregistered.title": "Account not found", "ui.userfront.login.unregistered.title": "Account not found",
"ui.userfront.login.verification.action_label": "Done", "ui.userfront.login.verification.action_label": "Done",
"ui.userfront.login.verification.action_label_close": "Close Window",
"ui.userfront.login.verification.page_title": "Sign-in approval", "ui.userfront.login.verification.page_title": "Sign-in approval",
"ui.userfront.login.verification.title": "Approval complete", "ui.userfront.login.verification.title": "Approval complete",
"ui.userfront.login.verification.title_remote": "Sign-in approved",
"ui.userfront.login_success.later": "Do this later (go to dashboard)", "ui.userfront.login_success.later": "Do this later (go to dashboard)",
"ui.userfront.login_success.qr": "Use QR approval", "ui.userfront.login_success.qr": "Use QR approval",
"ui.userfront.login_success.title": "Sign-in complete", "ui.userfront.login_success.title": "Sign-in complete",