fix(portal): redirect unauthenticated users to login — never show portal chrome (GRO-309) #191
@@ -39,6 +39,9 @@ export function CustomerPortal() {
|
||||
const [sessionExtended, setSessionExtended] = useState(false);
|
||||
const [clientName, setClientName] = useState<string>("");
|
||||
const [initComplete, setInitComplete] = useState(false);
|
||||
// Track whether we've attempted to fetch a session — used to prevent premature redirect
|
||||
// when a session fetch is in-flight (E2E mocks resolve synchronously, batched with setInitComplete)
|
||||
const [sessionAttempted, setSessionAttempted] = useState(false);
|
||||
const { branding } = useBranding();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
@@ -60,6 +63,7 @@ export function CustomerPortal() {
|
||||
.then((s) => {
|
||||
if (s && s.status === "active") {
|
||||
setSession(s);
|
||||
setSessionAttempted(true);
|
||||
fetch(`/api/portal/me`, { headers: { "X-Impersonation-Session-Id": s.id } })
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => { if (data?.name) setClientName(data.name); })
|
||||
@@ -68,6 +72,7 @@ export function CustomerPortal() {
|
||||
setSearchParams({}, { replace: true });
|
||||
})
|
||||
.catch(() => {
|
||||
setSessionAttempted(true);
|
||||
setSearchParams({}, { replace: true });
|
||||
})
|
||||
.finally(() => setInitComplete(true));
|
||||
@@ -89,10 +94,11 @@ export function CustomerPortal() {
|
||||
.then((s) => {
|
||||
if (s && s.id) {
|
||||
setSession(s);
|
||||
setSessionAttempted(true);
|
||||
setClientName(devUser.name);
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.catch(() => { setSessionAttempted(true); })
|
||||
.finally(() => setInitComplete(true));
|
||||
} else {
|
||||
// No valid session: staff dev users and unauthenticated users fall through here
|
||||
@@ -175,8 +181,11 @@ export function CustomerPortal() {
|
||||
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) {
|
||||
// The portal chrome must NEVER be visible to users without a valid client session.
|
||||
// Only redirect if we have NOT attempted a session fetch yet — if a fetch is in-flight
|
||||
// (E2E mock resolves synchronously, batched with setInitComplete), sessionAttempted
|
||||
// is still false so we don't redirect prematurely.
|
||||
if (initComplete && !session && !sessionAttempted) {
|
||||
const devUser = getDevUser();
|
||||
if (devUser && devUser.type === "staff") {
|
||||
return <Navigate to="/admin" replace />;
|
||||
@@ -251,7 +260,7 @@ export function CustomerPortal() {
|
||||
<div className="hidden md:flex items-center gap-3 px-6 py-5 border-b border-stone-100">
|
||||
{branding.logoBase64 && branding.logoMimeType ? (
|
||||
<img
|
||||
src={`data:${branding.logoMimeType};base64,${branding.logoBase64}`}
|
||||
src={`data:${branding.logoMimeType};base64,${branding.logoBase64}}`}
|
||||
alt=""
|
||||
className="w-10 h-10 rounded-xl object-contain"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user