fix(auth): Startseite/AdminRoute redirecten bei Reload fälschlich auf Objekte
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:
@@ -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
@@ -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).
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user