diff --git a/apps/backoffice/src/app/router/LandingRoute.tsx b/apps/backoffice/src/app/router/LandingRoute.tsx
index 776bf01f8..c353e91d5 100644
--- a/apps/backoffice/src/app/router/LandingRoute.tsx
+++ b/apps/backoffice/src/app/router/LandingRoute.tsx
@@ -1,13 +1,12 @@
-import Cookies from 'js-cookie';
import { LandingPage } from '@ema-platform/ui';
-import { authStorage } from '@ema-platform/auth';
+import { useAuthToken } from '@ema-platform/auth';
/**
* Public `/` — mounts the shared landing page. Backoffice has no /signup
* (enableSignup: false) and no /verify route, so those props are omitted.
*/
export function LandingRoute() {
- const token = authStorage.getToken() ?? Cookies.get('auth-token');
+ const token = useAuthToken();
return ;
}
diff --git a/apps/portal/src/app/components/LandingRoute.tsx b/apps/portal/src/app/components/LandingRoute.tsx
index 39f419253..8bab1d394 100644
--- a/apps/portal/src/app/components/LandingRoute.tsx
+++ b/apps/portal/src/app/components/LandingRoute.tsx
@@ -1,15 +1,14 @@
-import Cookies from 'js-cookie';
import { LandingPage } from '@ema-platform/ui';
-import { authStorage } from '@ema-platform/auth';
+import { useAuthToken } from '@ema-platform/auth';
/**
* Public `/` — mounts the shared landing page with portal-specific routes.
- * Auth state is read the same way ProtectedRoute does (token cookie or
- * storage fallback) so the header can show "Go to dashboard" instead of
- * Login/Sign Up without gating the route itself.
+ * Auth state is read via `useAuthToken`, the same hook ProtectedRoute uses,
+ * so the header can show "Go to dashboard" instead of Login/Sign Up without
+ * gating the route itself.
*/
export function LandingRoute() {
- const token = authStorage.getToken() ?? Cookies.get('auth-token');
+ const token = useAuthToken();
return ;
}
diff --git a/libs/auth/src/index.ts b/libs/auth/src/index.ts
index 886e79d3b..a0f46f25f 100644
--- a/libs/auth/src/index.ts
+++ b/libs/auth/src/index.ts
@@ -25,6 +25,7 @@ export {
resetSignup,
} from "./lib/store/signup.slice";
export { usePermissions } from "./lib/hooks/usePermissions";
+export { useAuthToken } from "./lib/hooks/useAuthToken";
export type { PermissionSet } from "./lib/hooks/usePermissions";
export { RequirePermission } from "./lib/components/RequirePermission";
export {
diff --git a/libs/auth/src/lib/components/ProtectedRoute.tsx b/libs/auth/src/lib/components/ProtectedRoute.tsx
index 9c55d85ee..5321a5889 100644
--- a/libs/auth/src/lib/components/ProtectedRoute.tsx
+++ b/libs/auth/src/lib/components/ProtectedRoute.tsx
@@ -1,7 +1,6 @@
-import Cookies from 'js-cookie';
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import type { ReactNode } from 'react';
-import { authStorage } from '../utils/auth-storage';
+import { useAuthToken } from '../hooks/useAuthToken';
interface ProtectedRouteProps {
children?: ReactNode;
@@ -10,7 +9,7 @@ interface ProtectedRouteProps {
export function ProtectedRoute({ children, loginPath = '/login' }: ProtectedRouteProps) {
const location = useLocation();
- const token = authStorage.getToken() ?? Cookies.get('auth-token');
+ const token = useAuthToken();
if (!token) {
return ;
diff --git a/libs/auth/src/lib/hooks/useAuthToken.ts b/libs/auth/src/lib/hooks/useAuthToken.ts
new file mode 100644
index 000000000..c6b75bdb9
--- /dev/null
+++ b/libs/auth/src/lib/hooks/useAuthToken.ts
@@ -0,0 +1,15 @@
+import { useSelector } from 'react-redux';
+import { authStorage } from '../utils/auth-storage';
+import type { AuthState } from '../types/auth.types';
+
+/**
+ * The current auth token, preferring Redux so components re-render on login
+ * and logout. Falls back to storage for the one case Redux misses: both
+ * `hydrateAuth` and the stores' `preloadedState` only populate `auth.token`
+ * when a token *and* a cached user are present, so a session with a token but
+ * no cached user would otherwise read as signed out.
+ */
+export function useAuthToken(): string | undefined {
+ const token = useSelector((state: { auth: AuthState }) => state.auth.token);
+ return token ?? authStorage.getToken();
+}