| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- // lib/auth/session.js
- import { cookies } from "next/headers";
- import { SignJWT, jwtVerify } from "jose";
- export const SESSION_COOKIE_NAME = "auth_session";
- export const SESSION_MAX_AGE_SECONDS = 60 * 60 * 8; // 8 hours
- function getSessionSecretKey() {
- const secret = process.env.SESSION_SECRET;
- if (!secret) {
- throw new Error("SESSION_SECRET environment variable is not set");
- }
- return new TextEncoder().encode(secret);
- }
- /**
- * Resolve whether the session cookie should be marked as "Secure".
- *
- * Default:
- * - Secure in production (`NODE_ENV=production`)
- *
- * Override (useful for local HTTP testing):
- * - SESSION_COOKIE_SECURE=false
- * - SESSION_COOKIE_SECURE=true
- */
- function resolveCookieSecureFlag() {
- const raw = (process.env.SESSION_COOKIE_SECURE || "").trim().toLowerCase();
- if (raw === "true") return true;
- if (raw === "false") return false;
- return process.env.NODE_ENV === "production";
- }
- /**
- * Create a signed session JWT and store it in a HTTP-only cookie.
- */
- export async function createSession({ userId, role, branchId }) {
- if (!userId || !role) {
- throw new Error("createSession requires userId and role");
- }
- const payload = {
- userId,
- role,
- branchId: branchId ?? null,
- };
- const jwt = await new SignJWT(payload)
- .setProtectedHeader({ alg: "HS256", typ: "JWT" })
- .setIssuedAt()
- .setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`)
- .sign(getSessionSecretKey());
- const cookieStore = await cookies();
- cookieStore.set(SESSION_COOKIE_NAME, jwt, {
- httpOnly: true,
- secure: resolveCookieSecureFlag(),
- sameSite: "lax",
- path: "/",
- maxAge: SESSION_MAX_AGE_SECONDS,
- });
- return jwt;
- }
- /**
- * Read the current session from the HTTP-only cookie.
- */
- export async function getSession() {
- const cookieStore = await cookies();
- const cookie = cookieStore.get(SESSION_COOKIE_NAME);
- if (!cookie?.value) {
- return null;
- }
- const secretKey = getSessionSecretKey();
- try {
- const { payload } = await jwtVerify(cookie.value, secretKey);
- const { userId, role, branchId } = payload;
- if (typeof userId !== "string" || typeof role !== "string") {
- return null;
- }
- return {
- userId,
- role,
- branchId: typeof branchId === "string" ? branchId : null,
- };
- } catch {
- // Invalid or expired token: clear cookie and return null
- const store = await cookies();
- store.set(SESSION_COOKIE_NAME, "", {
- httpOnly: true,
- secure: resolveCookieSecureFlag(),
- sameSite: "lax",
- path: "/",
- maxAge: 0,
- });
- return null;
- }
- }
- /**
- * Destroy the current session by clearing the session cookie.
- */
- export async function destroySession() {
- const cookieStore = await cookies();
- cookieStore.set(SESSION_COOKIE_NAME, "", {
- httpOnly: true,
- secure: resolveCookieSecureFlag(),
- sameSite: "lax",
- path: "/",
- maxAge: 0,
- });
- }
|