diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index 9a3c69b3..c2b2ac03 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -2358,8 +2358,9 @@ router.delete("/sessions/:sessionId", authenticateJWT, async (req, res) => { operation: "session_revoke", sessionId, revokedBy: userId, + sessionUserId: session.userId, }); - res.json({ message: "Session revoked successfully" }); + res.json({ success: true, message: "Session revoked successfully" }); } else { res.status(500).json({ error: "Failed to revoke session" }); } diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts index 3c66137d..b77719cc 100644 --- a/src/backend/utils/auth-manager.ts +++ b/src/backend/utils/auth-manager.ts @@ -267,6 +267,11 @@ class AuthManager { .limit(1); if (sessionRecords.length === 0) { + databaseLogger.warn("Session not found during JWT verification", { + operation: "jwt_verify_session_not_found", + sessionId: payload.sessionId, + userId: payload.userId, + }); return null; } } catch (dbError) { @@ -278,6 +283,7 @@ class AuthManager { sessionId: payload.sessionId, }, ); + return null; } } return payload; @@ -299,6 +305,22 @@ class AuthManager { try { await db.delete(sessions).where(eq(sessions.id, sessionId)); + try { + const { saveMemoryDatabaseToFile } = await import( + "../database/db/index.js" + ); + await saveMemoryDatabaseToFile(); + } catch (saveError) { + databaseLogger.error( + "Failed to save database after session revocation", + saveError, + { + operation: "session_revoke_db_save_failed", + sessionId, + }, + ); + } + return true; } catch (error) { databaseLogger.error("Failed to delete session", error, { @@ -336,6 +358,22 @@ class AuthManager { await db.delete(sessions).where(eq(sessions.userId, userId)); } + try { + const { saveMemoryDatabaseToFile } = await import( + "../database/db/index.js" + ); + await saveMemoryDatabaseToFile(); + } catch (saveError) { + databaseLogger.error( + "Failed to save database after revoking all user sessions", + saveError, + { + operation: "user_sessions_revoke_db_save_failed", + userId, + }, + ); + } + return deletedCount; } catch (error) { databaseLogger.error("Failed to delete user sessions", error, { @@ -440,6 +478,11 @@ class AuthManager { .limit(1); if (sessionRecords.length === 0) { + databaseLogger.warn("Session not found in middleware", { + operation: "middleware_session_not_found", + sessionId: payload.sessionId, + userId: payload.userId, + }); return res.status(401).json({ error: "Session not found", code: "SESSION_NOT_FOUND", @@ -479,10 +522,11 @@ class AuthManager { }); }); } catch (error) { - databaseLogger.error("Session check failed", error, { - operation: "session_check_failed", + databaseLogger.error("Session check failed in middleware", error, { + operation: "middleware_session_check_failed", sessionId: payload.sessionId, }); + return res.status(500).json({ error: "Session check failed" }); } } diff --git a/src/ui/mobile/MobileApp.tsx b/src/ui/mobile/MobileApp.tsx index 7fc6b129..511b569c 100644 --- a/src/ui/mobile/MobileApp.tsx +++ b/src/ui/mobile/MobileApp.tsx @@ -12,6 +12,10 @@ import { Auth } from "@/ui/mobile/authentication/Auth.tsx"; import { useTranslation } from "react-i18next"; import { Toaster } from "@/components/ui/sonner.tsx"; +function isReactNativeWebView(): boolean { + return typeof window !== "undefined" && !!(window as any).ReactNativeWebView; +} + const AppContent: FC = () => { const { t } = useTranslation(); const { tabs, currentTab, getTab } = useTabs(); @@ -114,7 +118,7 @@ const AppContent: FC = () => { ); } - if (!isAuthenticated) { + if (!isAuthenticated || isReactNativeWebView()) { return (