diff --git a/frontend/doc/auth-middleware.md b/frontend/doc/auth-middleware.md new file mode 100644 index 0000000..0424798 --- /dev/null +++ b/frontend/doc/auth-middleware.md @@ -0,0 +1,89 @@ +# Authentication Middleware + +Simple route protection for AssetsUp. Redirects unauthenticated users to `/signin` and preserves their intended destination. + +## Quick Start + +The middleware checks for an `auth-token` cookie and protects routes listed in the `PROTECTED` array. + +```typescript +const PROTECTED = ['/dashboard', '/assets', '/departments', '/users']; +const AUTH_PAGES = ['/signin', '/signup']; +``` + +## How It Works + +1. Protected route + no token → Redirect to `/signin?redirect={pathname}` +2. Auth page + has token → Redirect to `/dashboard` +3. Everything else → Allow + +## Adding Routes + +**New protected route:** + +```typescript +const PROTECTED = ['/dashboard', '/assets', '/reports']; // Add here + +export const config = { + matcher: ['/dashboard/:path*', '/assets/:path*', '/reports/:path*'], // Add here +}; +``` + +**New auth page:** + +```typescript +const AUTH_PAGES = ['/signin', '/signup', '/forgot-password']; // Add here + +export const config = { + matcher: [..., '/forgot-password'], // Add here +}; +``` + +## Using Redirect in Login Page + +```typescript +'use client'; +import { useRouter, useSearchParams } from 'next/navigation'; + +export default function SignInPage() { + const router = useRouter(); + const redirectUrl = useSearchParams().get('redirect') || '/dashboard'; + + const handleLogin = async () => { + // ... login logic + router.push(redirectUrl); + router.refresh(); // Important: refresh to update middleware state + }; +} +``` + +## Setting the Auth Token + +```typescript +// app/api/auth/login/route.ts +import { cookies } from 'next/headers'; + +cookies().set('auth-token', token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 7, // 7 days + path: '/', +}); +``` + +## Logout + +```typescript +// app/api/auth/logout/route.ts +import { cookies } from 'next/headers'; + +cookies().delete('auth-token'); +``` + +## Testing Checklist + +- [ ] `/dashboard` without token → redirects to `/signin?redirect=/dashboard` +- [ ] `/dashboard` with token → loads successfully +- [ ] `/signin` with token → redirects to `/dashboard` +- [ ] Login → redirects to original destination diff --git a/frontend/middleware.ts b/frontend/middleware.ts new file mode 100644 index 0000000..7bbf1a3 --- /dev/null +++ b/frontend/middleware.ts @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +const PROTECTED = ['/dashboard', '/assets', '/departments', '/users']; +const AUTH_PAGES = ['/signin', '/signup']; + +export const 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); + + if (isProtected && !token) { + const url = new URL('/signin', req.url); + url.searchParams.set('redirect', pathname); + return NextResponse.redirect(url); + } + + if (isAuthPage && token) { + return NextResponse.redirect(new URL('/dashboard', req.url)); + } + + return NextResponse.next(); +}; + +export const config = { + matcher: [ + '/dashboard/:path*', + '/assets/:path*', + '/departments/:path*', + '/users/:path*', + '/signin', + '/signup', + ], +};