Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
684 changes: 684 additions & 0 deletions flutter_profile_popup.dart

Large diffs are not rendered by default.

835 changes: 835 additions & 0 deletions main.dart

Large diffs are not rendered by default.

29 changes: 19 additions & 10 deletions services/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { AuthProvider } from "./context/AuthContext";
import MainLayout from "./layout/MainLayout";
import Dashboard from "./pages/Dashboard";
import GraphView from "./pages/GraphView";
import Cases from "./pages/Cases";
import Alerts from "./pages/Alerts";
import Reports from "./pages/Reports";
import Profile from "./pages/Profile";
import Documentation from "./pages/Documentation";
import Authentication from "./pages/Authentication";

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainLayout />}>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="graph/:account_id" element={<GraphView />} />
<Route path="cases" element={<Cases />} />
<Route path="alerts" element={<Alerts />} />
<Route path="reports" element={<Reports />} />
</Route>
</Routes>
<AuthProvider>
<Routes>
<Route path="/auth" element={<Authentication />} />
<Route path="/" element={<MainLayout />}>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="graph/:account_id" element={<GraphView />} />
<Route path="cases" element={<Cases />} />
<Route path="alerts" element={<Alerts />} />
<Route path="reports" element={<Reports />} />
<Route path="profile" element={<Profile />} />
<Route path="docs" element={<Documentation />} />
</Route>
</Routes>
</AuthProvider>
</BrowserRouter>
);
}
Expand Down
64 changes: 64 additions & 0 deletions services/dashboard/src/context/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { createContext, useContext, useState, useCallback, useEffect } from "react";

type AuthContextType = {
isLoggedIn: boolean;
userEmail: string | null;
login: (email: string) => void;
logout: () => void;
};

const AuthContext = createContext<AuthContextType | null>(null);

export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
const [isLoggedIn, setIsLoggedIn] = useState(() => {
try {
const stored = localStorage.getItem("isLoggedIn");
return stored === "true";
} catch {
Comment on lines +21 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Do not trust auth state sourced only from localStorage.

isLoggedIn can be forged by manually setting localStorage keys, which allows frontend auth bypass.

Use a server-verified session/token check during bootstrap and treat localStorage as cache only.

Also applies to: 37-43

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/dashboard/src/context/AuthContext.tsx` around lines 21 - 25, The
AuthContext currently initializes isLoggedIn from localStorage (useState
initializer) which can be forged; instead treat localStorage as a cache only and
perform a server-verified session/token check during bootstrap: update the
AuthContext bootstrap flow (the useEffect or bootstrapAuth routine that calls
setIsLoggedIn) to call your backend session or token verification endpoint
(e.g., verifySession / getCurrentUser) and set isLoggedIn based on that
response, falling back to localStorage only while the network call is pending;
ensure any localStorage read in the isLoggedIn initializer is used as a
temporary optimistic value and that all protected actions rely on the
server-validated state returned to setIsLoggedIn and to clear invalid
localStorage tokens via setIsLoggedIn and localStorage.removeItem when
verification fails.

return false;
}
});
const [userEmail, setUserEmail] = useState<string | null>(() => {
try {
return localStorage.getItem("userEmail");
} catch {
return null;
}
});

const login = useCallback((email: string) => {
setIsLoggedIn(true);
setUserEmail(email);
try {
localStorage.setItem("isLoggedIn", "true");
localStorage.setItem("userEmail", email);
} catch {
// LocalStorage not available
}
}, []);

const logout = useCallback(() => {
setIsLoggedIn(false);
setUserEmail(null);
try {
localStorage.removeItem("isLoggedIn");
localStorage.removeItem("userEmail");
} catch {
// LocalStorage not available
}
}, []);

return (
<AuthContext.Provider value={{ isLoggedIn, userEmail, login, logout }}>
{children}
</AuthContext.Provider>
);
}
Loading