feat: upgrade next-auth to v5.0.0-beta.29 and refactor authentication middleware

- Updated next-auth dependency in package.json to version 5.0.0-beta.29.
- Refactored create-admin script to use a valid email format.
- Implemented authentication middleware for various API routes to enforce access control.
- Refactored API route handlers to improve readability and maintainability.
- Enhanced error handling in authentication error page.
- Added detailed tests for authentication flow, including protected routes and NextAuth endpoints.
This commit is contained in:
2025-06-25 12:32:13 +02:00
parent 035a0386d7
commit c1bb4c44fd
24 changed files with 626 additions and 369 deletions

View File

@@ -1,25 +1,26 @@
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import db from "./db.js"
import Credentials from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
import { z } from "zod"
import { randomBytes } from "crypto"
const loginSchema = z.object({
email: z.string().email("Invalid email format"),
password: z.string().min(6, "Password must be at least 6 characters")
})
export const authOptions = {
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
CredentialsProvider({
Credentials({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
async authorize(credentials) {
try {
// Import database here to avoid edge runtime issues
const { default: db } = await import("./db.js")
// Validate input
const validatedFields = loginSchema.parse(credentials)
@@ -68,9 +69,6 @@ export const authOptions = {
WHERE id = ?
`).run(user.id)
// Log successful login
logAuditEvent(user.id, 'LOGIN_SUCCESS', 'user', user.id, req)
return {
id: user.id,
email: user.email,
@@ -87,24 +85,12 @@ export const authOptions = {
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
callbacks: {
async jwt({ token, user, account }) {
async jwt({ token, user }) {
if (user) {
token.role = user.role
token.userId = user.id
// Create session in database
const sessionToken = randomBytes(32).toString('hex')
const expires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days
db.prepare(`
INSERT INTO sessions (session_token, user_id, expires)
VALUES (?, ?, ?)
`).run(sessionToken, user.id, expires.toISOString())
token.sessionToken = sessionToken
}
return token
},
@@ -112,22 +98,8 @@ export const authOptions = {
if (token) {
session.user.id = token.userId
session.user.role = token.role
// Verify session is still valid in database
const dbSession = db.prepare(`
SELECT user_id FROM sessions
WHERE session_token = ? AND expires > datetime('now')
`).get(token.sessionToken)
if (!dbSession) {
// Session expired or invalid
return null
}
}
return session
},
async signIn({ user, account, profile, email, credentials }) {
return true
}
},
pages: {
@@ -135,39 +107,5 @@ export const authOptions = {
signOut: '/auth/signout',
error: '/auth/error'
},
events: {
async signOut({ token }) {
// Remove session from database
if (token?.sessionToken) {
db.prepare(`
DELETE FROM sessions WHERE session_token = ?
`).run(token.sessionToken)
if (token.userId) {
logAuditEvent(token.userId, 'LOGOUT', 'user', token.userId)
}
}
}
}
}
// Audit logging helper
function logAuditEvent(userId, action, resourceType, resourceId, req = null) {
try {
db.prepare(`
INSERT INTO audit_logs (user_id, action, resource_type, resource_id, ip_address, user_agent)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
userId,
action,
resourceType,
resourceId,
req?.ip || req?.socket?.remoteAddress || 'unknown',
req?.headers?.['user-agent'] || 'unknown'
)
} catch (error) {
console.error("Audit log error:", error)
}
}
export default NextAuth(authOptions)
debug: process.env.NODE_ENV === 'development'
})