forked from MentoNest/skillsync_frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.ts
More file actions
95 lines (84 loc) · 3.49 KB
/
Copy pathmiddleware.ts
File metadata and controls
95 lines (84 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { NextRequest, NextResponse } from 'next/server';
import {
SESSION_COOKIE,
isProtectedRoute,
isAuthRoute,
getDashboardPath,
} from '@/lib/auth';
/**
* middleware.ts
*
* Runs at the edge before every matched request.
*
* Responsibilities:
* 1. Unauthenticated users hitting a protected route → redirect to /login
* 2. Authenticated users hitting an auth route (/login, /register)
* → redirect to their role dashboard (prevents double-login)
* 3. Every other request passes through unchanged.
*
* Session format (stored in the HTTP-only cookie `skillsync_session`):
* Base64-encoded JSON: { role: 'mentor' | 'mentee' | 'admin' }
*
* The LoginPage sets this cookie after successful auth so the middleware
* can read it on the server without exposing it to client JS.
*/
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Read the session cookie (set by the login API / server action)
const sessionCookie = request.cookies.get(SESSION_COOKIE)?.value ?? null;
// Parse role from session — null if absent or malformed
let role: string | null = null;
if (sessionCookie) {
try {
const decoded = Buffer.from(sessionCookie, 'base64').toString('utf-8');
const parsed = JSON.parse(decoded) as { role?: string };
role = parsed.role ?? null;
} catch {
// Malformed cookie — treat as unauthenticated
}
}
const isAuthenticated = role !== null;
// ── 1. Protect dashboard routes ──────────────────────────────────────────
if (isProtectedRoute(pathname) && !isAuthenticated) {
const loginUrl = request.nextUrl.clone();
loginUrl.pathname = '/login';
// Preserve the original destination so we can redirect back after login
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
// ── 2. Redirect authenticated users away from auth pages ─────────────────
if (isAuthRoute(pathname) && isAuthenticated) {
const dashboardUrl = request.nextUrl.clone();
dashboardUrl.pathname = getDashboardPath(role!);
dashboardUrl.search = '';
return NextResponse.redirect(dashboardUrl);
}
// ── 3. Role mismatch guard ────────────────────────────────────────────────
// Prevent a mentee from accessing /mentor, a mentor from /admin, etc.
if (isAuthenticated && role) {
const dashboard = getDashboardPath(role);
// Only enforce if the path is a protected route that doesn't belong to this role
const mismatch =
isProtectedRoute(pathname) && !pathname.startsWith(dashboard);
if (mismatch) {
const correctedUrl = request.nextUrl.clone();
correctedUrl.pathname = dashboard;
correctedUrl.search = '';
return NextResponse.redirect(correctedUrl);
}
}
return NextResponse.next();
}
// ─── Matcher ──────────────────────────────────────────────────────────────────
// Apply the middleware only to routes that need auth logic.
// Static assets, API routes, and Next.js internals are excluded.
export const config = {
matcher: [
'/mentor/:path*',
'/mentee/:path*',
'/admin/:path*',
'/dashboard/:path*',
'/login',
'/register',
],
};