Skip to content

Commit 69b426a

Browse files
Merge pull request #319 from canhamzacode/main
FRONTEND: Implement Middleware for Route Protection in Next.js
2 parents 933a565 + 5d193b4 commit 69b426a

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

frontend/doc/auth-middleware.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Authentication Middleware
2+
3+
Simple route protection for AssetsUp. Redirects unauthenticated users to `/signin` and preserves their intended destination.
4+
5+
## Quick Start
6+
7+
The middleware checks for an `auth-token` cookie and protects routes listed in the `PROTECTED` array.
8+
9+
```typescript
10+
const PROTECTED = ['/dashboard', '/assets', '/departments', '/users'];
11+
const AUTH_PAGES = ['/signin', '/signup'];
12+
```
13+
14+
## How It Works
15+
16+
1. Protected route + no token → Redirect to `/signin?redirect={pathname}`
17+
2. Auth page + has token → Redirect to `/dashboard`
18+
3. Everything else → Allow
19+
20+
## Adding Routes
21+
22+
**New protected route:**
23+
24+
```typescript
25+
const PROTECTED = ['/dashboard', '/assets', '/reports']; // Add here
26+
27+
export const config = {
28+
matcher: ['/dashboard/:path*', '/assets/:path*', '/reports/:path*'], // Add here
29+
};
30+
```
31+
32+
**New auth page:**
33+
34+
```typescript
35+
const AUTH_PAGES = ['/signin', '/signup', '/forgot-password']; // Add here
36+
37+
export const config = {
38+
matcher: [..., '/forgot-password'], // Add here
39+
};
40+
```
41+
42+
## Using Redirect in Login Page
43+
44+
```typescript
45+
'use client';
46+
import { useRouter, useSearchParams } from 'next/navigation';
47+
48+
export default function SignInPage() {
49+
const router = useRouter();
50+
const redirectUrl = useSearchParams().get('redirect') || '/dashboard';
51+
52+
const handleLogin = async () => {
53+
// ... login logic
54+
router.push(redirectUrl);
55+
router.refresh(); // Important: refresh to update middleware state
56+
};
57+
}
58+
```
59+
60+
## Setting the Auth Token
61+
62+
```typescript
63+
// app/api/auth/login/route.ts
64+
import { cookies } from 'next/headers';
65+
66+
cookies().set('auth-token', token, {
67+
httpOnly: true,
68+
secure: process.env.NODE_ENV === 'production',
69+
sameSite: 'lax',
70+
maxAge: 60 * 60 * 24 * 7, // 7 days
71+
path: '/',
72+
});
73+
```
74+
75+
## Logout
76+
77+
```typescript
78+
// app/api/auth/logout/route.ts
79+
import { cookies } from 'next/headers';
80+
81+
cookies().delete('auth-token');
82+
```
83+
84+
## Testing Checklist
85+
86+
- [ ] `/dashboard` without token → redirects to `/signin?redirect=/dashboard`
87+
- [ ] `/dashboard` with token → loads successfully
88+
- [ ] `/signin` with token → redirects to `/dashboard`
89+
- [ ] Login → redirects to original destination

frontend/middleware.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { NextResponse } from 'next/server';
2+
import type { NextRequest } from 'next/server';
3+
const PROTECTED = ['/dashboard', '/assets', '/departments', '/users'];
4+
const AUTH_PAGES = ['/signin', '/signup'];
5+
6+
export const middleware = (req: NextRequest) => {
7+
const token = req.cookies.get('auth-token')?.value;
8+
const { pathname } = req.nextUrl;
9+
10+
const isProtected = PROTECTED.some((route) => pathname.startsWith(route));
11+
const isAuthPage = AUTH_PAGES.includes(pathname);
12+
13+
if (isProtected && !token) {
14+
const url = new URL('/signin', req.url);
15+
url.searchParams.set('redirect', pathname);
16+
return NextResponse.redirect(url);
17+
}
18+
19+
if (isAuthPage && token) {
20+
return NextResponse.redirect(new URL('/dashboard', req.url));
21+
}
22+
23+
return NextResponse.next();
24+
};
25+
26+
export const config = {
27+
matcher: [
28+
'/dashboard/:path*',
29+
'/assets/:path*',
30+
'/departments/:path*',
31+
'/users/:path*',
32+
'/signin',
33+
'/signup',
34+
],
35+
};

0 commit comments

Comments
 (0)