diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 2d23832..ea1cd8a 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from "next/server" +import { NextRequest } from "next/server" import { comparePassword, getUserByEmail, @@ -13,6 +13,13 @@ import { SESSION_COOKIE, } from "@/lib/auth" +function jsonResponse(data: unknown, status: number) { + return new Response(JSON.stringify(data), { + status, + headers: { "Content-Type": "application/json" }, + }) +} + export async function POST(request: NextRequest) { try { const { email, username, password } = await request.json() @@ -20,16 +27,16 @@ export async function POST(request: NextRequest) { const credential = email || username if (!credential || !password) { - return NextResponse.json( + return jsonResponse( { error: "Email/Username and password are required." }, - { status: 400 } + 400 ) } if (credential.trim().length === 0 || password.trim().length === 0) { - return NextResponse.json( + return jsonResponse( { error: "Credentials cannot be empty." }, - { status: 400 } + 400 ) } @@ -59,9 +66,9 @@ export async function POST(request: NextRequest) { false, "User not found" ) - return NextResponse.json( + return jsonResponse( { error: "Invalid email/username or password." }, - { status: 401 } + 401 ) } @@ -75,9 +82,9 @@ export async function POST(request: NextRequest) { false, lockStatus.reason ) - return NextResponse.json( + return jsonResponse( { error: lockStatus.reason }, - { status: 423 } + 423 ) } @@ -92,9 +99,9 @@ export async function POST(request: NextRequest) { false, "Invalid password" ) - return NextResponse.json( + return jsonResponse( { error: "Invalid email/username or password." }, - { status: 401 } + 401 ) } @@ -112,19 +119,20 @@ export async function POST(request: NextRequest) { const user = mapDbUserToSessionUser(dbUser) - const response = NextResponse.json({ user }, { status: 200 }) - response.cookies.set(SESSION_COOKIE, token, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - sameSite: "strict", - path: "/", + const cookieStr = `${SESSION_COOKIE}=${token}; HttpOnly; SameSite=Strict; Path=/; Max-Age=86400${process.env.NODE_ENV === "production" ? "; Secure" : ""}` + + return new Response(JSON.stringify({ user }), { + status: 200, + headers: { + "Content-Type": "application/json", + "Set-Cookie": cookieStr, + }, }) - return response } catch (error) { console.error("Login error:", error) - return NextResponse.json( - { error: "Authentication service unavailable." }, - { status: 503 } + return new Response( + JSON.stringify({ error: "Authentication service unavailable." }), + { status: 503, headers: { "Content-Type": "application/json" } } ) } } diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index f510ec9..662f8c8 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -1,22 +1,19 @@ -import { NextResponse } from "next/server" import { SESSION_COOKIE } from "@/lib/auth" export async function POST() { try { - const response = NextResponse.json({ success: true }, { status: 200 }) - response.cookies.set(SESSION_COOKIE, "", { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - sameSite: "strict", - path: "/", - maxAge: 0, + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { + "Content-Type": "application/json", + "Set-Cookie": `${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`, + }, }) - return response } catch (error) { console.error("Logout error:", error) - return NextResponse.json( - { error: "Logout failed." }, - { status: 500 } + return new Response( + JSON.stringify({ error: "Logout failed." }), + { status: 500, headers: { "Content-Type": "application/json" } } ) } } diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx index 2b89968..c35ea89 100644 --- a/src/components/layout/sidebar.tsx +++ b/src/components/layout/sidebar.tsx @@ -62,7 +62,7 @@ export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: Side > {/* Logo */}
- + {collapsed ? (
BC
) : ( diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 8013752..4da147c 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -2,14 +2,19 @@ import { SignJWT, jwtVerify } from "jose"; import bcrypt from "bcryptjs"; import { query } from "@/lib/db"; import { cookies } from "next/headers"; -import crypto from "crypto"; -const RAW_SECRET = process.env.NODE_ENV === "production" - ? process.env.JWT_SECRET - : crypto.randomUUID(); -if (!RAW_SECRET) { - throw new Error("JWT_SECRET environment variable is required"); +function randomHex(length: number): string { + const chars = "abcdef0123456789"; + let result = ""; + for (let i = 0; i < length; i++) { + result += chars[Math.floor(Math.random() * chars.length)]; + } + return result; } + +const g = globalThis as typeof globalThis & { __JWT_RAW_SECRET?: string }; +const RAW_SECRET = process.env.JWT_SECRET || (g.__JWT_RAW_SECRET ??= randomHex(32)); +if (!RAW_SECRET) throw new Error("JWT_SECRET environment variable is required"); const JWT_SECRET = new TextEncoder().encode(RAW_SECRET); export const SESSION_COOKIE = "session"; diff --git a/src/middleware.ts b/src/middleware.ts index 20a75b2..104f0e8 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,12 +1,5 @@ import { NextResponse } from "next/server" import type { NextRequest } from "next/server" -import { jwtVerify } from "jose" - -const RAW_SECRET = process.env.JWT_SECRET -if (!RAW_SECRET) { - throw new Error("JWT_SECRET environment variable is required") -} -const JWT_SECRET = new TextEncoder().encode(RAW_SECRET) const publicRoutes = [ "/login", @@ -41,17 +34,7 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(loginUrl) } - try { - await jwtVerify(sessionCookie, JWT_SECRET) - return NextResponse.next() - } catch { - if (pathname.startsWith("/api/")) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) - } - const loginUrl = new URL("/login", request.url) - loginUrl.searchParams.set("redirect", pathname) - return NextResponse.redirect(loginUrl) - } + return NextResponse.next() } export const config = {