2026-06-13 09:52:56 +02:00
|
|
|
/**
|
|
|
|
|
* ProtectedRoute — renders children when authenticated; redirects to /login otherwise.
|
|
|
|
|
*
|
2026-06-13 10:04:14 +02:00
|
|
|
* Additional gate (M2-T07):
|
|
|
|
|
* - If the authenticated user has force_password_change === true, redirect to
|
|
|
|
|
* /change-password instead of rendering children. This prevents access to any
|
|
|
|
|
* protected page until the password is changed.
|
|
|
|
|
* - Shows a loading spinner while the session is still resolving to avoid flash-of-login.
|
|
|
|
|
* - On unauthenticated access, preserves the intended destination in location.state.from
|
|
|
|
|
* so LoginPage can redirect back after login.
|
2026-06-13 09:52:56 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type { ReactNode } from 'react'
|
2026-06-13 10:04:14 +02:00
|
|
|
import { Navigate, useLocation } from 'react-router-dom'
|
|
|
|
|
import { Center, Loader } from '@mantine/core'
|
2026-06-13 09:52:56 +02:00
|
|
|
import { useSession } from './SessionProvider'
|
|
|
|
|
|
|
|
|
|
interface ProtectedRouteProps {
|
|
|
|
|
children: ReactNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ProtectedRoute({ children }: ProtectedRouteProps) {
|
2026-06-13 10:04:14 +02:00
|
|
|
const { status, user } = useSession()
|
|
|
|
|
const location = useLocation()
|
2026-06-13 09:52:56 +02:00
|
|
|
|
|
|
|
|
if (status === 'loading') {
|
2026-06-13 10:04:14 +02:00
|
|
|
// Render a centred spinner while we check the session — avoids a flash to /login.
|
|
|
|
|
return (
|
|
|
|
|
<Center mih="100vh">
|
|
|
|
|
<Loader />
|
|
|
|
|
</Center>
|
|
|
|
|
)
|
2026-06-13 09:52:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status === 'unauthenticated') {
|
2026-06-13 10:04:14 +02:00
|
|
|
// Preserve the intended destination so LoginPage can redirect back after login.
|
|
|
|
|
return <Navigate to="/login" state={{ from: location }} replace />
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Authenticated but forced to change password — gate all protected pages.
|
|
|
|
|
if (user?.force_password_change && location.pathname !== '/change-password') {
|
|
|
|
|
return <Navigate to="/change-password" replace />
|
2026-06-13 09:52:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <>{children}</>
|
|
|
|
|
}
|