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:
@@ -1,8 +1,9 @@
|
||||
import { getAllProjectTasks } from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// GET: Get all project tasks across all projects
|
||||
export async function GET() {
|
||||
async function getAllProjectTasksHandler() {
|
||||
try {
|
||||
const tasks = getAllProjectTasks();
|
||||
return NextResponse.json(tasks);
|
||||
@@ -13,3 +14,6 @@ export async function GET() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getAllProjectTasksHandler);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import NextAuth from "@/lib/auth"
|
||||
import { handlers } from "@/lib/auth"
|
||||
|
||||
export const GET = NextAuth
|
||||
export const POST = NextAuth
|
||||
export const { GET, POST } = handlers
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import db from "@/lib/db";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
export async function GET(req, { params }) {
|
||||
async function getContractHandler(req, { params }) {
|
||||
const { id } = await params;
|
||||
|
||||
const contract = db
|
||||
@@ -20,7 +21,7 @@ export async function GET(req, { params }) {
|
||||
return NextResponse.json(contract);
|
||||
}
|
||||
|
||||
export async function DELETE(req, { params }) {
|
||||
async function deleteContractHandler(req, { params }) {
|
||||
const { id } = params;
|
||||
|
||||
try {
|
||||
@@ -57,3 +58,7 @@ export async function DELETE(req, { params }) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getContractHandler);
|
||||
export const DELETE = withUserAuth(deleteContractHandler);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import db from "@/lib/db";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
export async function GET() {
|
||||
async function getContractsHandler() {
|
||||
const contracts = db
|
||||
.prepare(
|
||||
`
|
||||
@@ -21,7 +22,7 @@ export async function GET() {
|
||||
return NextResponse.json(contracts);
|
||||
}
|
||||
|
||||
export async function POST(req) {
|
||||
async function createContractHandler(req) {
|
||||
const data = await req.json();
|
||||
db.prepare(
|
||||
`
|
||||
@@ -46,3 +47,7 @@ export async function POST(req) {
|
||||
);
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getContractsHandler);
|
||||
export const POST = withUserAuth(createContractHandler);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import db from "@/lib/db";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
export async function POST(req) {
|
||||
async function createNoteHandler(req) {
|
||||
const { project_id, task_id, note } = await req.json();
|
||||
|
||||
if (!note || (!project_id && !task_id)) {
|
||||
@@ -18,7 +19,7 @@ export async function POST(req) {
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
export async function DELETE(_, { params }) {
|
||||
async function deleteNoteHandler(_, { params }) {
|
||||
const { id } = params;
|
||||
|
||||
db.prepare("DELETE FROM notes WHERE note_id = ?").run(id);
|
||||
@@ -26,7 +27,7 @@ export async function DELETE(_, { params }) {
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
export async function PUT(req, { params }) {
|
||||
async function updateNoteHandler(req, { params }) {
|
||||
const noteId = params.id;
|
||||
const { note } = await req.json();
|
||||
|
||||
@@ -42,3 +43,8 @@ export async function PUT(req, { params }) {
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const POST = withUserAuth(createNoteHandler);
|
||||
export const DELETE = withUserAuth(deleteNoteHandler);
|
||||
export const PUT = withUserAuth(updateNoteHandler);
|
||||
|
||||
@@ -3,9 +3,10 @@ import {
|
||||
deleteProjectTask,
|
||||
} from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// PATCH: Update project task status
|
||||
export async function PATCH(req, { params }) {
|
||||
async function updateProjectTaskHandler(req, { params }) {
|
||||
try {
|
||||
const { status } = await req.json();
|
||||
|
||||
@@ -27,7 +28,7 @@ export async function PATCH(req, { params }) {
|
||||
}
|
||||
|
||||
// DELETE: Delete a project task
|
||||
export async function DELETE(req, { params }) {
|
||||
async function deleteProjectTaskHandler(req, { params }) {
|
||||
try {
|
||||
deleteProjectTask(params.id);
|
||||
return NextResponse.json({ success: true });
|
||||
@@ -38,3 +39,7 @@ export async function DELETE(req, { params }) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const PATCH = withUserAuth(updateProjectTaskHandler);
|
||||
export const DELETE = withUserAuth(deleteProjectTaskHandler);
|
||||
|
||||
@@ -5,9 +5,10 @@ import {
|
||||
} from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import db from "@/lib/db";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// GET: Get all project tasks or task templates based on query params
|
||||
export async function GET(req) {
|
||||
async function getProjectTasksHandler(req) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const projectId = searchParams.get("project_id");
|
||||
|
||||
@@ -23,7 +24,7 @@ export async function GET(req) {
|
||||
}
|
||||
|
||||
// POST: Create a new project task
|
||||
export async function POST(req) {
|
||||
async function createProjectTaskHandler(req) {
|
||||
try {
|
||||
const data = await req.json();
|
||||
|
||||
@@ -113,3 +114,7 @@ export async function PATCH(req) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getProjectTasksHandler);
|
||||
export const POST = withUserAuth(createProjectTaskHandler);
|
||||
|
||||
@@ -4,19 +4,25 @@ import {
|
||||
deleteProject,
|
||||
} from "@/lib/queries/projects";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
export async function GET(_, { params }) {
|
||||
async function getProjectHandler(_, { params }) {
|
||||
const project = getProjectById(params.id);
|
||||
return NextResponse.json(project);
|
||||
}
|
||||
|
||||
export async function PUT(req, { params }) {
|
||||
async function updateProjectHandler(req, { params }) {
|
||||
const data = await req.json();
|
||||
updateProject(params.id, data);
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
export async function DELETE(_, { params }) {
|
||||
async function deleteProjectHandler(_, { params }) {
|
||||
deleteProject(params.id);
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getProjectHandler);
|
||||
export const PUT = withUserAuth(updateProjectHandler);
|
||||
export const DELETE = withUserAuth(deleteProjectHandler);
|
||||
|
||||
@@ -4,9 +4,10 @@ import {
|
||||
deleteNote,
|
||||
} from "@/lib/queries/notes";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// GET: Get notes for a specific task
|
||||
export async function GET(req) {
|
||||
async function getTaskNotesHandler(req) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const taskId = searchParams.get("task_id");
|
||||
|
||||
@@ -26,7 +27,7 @@ export async function GET(req) {
|
||||
}
|
||||
|
||||
// POST: Add a note to a task
|
||||
export async function POST(req) {
|
||||
async function addTaskNoteHandler(req) {
|
||||
try {
|
||||
const { task_id, note, is_system } = await req.json();
|
||||
|
||||
@@ -49,7 +50,7 @@ export async function POST(req) {
|
||||
}
|
||||
|
||||
// DELETE: Delete a note
|
||||
export async function DELETE(req) {
|
||||
async function deleteTaskNoteHandler(req) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const noteId = searchParams.get("note_id");
|
||||
@@ -71,3 +72,8 @@ export async function DELETE(req) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getTaskNotesHandler);
|
||||
export const POST = withUserAuth(addTaskNoteHandler);
|
||||
export const DELETE = withUserAuth(deleteTaskNoteHandler);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import db from "@/lib/db";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// GET: Get a specific task template
|
||||
export async function GET(req, { params }) {
|
||||
async function getTaskHandler(req, { params }) {
|
||||
try {
|
||||
const template = db
|
||||
.prepare("SELECT * FROM tasks WHERE task_id = ? AND is_standard = 1")
|
||||
@@ -25,7 +26,7 @@ export async function GET(req, { params }) {
|
||||
}
|
||||
|
||||
// PUT: Update a task template
|
||||
export async function PUT(req, { params }) {
|
||||
async function updateTaskHandler(req, { params }) {
|
||||
try {
|
||||
const { name, max_wait_days, description } = await req.json();
|
||||
|
||||
@@ -58,7 +59,7 @@ export async function PUT(req, { params }) {
|
||||
}
|
||||
|
||||
// DELETE: Delete a task template
|
||||
export async function DELETE(req, { params }) {
|
||||
async function deleteTaskHandler(req, { params }) {
|
||||
try {
|
||||
const result = db
|
||||
.prepare("DELETE FROM tasks WHERE task_id = ? AND is_standard = 1")
|
||||
@@ -79,3 +80,8 @@ export async function DELETE(req, { params }) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getTaskHandler);
|
||||
export const PUT = withUserAuth(updateTaskHandler);
|
||||
export const DELETE = withUserAuth(deleteTaskHandler);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import db from "@/lib/db";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// POST: create new template
|
||||
export async function POST(req) {
|
||||
async function createTaskHandler(req) {
|
||||
const { name, max_wait_days, description } = await req.json();
|
||||
|
||||
if (!name) {
|
||||
@@ -18,3 +19,6 @@ export async function POST(req) {
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const POST = withUserAuth(createTaskHandler);
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { getAllTaskTemplates } from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// GET: Get all task templates
|
||||
export async function GET() {
|
||||
async function getTaskTemplatesHandler() {
|
||||
const templates = getAllTaskTemplates();
|
||||
return NextResponse.json(templates);
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getTaskTemplatesHandler);
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
'use client'
|
||||
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
export default function AuthError() {
|
||||
const searchParams = useSearchParams()
|
||||
const error = searchParams.get('error')
|
||||
|
||||
const getErrorMessage = (error) => {
|
||||
switch (error) {
|
||||
case 'CredentialsSignin':
|
||||
return 'Invalid email or password. Please check your credentials and try again.'
|
||||
case 'AccessDenied':
|
||||
return 'Access denied. You do not have permission to sign in.'
|
||||
case 'Verification':
|
||||
return 'The verification token has expired or has already been used.'
|
||||
default:
|
||||
return 'An unexpected error occurred during authentication. Please try again.'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
@@ -7,8 +27,13 @@ export default function AuthError() {
|
||||
Authentication Error
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-gray-600">
|
||||
There was a problem signing you in. Please try again.
|
||||
{getErrorMessage(error)}
|
||||
</p>
|
||||
{error && (
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
Error code: {error}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-6">
|
||||
<a
|
||||
href="/auth/signin"
|
||||
|
||||
@@ -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'
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { getToken } from "next-auth/jwt"
|
||||
import { auth } from "@/lib/auth"
|
||||
import { NextResponse } from "next/server"
|
||||
import db from "../db.js"
|
||||
|
||||
// Role hierarchy for permission checking
|
||||
const ROLE_HIERARCHY = {
|
||||
@@ -13,51 +12,30 @@ const ROLE_HIERARCHY = {
|
||||
export function withAuth(handler, options = {}) {
|
||||
return async (req, context) => {
|
||||
try {
|
||||
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET })
|
||||
const session = await auth(req)
|
||||
|
||||
// Check if user is authenticated
|
||||
if (!token?.userId) {
|
||||
if (!session?.user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Authentication required" },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check if user account is active
|
||||
const user = db.prepare("SELECT is_active FROM users WHERE id = ?").get(token.userId)
|
||||
if (!user?.is_active) {
|
||||
return NextResponse.json(
|
||||
{ error: "Account deactivated" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check role-based permissions
|
||||
if (options.requiredRole && !hasPermission(token.role, options.requiredRole)) {
|
||||
logAuditEvent(token.userId, 'ACCESS_DENIED', options.resource || 'api', req.url)
|
||||
// Check role-based permissions (without database access)
|
||||
if (options.requiredRole && !hasPermission(session.user.role, options.requiredRole)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Insufficient permissions" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Check resource-specific permissions
|
||||
if (options.checkResourceAccess) {
|
||||
const hasAccess = await options.checkResourceAccess(token, context.params)
|
||||
if (!hasAccess) {
|
||||
return NextResponse.json(
|
||||
{ error: "Access denied to this resource" },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Add user info to request
|
||||
req.user = {
|
||||
id: token.userId,
|
||||
email: token.email,
|
||||
name: token.name,
|
||||
role: token.role
|
||||
id: session.user.id,
|
||||
email: session.user.email,
|
||||
name: session.user.name,
|
||||
role: session.user.role
|
||||
}
|
||||
|
||||
// Call the original handler
|
||||
@@ -95,22 +73,3 @@ export function withManagerAuth(handler) {
|
||||
export function withAdminAuth(handler) {
|
||||
return withAuth(handler, { requiredRole: 'admin' })
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,38 @@
|
||||
import { withAuth } from "next-auth/middleware"
|
||||
import { auth } from "@/lib/auth"
|
||||
|
||||
export default withAuth(
|
||||
function middleware(req) {
|
||||
// Additional middleware logic can go here
|
||||
},
|
||||
{
|
||||
callbacks: {
|
||||
authorized: ({ token, req }) => {
|
||||
const { pathname } = req.nextUrl
|
||||
|
||||
// Allow access to auth pages
|
||||
if (pathname.startsWith('/auth/')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Require authentication for all other pages
|
||||
if (!token) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check admin routes
|
||||
if (pathname.startsWith('/admin/')) {
|
||||
return token.role === 'admin'
|
||||
}
|
||||
|
||||
// Allow authenticated users to access other pages
|
||||
return true
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
signIn: '/auth/signin',
|
||||
},
|
||||
export default auth((req) => {
|
||||
const { pathname } = req.nextUrl
|
||||
|
||||
// Allow access to auth pages
|
||||
if (pathname.startsWith('/auth/')) {
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
// Require authentication for all other pages
|
||||
if (!req.auth) {
|
||||
const url = new URL('/auth/signin', req.url)
|
||||
url.searchParams.set('callbackUrl', req.nextUrl.pathname)
|
||||
return Response.redirect(url)
|
||||
}
|
||||
|
||||
// Check admin routes (role check only, no database access)
|
||||
if (pathname.startsWith('/admin/')) {
|
||||
if (req.auth.user.role !== 'admin') {
|
||||
return Response.redirect(new URL('/auth/signin', req.url))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except for the ones starting with:
|
||||
* - api/auth (NextAuth.js API routes)
|
||||
* - api (all API routes handle their own auth)
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico (favicon file)
|
||||
* - auth pages (auth pages should be accessible)
|
||||
*/
|
||||
'/((?!api/auth|_next/static|_next/image|favicon.ico).*)',
|
||||
'/((?!api|_next/static|_next/image|favicon.ico|auth).*)',
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user