From bbf29bf400b68190ce6c7c48a5b4e26afc3b5124 Mon Sep 17 00:00:00 2001 From: chan Date: Tue, 28 Apr 2026 11:33:40 +0900 Subject: [PATCH 1/3] fix: clear stale auth flags and improve user name fallback logic (#637) - Clear AuthTokenStore in _silentSessionRecovery when session is invalid (Case 2) - Use .trim().isNotEmpty for userName fallback to handle empty strings (Case 1) --- .../dashboard/presentation/dashboard_screen.dart | 12 +++++++----- userfront/lib/main.dart | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart index 5188cdd8..a3c5399f 100644 --- a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart +++ b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart @@ -846,11 +846,13 @@ class _DashboardScreenState extends ConsumerState { final profileState = ref.watch(profileProvider); final profile = profileState.value; final timelineState = ref.watch(authTimelineProvider); - final userName = - profile?.name ?? - profile?.email ?? - profile?.phone ?? - tr('ui.userfront.profile.user_fallback', fallback: 'User'); + final userName = (profile?.name.trim().isNotEmpty ?? false) + ? profile!.name + : (profile?.email.trim().isNotEmpty ?? false) + ? profile!.email + : (profile?.phone.trim().isNotEmpty ?? false) + ? profile!.phone + : tr('ui.userfront.profile.user_fallback', fallback: 'User'); final departmentValue = profile?.tenant?.name ?? profile?.department ?? ''; final department = departmentValue.isNotEmpty diff --git a/userfront/lib/main.dart b/userfront/lib/main.dart index 4d0967d7..5bb87ea9 100644 --- a/userfront/lib/main.dart +++ b/userfront/lib/main.dart @@ -104,11 +104,13 @@ Future _silentSessionRecovery() async { _log.info("[SessionRecovery] Recovery complete. Subject: $subject"); } else { _log.warning("[SessionRecovery] Session found but subject is empty."); + AuthTokenStore.clear(); } } catch (e) { _log.info( "[SessionRecovery] No valid cookie session found or request failed: $e", ); + AuthTokenStore.clear(); } } From ff7a786c211bcdb2ce6ccf522b8a74a0d85f94d6 Mon Sep 17 00:00:00 2001 From: chan Date: Tue, 28 Apr 2026 11:51:41 +0900 Subject: [PATCH 2/3] fix: verify local token in _silentSessionRecovery to prevent 401 loop on expired JWT --- userfront/lib/main.dart | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/userfront/lib/main.dart b/userfront/lib/main.dart index 5bb87ea9..e8b05192 100644 --- a/userfront/lib/main.dart +++ b/userfront/lib/main.dart @@ -79,8 +79,26 @@ Future _silentSessionRecovery() async { // 1. Local token check final hasLocalToken = AuthTokenStore.hasToken(); if (hasLocalToken) { - _log.info("[SessionRecovery] Local token found. Skipping recovery."); - return; + _log.info("[SessionRecovery] Local token found. Verifying session..."); + try { + final status = await AuthProxyService.getSessionStatus( + token: AuthTokenStore.getToken(), + useCookie: false, + ); + if (status == 401 || status == 403) { + _log.warning("[SessionRecovery] Local token is invalid. Clearing store."); + AuthTokenStore.clear(); + return; + } + _log.info("[SessionRecovery] Local token is valid. Skipping cookie check."); + return; + } catch (e) { + _log.info("[SessionRecovery] Failed to verify local token: $e"); + // 만약 네트워크 에러 등이라면 당장 로그아웃 시키지 않고 일단 통과시킬 수도 있지만, + // 보안과 확실한 상태 갱신을 위해 여기서는 실패 시 상태를 유지하거나 필요에 따라 처리합니다. + // (현재는 401/403 확실한 인증 실패시에만 clear 처리) + return; + } } _log.info( From e3f9bbf925aa3c3c92f295886d3c522f311f3e49 Mon Sep 17 00:00:00 2001 From: chan Date: Tue, 28 Apr 2026 13:03:29 +0900 Subject: [PATCH 3/3] style: format dart files to pass formatting check --- .../features/dashboard/presentation/dashboard_screen.dart | 8 ++++---- userfront/lib/main.dart | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart index a3c5399f..2bcae7c6 100644 --- a/userfront/lib/features/dashboard/presentation/dashboard_screen.dart +++ b/userfront/lib/features/dashboard/presentation/dashboard_screen.dart @@ -849,10 +849,10 @@ class _DashboardScreenState extends ConsumerState { final userName = (profile?.name.trim().isNotEmpty ?? false) ? profile!.name : (profile?.email.trim().isNotEmpty ?? false) - ? profile!.email - : (profile?.phone.trim().isNotEmpty ?? false) - ? profile!.phone - : tr('ui.userfront.profile.user_fallback', fallback: 'User'); + ? profile!.email + : (profile?.phone.trim().isNotEmpty ?? false) + ? profile!.phone + : tr('ui.userfront.profile.user_fallback', fallback: 'User'); final departmentValue = profile?.tenant?.name ?? profile?.department ?? ''; final department = departmentValue.isNotEmpty diff --git a/userfront/lib/main.dart b/userfront/lib/main.dart index e8b05192..f33ef9f0 100644 --- a/userfront/lib/main.dart +++ b/userfront/lib/main.dart @@ -86,11 +86,15 @@ Future _silentSessionRecovery() async { useCookie: false, ); if (status == 401 || status == 403) { - _log.warning("[SessionRecovery] Local token is invalid. Clearing store."); + _log.warning( + "[SessionRecovery] Local token is invalid. Clearing store.", + ); AuthTokenStore.clear(); return; } - _log.info("[SessionRecovery] Local token is valid. Skipping cookie check."); + _log.info( + "[SessionRecovery] Local token is valid. Skipping cookie check.", + ); return; } catch (e) { _log.info("[SessionRecovery] Failed to verify local token: $e");