fix(portal): redirect unauthenticated users to login — never show portal chrome (GRO-309)
- CustomerPortal.tsx: add initComplete state to track async session initialization. After init completes with no valid session, redirect: staff dev users → /admin, all others → /login - Dashboard.tsx: change !sessionId fallback from dead-end UI to <Navigate to="/login" replace /> (defense-in-depth) - All 85 web unit tests pass Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useCallback, useEffect, useRef } from "react";
|
import { useState, useCallback, useEffect, useRef } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams, Navigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
Home, Calendar, PawPrint, FileText, CreditCard, MessageSquare,
|
Home, Calendar, PawPrint, FileText, CreditCard, MessageSquare,
|
||||||
Settings, LogOut, Shield,
|
Settings, LogOut, Shield,
|
||||||
@@ -38,6 +38,7 @@ export function CustomerPortal() {
|
|||||||
const [session, setSession] = useState<ImpersonationSession | null>(null);
|
const [session, setSession] = useState<ImpersonationSession | null>(null);
|
||||||
const [sessionExtended, setSessionExtended] = useState(false);
|
const [sessionExtended, setSessionExtended] = useState(false);
|
||||||
const [clientName, setClientName] = useState<string>("");
|
const [clientName, setClientName] = useState<string>("");
|
||||||
|
const [initComplete, setInitComplete] = useState(false);
|
||||||
const { branding } = useBranding();
|
const { branding } = useBranding();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
@@ -68,7 +69,8 @@ export function CustomerPortal() {
|
|||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setSearchParams({}, { replace: true });
|
setSearchParams({}, { replace: true });
|
||||||
});
|
})
|
||||||
|
.finally(() => setInitComplete(true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +92,11 @@ export function CustomerPortal() {
|
|||||||
setClientName(devUser.name);
|
setClientName(devUser.name);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {})
|
||||||
|
.finally(() => setInitComplete(true));
|
||||||
|
} else {
|
||||||
|
// No valid session: staff dev users and unauthenticated users fall through here
|
||||||
|
setInitComplete(true);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -168,6 +174,16 @@ export function CustomerPortal() {
|
|||||||
|
|
||||||
const avatarInitials = (clientName.split(" ")[0] || "G").charAt(0).toUpperCase();
|
const avatarInitials = (clientName.split(" ")[0] || "G").charAt(0).toUpperCase();
|
||||||
|
|
||||||
|
// After init completes, redirect unauthenticated users to /login and staff to /admin
|
||||||
|
// The portal chrome must NEVER be visible to users without a valid client session
|
||||||
|
if (initComplete && !session) {
|
||||||
|
const devUser = getDevUser();
|
||||||
|
if (devUser && devUser.type === "staff") {
|
||||||
|
return <Navigate to="/admin" replace />;
|
||||||
|
}
|
||||||
|
return <Navigate to="/login" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="min-h-screen bg-[#faf8f5] font-sans"
|
className="min-h-screen bg-[#faf8f5] font-sans"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { Navigate } from "react-router-dom";
|
||||||
import { Calendar, Clock, PawPrint, CreditCard, Star, ChevronRight, AlertTriangle } from "lucide-react";
|
import { Calendar, Clock, PawPrint, CreditCard, Star, ChevronRight, AlertTriangle } from "lucide-react";
|
||||||
|
|
||||||
interface DashboardProps {
|
interface DashboardProps {
|
||||||
@@ -183,13 +184,7 @@ export function Dashboard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
return (
|
return <Navigate to="/login" replace />;
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="bg-stone-100 rounded-2xl p-5 text-center">
|
|
||||||
<p className="text-stone-600">Please sign in to view your dashboard.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const upcomingAppointments = getUpcomingAppointments();
|
const upcomingAppointments = getUpcomingAppointments();
|
||||||
|
|||||||
Reference in New Issue
Block a user