lomavuokraus/lib/jwt.ts
Tero Halla-aho 0bb709d9c5
Some checks failed
CI / checks (push) Has been cancelled
chore: fix audit alerts and formatting
2026-02-04 12:43:03 +02:00

79 lines
2.1 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;";
}