diff --git a/next.config.ts b/next.config.ts
index 2e27cc2..3850d87 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,4 +1,10 @@
import type { NextConfig } from "next"
+import crypto from "crypto"
+
+// Generate a random JWT secret on every dev server start to invalidate all prior sessions
+if (process.env.NODE_ENV !== "production") {
+ process.env.JWT_SECRET = crypto.randomUUID()
+}
const nextConfig: NextConfig = {
eslint: {
diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx
index 53f5638..24cf19f 100644
--- a/src/app/(dashboard)/layout.tsx
+++ b/src/app/(dashboard)/layout.tsx
@@ -1,5 +1,7 @@
"use client"
+import { useEffect } from "react"
+import { useRouter } from "next/navigation"
import { AppShell } from "@/components/layout/app-shell"
import { UserProvider, useUser } from "@/providers/user-provider"
import { NotificationProvider } from "@/providers/notification-provider"
@@ -8,8 +10,15 @@ import { Loader2 } from "lucide-react"
function DashboardContent({ children }: { children: React.ReactNode }) {
const { user, loading } = useUser()
+ const router = useRouter()
- if (loading) {
+ useEffect(() => {
+ if (!user && !loading) {
+ router.push("/login")
+ }
+ }, [user, loading, router])
+
+ if (loading || !user) {
return (
@@ -17,8 +26,6 @@ function DashboardContent({ children }: { children: React.ReactNode }) {
)
}
- if (!user) return null
-
return
{children}
}
diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts
index 5f24e89..2d23832 100644
--- a/src/app/api/auth/login/route.ts
+++ b/src/app/api/auth/login/route.ts
@@ -10,6 +10,7 @@ import {
isAccountLocked,
createSession,
setSessionContext,
+ SESSION_COOKIE,
} from "@/lib/auth"
export async function POST(request: NextRequest) {
@@ -106,12 +107,19 @@ export async function POST(request: NextRequest) {
true
)
- await createSession(dbUser.id, dbUser.role_name)
+ const token = await createSession(dbUser.id, dbUser.role_name)
await setSessionContext(dbUser.id, ipAddress)
const user = mapDbUserToSessionUser(dbUser)
- return NextResponse.json({ user }, { status: 200 })
+ const response = NextResponse.json({ user }, { status: 200 })
+ response.cookies.set(SESSION_COOKIE, token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "strict",
+ path: "/",
+ })
+ return response
} catch (error) {
console.error("Login error:", error)
return NextResponse.json(
diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts
index fb16836..f510ec9 100644
--- a/src/app/api/auth/logout/route.ts
+++ b/src/app/api/auth/logout/route.ts
@@ -1,10 +1,17 @@
import { NextResponse } from "next/server"
-import { destroySession } from "@/lib/auth"
+import { SESSION_COOKIE } from "@/lib/auth"
export async function POST() {
try {
- await destroySession()
- return NextResponse.json({ success: true }, { status: 200 })
+ 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 response
} catch (error) {
console.error("Logout error:", error)
return NextResponse.json(
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index c1ebbef..587932b 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,6 +1,6 @@
"use client"
-import { useState, useEffect, useRef } from "react"
+import { useState, useEffect, useRef, Suspense } from "react"
import { useSearchParams, useRouter } from "next/navigation"
import { Eye, EyeOff, Loader2 } from "lucide-react"
@@ -13,7 +13,20 @@ const waves = [
]
export default function LoginPage() {
+ return (
+
+
+
+ }>
+