lomavuokraus/lib/jwt.ts
2025-12-20 19:18:41 +02:00

66 lines
2 KiB
TypeScript

import { SignJWT, jwtVerify } from 'jose';
import { NextRequest } from 'next/server';
const ALGORITHM = 'HS256';
const TOKEN_EXP_HOURS = 24;
function getSecret() {
const secret = process.env.AUTH_SECRET || 'dev-auth-secret';
return new TextEncoder().encode(secret);
}
export async function signAccessToken(payload: { userId: string; role: string }) {
const secret = getSecret();
const exp = Math.floor(Date.now() / 1000) + TOKEN_EXP_HOURS * 3600;
return new SignJWT(payload).setProtectedHeader({ alg: ALGORITHM }).setExpirationTime(exp).sign(secret);
}
export async function verifyAccessToken(token: string) {
const secret = getSecret();
const { payload } = await jwtVerify(token, secret, { algorithms: [ALGORITHM] });
return payload as { userId: string; role: string };
}
export async function getAuthFromRequest(request: Request | NextRequest) {
let token: string | null = null;
const header = request.headers.get('authorization');
if (header?.startsWith('Bearer ')) {
token = header.slice('Bearer '.length);
}
if (!token) {
const cookieHeader = request.headers.get('cookie') ?? '';
const match = cookieHeader.split(';').map((c) => c.trim()).find((c) => c.startsWith('session_token='));
if (match) {
token = decodeURIComponent(match.split('=')[1]);
}
}
if (!token) {
return null;
}
try {
return await verifyAccessToken(token);
} catch {
return null;
}
}
export async function requireAuth(request: Request | NextRequest) {
const auth = await getAuthFromRequest(request);
if (!auth) {
throw new Error('Unauthorized');
}
return auth;
}
export function buildSessionCookie(token: string) {
const secure = process.env.APP_URL?.startsWith('https://') || process.env.NODE_ENV === 'production';
const maxAge = TOKEN_EXP_HOURS * 3600;
return `session_token=${encodeURIComponent(token)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${maxAge};${secure ? ' Secure;' : ''}`;
}
export function clearSessionCookie() {
return 'session_token=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0;';
}