Files
cvfs/apps/webapp/src/middleware.ts
Claude 8d72dfa09d fix(webapp): use Web Crypto API in middleware and drop node: prefix in auth route
Middleware runs in Edge Runtime (no Node.js built-ins), so use
globalThis.crypto.subtle for HMAC verification. Route handler uses
`import { createHmac } from 'crypto'` without the node: prefix
which webpack cannot resolve during Next.js build.

https://claude.ai/code/session_01CdisLhbC2kVt2hxfJ7TNPf
2026-04-03 13:59:09 +00:00

30 lines
1.3 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
const SECRET = process.env.SESSION_SECRET ?? 'dev-secret-change-in-production';
async function verifySession(token: string): Promise<boolean> {
const lastDot = token.lastIndexOf('.');
if (lastDot === -1) return false;
const payload = token.slice(0, lastDot);
const sigHex = token.slice(lastDot + 1);
try {
const key = await globalThis.crypto.subtle.importKey(
'raw', new TextEncoder().encode(SECRET),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['verify'],
);
const sigBytes = new Uint8Array((sigHex.match(/.{1,2}/g) ?? []).map(b => parseInt(b, 16)));
return await globalThis.crypto.subtle.verify('HMAC', key, sigBytes, new TextEncoder().encode(payload));
} catch { return false; }
}
export async function middleware(req: NextRequest) {
if (!req.nextUrl.pathname.startsWith('/dashboard')) return NextResponse.next();
const session = req.cookies.get('session')?.value;
const oidc = req.cookies.get('oidc_token')?.value;
if (oidc) return NextResponse.next();
if (session && await verifySession(session)) return NextResponse.next();
return NextResponse.redirect(new URL('/login', req.url));
}
export const config = { matcher: ['/dashboard/:path*'] };