fix(auth): Startseite/AdminRoute redirecten bei Reload fälschlich auf Objekte
CI / backend-tests (push) Failing after 2m9s
CI / frontend-build (push) Successful in 24s

Race Condition: bei bereits eingeloggter Session (Direktaufruf/Reload von "/" oder /dashboard/etc.) ist "eingeloggt" sofort true (Token im Storage), "rollen" aber noch [] bis /auth/me durch ist. Startseite/AdminRoute haben in genau diesem kurzen Fenster mit istVerantwortlich=false entschieden und per Navigate replace auf /objekte umgeleitet, bevor die echten Rollen (z.B. "administration") eintrafen - Admin landete dadurch nie auf dem Dashboard, sondern immer auf Objekte. Neues rollenGeladen-Flag im AuthContext, Startseite/AdminRoute warten jetzt darauf statt vorschnell mit rollen=[] zu entscheiden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-07 14:14:49 +02:00
co-authored by Claude Sonnet 5
parent 6db571f5ce
commit e3616c1929
3 changed files with 38 additions and 4 deletions
+14
View File
@@ -6658,3 +6658,17 @@ Keine Commits in dieser Session.
- frontend/src/styles/global.css | 2 ++
---
## 2026-09-07 14:03 14:07 (4m)
**Beschreibung:** Claude Code Session
**Projekt:** frontend
### Commits
- 6db571f fix(dashboard): Resize-Griff war unsichtbar - children-Injection landete in falscher Ebene
### Geänderte Dateien
- DEVLOG.md | 14 ++++++++++++++
- frontend/DEVLOG.md | 14 ++++++++++++++
- frontend/src/pages/dashboard/DashboardPage.tsx | 5 ++---
- frontend/src/pages/dashboard/TileFrame.tsx | 13 ++++++++++---
---
+11 -2
View File
@@ -21,16 +21,25 @@ function GeschuetzteRoute({ children }: { children: ReactNode }) {
}
function Startseite() {
const { eingeloggt, istVerantwortlich } = useAuth();
const { eingeloggt, istVerantwortlich, rollenGeladen } = useAuth();
if (!eingeloggt) return <Navigate to="/login" replace />;
// Ohne dieses Warten würde bei bereits eingeloggter Session (Reload/Direktaufruf)
// kurz mit rollen=[] entschieden und fälschlich auf /objekte redirected, bevor
// /auth/me durch ist.
if (!rollenGeladen) return null;
return <Navigate to={istVerantwortlich ? "/dashboard" : "/objekte"} replace />;
}
function AdminRoute({ children }: { children: ReactNode }) {
const { eingeloggt, istVerantwortlich } = useAuth();
const { eingeloggt, istVerantwortlich, rollenGeladen } = useAuth();
if (!eingeloggt) {
return <Navigate to="/login" replace />;
}
// Gleiches Race wie in Startseite: bei Direktaufruf/Reload noch nicht auf
// rollen=[] entscheiden, sonst kurzzeitig fälschlicher Redirect.
if (!rollenGeladen) {
return null;
}
// Nicht mehr admin-exklusiv: Materialverantwortliche/Leitungsverantwortliche
// dürfen rein, sehen dort aber nur die für sie freigeschalteten Tabs
// (AdminPage filtert selbst nach Rolle, Backend erzwingt es ohnehin je Endpunkt).
+13 -2
View File
@@ -5,6 +5,12 @@ import type { Me } from "../api/types";
interface AuthContextValue {
eingeloggt: boolean;
// Beim Direktladen/Reload einer bereits eingeloggten Session ist "eingeloggt"
// sofort true (Token liegt im Storage), "rollen" aber noch [] bis /auth/me
// durch ist - ohne dieses Flag würden Startseite/AdminRoute in genau diesem
// Fenster fälschlich "istVerantwortlich=false" annehmen und auf /objekte
// redirecten, obwohl die echten Rollen kurz danach eintreffen.
rollenGeladen: boolean;
rollen: string[];
istAdmin: boolean;
// Rollen-Vorgabe: Materialverantwortliche/Leitungsverantwortliche sahen bisher
@@ -23,20 +29,25 @@ const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [eingeloggt, setEingeloggt] = useState<boolean>(getAuthToken() !== null);
const [rollen, setRollen] = useState<string[]>([]);
const [rollenGeladen, setRollenGeladen] = useState(false);
useEffect(() => {
if (!eingeloggt) {
setRollen([]);
setRollenGeladen(true);
return;
}
setRollenGeladen(false);
apiRequest<Me>("/auth/me")
.then((me) => setRollen(me.rollen))
.catch(() => setRollen([]));
.catch(() => setRollen([]))
.finally(() => setRollenGeladen(true));
}, [eingeloggt]);
const value = useMemo<AuthContextValue>(
() => ({
eingeloggt,
rollenGeladen,
rollen,
istAdmin: rollen.includes("administration"),
istMaterialverantwortlich: rollen.includes("materialverantwortlicher"),
@@ -55,7 +66,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
setEingeloggt(false);
},
}),
[eingeloggt, rollen]
[eingeloggt, rollenGeladen, rollen]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;