Downgrade to NextAuth v4 to fix constructor error

This commit is contained in:
2025-12-02 13:56:57 +00:00
parent 60ebf13a9c
commit fe783301e7
6 changed files with 53 additions and 213 deletions

View File

@@ -1,3 +0,0 @@
import { handlers } from "@/auth"
export const { GET, POST } = handlers

View File

@@ -1,73 +0,0 @@
import type { NextAuthConfig } from "next-auth"
export const authConfig = {
trustHost: true,
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days in seconds
updateAge: 24 * 60 * 60, // Update session every 24 hours
},
cookies: {
sessionToken: {
name: 'next-auth.session-token',
options: {
httpOnly: true, // Prevent JavaScript access to cookies (XSS protection)
sameSite: 'lax', // CSRF protection - prevents cookie from being sent in cross-site requests
path: '/',
secure: false, // Set to false for HTTP, true only if using HTTPS
},
},
},
pages: {
signIn: "/auth/login",
signOut: "/auth/logout",
error: "/auth/error",
},
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user
// Define protected routes
const isProtectedRoute =
nextUrl.pathname === "/" ||
nextUrl.pathname.startsWith("/list") ||
nextUrl.pathname.startsWith("/profile") ||
nextUrl.pathname.startsWith("/invitations")
// Define auth pages (login, register, etc.)
const isAuthPage = nextUrl.pathname.startsWith("/auth")
// Allow access to auth pages regardless of authentication status
if (isAuthPage) {
// If already logged in and trying to access login/register, redirect to home
if (isLoggedIn && (nextUrl.pathname === "/auth/login" || nextUrl.pathname === "/auth/register")) {
return Response.redirect(new URL("/", nextUrl))
}
return true
}
// Redirect to login if accessing protected route without authentication
if (isProtectedRoute && !isLoggedIn) {
return false // This will redirect to signIn page
}
// Allow access to public routes
return true
},
async jwt({ token, user }) {
// Add user ID to token on sign in
if (user) {
token.id = user.id
}
return token
},
async session({ session, token }) {
// Add user ID to session from token
if (session.user && token.id) {
session.user.id = token.id as string
}
return session
},
},
providers: [], // Providers will be added in auth.ts
} satisfies NextAuthConfig

59
auth.ts
View File

@@ -1,59 +0,0 @@
import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
import { authConfig } from "@/auth.config"
import { prisma } from "@/lib/db"
import bcrypt from "bcryptjs"
import { z } from "zod"
// Validation schemas
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(1),
})
export const { handlers, signIn, signOut, auth } = NextAuth({
...authConfig,
trustHost: true,
providers: [
Credentials({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
try {
// Validate credentials format
const { email, password } = loginSchema.parse(credentials)
// Query user from database
const user = await prisma.user.findUnique({
where: { email },
})
// Check if user exists
if (!user || !user.passwordHash) {
return null
}
// Verify password with bcrypt
const isPasswordValid = await bcrypt.compare(password, user.passwordHash)
if (!isPasswordValid) {
return null
}
// Return user object (without password hash)
return {
id: user.id,
email: user.email,
name: user.name,
}
} catch (error) {
console.error("Authorization error:", error)
return null
}
},
}),
],
})

View File

@@ -1,77 +0,0 @@
import NextAuth from "next-auth"
import { authConfig } from "@/auth.config"
/**
* Middleware for session validation, route protection, and security headers
*
* This middleware:
* - Validates session tokens on protected routes
* - Redirects unauthenticated users to login
* - Handles session expiration
* - Allows access to auth pages for unauthenticated users
* - Adds security headers (CSRF protection, XSS protection, etc.)
*
* Requirements: 3.3, 3.5, 9.4 (CSRF protection), 9.5 (security headers)
*/
const { auth } = NextAuth(authConfig)
export default auth((req) => {
// The authorization logic is handled in auth.config.ts authorized callback
// Here we just add security headers
const requestHeaders = new Headers(req.headers)
// Add security headers - Requirement 9.4, 9.5
// CSRF Protection - SameSite cookie attribute is set in auth.ts
// This header provides additional CSRF protection
requestHeaders.set('X-Content-Type-Options', 'nosniff')
// XSS Protection
requestHeaders.set('X-XSS-Protection', '1; mode=block')
// Prevent clickjacking attacks
requestHeaders.set('X-Frame-Options', 'DENY')
// Referrer Policy - don't leak referrer information
requestHeaders.set('Referrer-Policy', 'strict-origin-when-cross-origin')
// Content Security Policy - helps prevent XSS attacks
// Note: This is a basic CSP. You may need to adjust based on your needs
const csp = [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'", // unsafe-inline and unsafe-eval needed for Next.js
"style-src 'self' 'unsafe-inline'", // unsafe-inline needed for Tailwind
"img-src 'self' data: blob:",
"font-src 'self' data:",
"connect-src 'self'",
"frame-ancestors 'none'",
].join('; ')
requestHeaders.set('Content-Security-Policy', csp)
// Permissions Policy - restrict access to browser features
requestHeaders.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')
return undefined // Let NextAuth handle the response
})
/**
* Matcher configuration for middleware
* Applies middleware to all routes except:
* - API routes (handled separately)
* - Static files (_next/static)
* - Image optimization (_next/image)
* - Favicon and other public assets
*/
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public assets (images, icons, manifest, sw.js)
*/
"/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|json|js)$).*)",
],
}

View File

@@ -22,7 +22,7 @@
"lucide-react": "^0.294.0",
"mysql2": "^3.15.3",
"next": "14.0.4",
"next-auth": "^5.0.0-beta.30",
"next-auth": "^4.24.5",
"react": "^18",
"react-dom": "^18",
"zod": "^4.1.13"

View File

@@ -0,0 +1,52 @@
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import { prisma } from "@/lib/db"
import bcrypt from "bcryptjs"
import { z } from "zod"
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(1),
})
export default NextAuth({
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
try {
const { email, password } = loginSchema.parse(credentials)
const user = await prisma.user.findUnique({ where: { email } })
if (!user || !user.passwordHash) return null
const isValid = await bcrypt.compare(password, user.passwordHash)
if (!isValid) return null
return { id: user.id, email: user.email, name: user.name }
} catch {
return null
}
},
}),
],
session: { strategy: "jwt" },
pages: {
signIn: "/auth/login",
error: "/auth/error",
},
callbacks: {
async jwt({ token, user }) {
if (user) token.id = user.id
return token
},
async session({ session, token }) {
if (token.id) session.user.id = token.id
return session
},
},
})