Repository navigation
Expand file tree
/
Copy pathmiddleware.ts
More file actions
50 lines (41 loc) · 1.75 KB
/
Copy pathmiddleware.ts
File metadata and controls
50 lines (41 loc) · 1.75 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
// #138 — Middleware guards all dashboard and profile routes.
// Previous version only checked /dashboard and redirected to / with no return
// URL, meaning users had to navigate back manually after login.
//
// Fixes:
// • /profile routes are now protected (added to matcher and check)
// • Redirect target is /login (not /) so the login page receives the intent
// • `returnTo` query param preserves the original URL for post-login redirect
// • Expired JWTs are detected by checking the `exp` claim in the cookie value
// so stale tokens don't grant access to protected pages
import { NextRequest, NextResponse } from 'next/server';
const PROTECTED_PREFIXES = ['/dashboard', '/profile'];
const LOGIN_PATH = '/';
function isExpiredJwt(tokenValue: string): boolean {
try {
const parts = tokenValue.split('.');
if (parts.length !== 3) return true;
const payload = JSON.parse(atob(parts[1]));
if (typeof payload.exp !== 'number') return false;
return Date.now() / 1000 > payload.exp;
} catch {
return true;
}
}
export default function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
const isProtected = PROTECTED_PREFIXES.some((prefix) => pathname.startsWith(prefix));
if (!isProtected) return NextResponse.next();
const tokenCookie = req.cookies.get('audioblocks_jwt');
const isAuthenticated = tokenCookie && !isExpiredJwt(tokenCookie.value);
if (!isAuthenticated) {
const loginUrl = new URL(LOGIN_PATH, req.url);
loginUrl.searchParams.set('returnTo', pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
export const config = {
// Literal array required — Next.js extracts this at build time (#138).
matcher: ['/dashboard/:path*', '/profile/:path*'],
};