From d382f7945d29d085f555c344646abe072e5981ef Mon Sep 17 00:00:00 2001
From: Musa Khalid <112591148+Mkalbani@users.noreply.github.com>
Date: Wed, 25 Feb 2026 11:39:27 +0000
Subject: [PATCH] feat: add AuthInitializer component and update middleware
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add initialize() method to auth store (runs once on app boot)
- Create AuthInitializer component that hydrates auth state on mount
- Include AuthInitializer in providers.tsx
- Update middleware: redirect / → /dashboard
- Update middleware: unauthenticated /dashboard/* → /login
- Update middleware: authenticated /login|/register → /dashboard
Closes #448
---
frontend/app/providers.tsx | 6 +++-
frontend/components/auth/AuthInitializer.tsx | 12 ++++++++
frontend/middleware.ts | 30 ++++++++++++++------
frontend/store/auth.store.ts | 26 +++++++++++++++--
4 files changed, 62 insertions(+), 12 deletions(-)
create mode 100644 frontend/components/auth/AuthInitializer.tsx
diff --git a/frontend/app/providers.tsx b/frontend/app/providers.tsx
index b79ccdec..22f8b8d6 100644
--- a/frontend/app/providers.tsx
+++ b/frontend/app/providers.tsx
@@ -2,6 +2,7 @@
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { AuthInitializer } from '@/components/auth/AuthInitializer';
const queryClient = new QueryClient({
defaultOptions: {
@@ -14,6 +15,9 @@ const queryClient = new QueryClient({
export function Providers({ children }: { children: React.ReactNode }) {
return (
- {children}
+
+
+ {children}
+
);
}
diff --git a/frontend/components/auth/AuthInitializer.tsx b/frontend/components/auth/AuthInitializer.tsx
new file mode 100644
index 00000000..94d4a2d2
--- /dev/null
+++ b/frontend/components/auth/AuthInitializer.tsx
@@ -0,0 +1,12 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useAuthStore } from '@/store/auth.store';
+
+export function AuthInitializer() {
+ useEffect(() => {
+ useAuthStore.getState().initialize();
+ }, []);
+
+ return null;
+}
diff --git a/frontend/middleware.ts b/frontend/middleware.ts
index b51d2dfc..6d1b9a1c 100644
--- a/frontend/middleware.ts
+++ b/frontend/middleware.ts
@@ -1,34 +1,46 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
-const PROTECTED = [ '/assets', '/departments', '/users'];
+
+const PROTECTED_ROUTES = ['/dashboard', '/assets', '/departments', '/users', '/reports', '/settings'];
const AUTH_PAGES = ['/login', '/register'];
-export const middleware = (req: NextRequest) => {
+export function middleware(req: NextRequest) {
const token = req.cookies.get('auth-token')?.value;
const { pathname } = req.nextUrl;
- const isProtected = PROTECTED.some((route) => pathname.startsWith(route));
- const isAuthPage = AUTH_PAGES.includes(pathname);
+ const isAuthenticated = !!token;
+ const isProtectedRoute = PROTECTED_ROUTES.some((route) => pathname.startsWith(route));
+ const isAuthPage = AUTH_PAGES.some((route) => pathname.startsWith(route));
+
+ // Redirect root to dashboard
+ if (pathname === '/') {
+ return NextResponse.redirect(new URL('/dashboard', req.url));
+ }
- if (isProtected && !token) {
+ // Redirect unauthenticated users from protected routes to login
+ if (isProtectedRoute && !isAuthenticated) {
const url = new URL('/login', req.url);
url.searchParams.set('redirect', pathname);
return NextResponse.redirect(url);
}
- // if (isAuthPage && token) {
- // return NextResponse.redirect(new URL('/dashboard', req.url));
- // }
+ // Redirect authenticated users away from auth pages to dashboard
+ if (isAuthPage && isAuthenticated) {
+ return NextResponse.redirect(new URL('/dashboard', req.url));
+ }
return NextResponse.next();
-};
+}
export const config = {
matcher: [
+ '/',
'/dashboard/:path*',
'/assets/:path*',
'/departments/:path*',
'/users/:path*',
+ '/reports/:path*',
+ '/settings/:path*',
'/login',
'/register',
],
diff --git a/frontend/store/auth.store.ts b/frontend/store/auth.store.ts
index 99499640..32153a1f 100644
--- a/frontend/store/auth.store.ts
+++ b/frontend/store/auth.store.ts
@@ -14,8 +14,10 @@ interface AuthStore {
user: User | null;
token: string | null;
isAuthenticated: boolean;
+ isInitialized: boolean;
setAuth: (token: string, user: User) => void;
logout: () => void;
+ initialize: () => void;
loadAuthFromStorage: () => void;
}
@@ -25,6 +27,7 @@ export const useAuthStore = create()(
user: null,
token: null,
isAuthenticated: false,
+ isInitialized: false,
setAuth: (token: string, user: User) => {
// Store token in localStorage for API calls
@@ -52,11 +55,29 @@ export const useAuthStore = create()(
});
},
+ initialize: () => {
+ const { isInitialized } = get();
+ if (isInitialized) return;
+
+ const token = localStorage.getItem('token');
+ if (token) {
+ const user = JSON.parse(localStorage.getItem('user') || 'null');
+ if (user) {
+ set({
+ token,
+ user,
+ isAuthenticated: true,
+ isInitialized: true,
+ });
+ return;
+ }
+ }
+ set({ isInitialized: true });
+ },
+
loadAuthFromStorage: () => {
const token = localStorage.getItem('token');
if (token) {
- // For now, we'll need to validate the token or decode it
- // In a real app, you might want to validate the token on app load
const user = JSON.parse(localStorage.getItem('user') || 'null');
if (user) {
set({
@@ -74,6 +95,7 @@ export const useAuthStore = create()(
user: state.user,
token: state.token,
isAuthenticated: state.isAuthenticated,
+ isInitialized: state.isInitialized,
}),
}
)