22 KiB
Authorization Implementation Guide
Project Overview
This document outlines the implementation strategy for adding authentication and authorization to the Project Management Panel - a Next.js 15 application with SQLite database.
Current State Analysis (Updated: June 25, 2025)
✅ What We Have Implemented
- Framework: Next.js 15 with App Router
- Database: SQLite with better-sqlite3
- Authentication: NextAuth.js v5 with credentials provider
- User Management: Complete user CRUD operations with bcrypt password hashing
- Database Schema: Users table with roles, audit logs, sessions
- API Protection: Middleware system with role-based access control
- Session Management: JWT-based sessions with 30-day expiration
- Security Features: Account lockout, failed login tracking, password validation
- UI Components: Authentication provider, navigation with user context
- Auth Pages: Sign-in page implemented
✅ What's Protected
- API Routes: All major endpoints (projects, contracts, tasks, notes) are protected
- Role Hierarchy: admin > project_manager > user > read_only
- Navigation: Role-based menu items (admin sees user management)
- Session Security: Automatic session management and validation
🔄 Partially Implemented
- Auth Pages: Sign-in exists, missing sign-out and error pages
- User Interface: Basic auth integration, could use more polish
- Admin Features: User management backend exists, UI needs completion
- Audit Logging: Database schema exists, not fully integrated
❌ Still Missing
- Complete user management UI for admins
- Password reset functionality
- Rate limiting implementation
- Enhanced input validation schemas
- CSRF protection
- Security headers middleware
- Comprehensive error handling
- Email notifications
Recommended Implementation Strategy
1. Authentication Solution: NextAuth.js
Why NextAuth.js?
- ✅ Native Next.js 15 App Router support
- ✅ Database session management
- ✅ Built-in security features (CSRF, JWT handling)
- ✅ Flexible provider system
- ✅ SQLite adapter available
2. Role-Based Access Control (RBAC)
Proposed User Roles:
| Role | Permissions | Use Case |
|---|---|---|
| Admin | Full system access, user management | System administrators |
| Project Manager | Manage all projects/tasks, view reports | Team leads, supervisors |
| User | View/edit assigned projects/tasks | Regular employees |
| Read-only | View-only access to data | Clients, stakeholders |
Implementation Status
✅ Phase 1: Foundation Setup - COMPLETED
1.1 Dependencies - ✅ INSTALLED
- NextAuth.js v5 (beta)
- bcryptjs for password hashing
- Zod for validation
- Better-sqlite3 adapter compatibility
1.2 Environment Configuration - ✅ COMPLETED
.env.localconfigured with NEXTAUTH_SECRET and NEXTAUTH_URL- Database URL configuration
- Development environment setup
1.3 Database Schema - ✅ IMPLEMENTED
- Users table with roles and security features
- Sessions table for NextAuth.js
- Audit logs table for security tracking
- Proper indexes for performance
1.4 Initial Admin User - ✅ COMPLETED
scripts/create-admin.jsscript available- Default admin user: admin@localhost.com / admin123456
✅ Phase 2: Authentication Core - COMPLETED
2.1 NextAuth.js Configuration - ✅ IMPLEMENTED
- File:
src/lib/auth.js - Credentials provider with email/password
- JWT session strategy with 30-day expiration
- Account lockout after 5 failed attempts (15-minute lockout)
- Password verification with bcrypt
- Failed login attempt tracking
- Session callbacks for role management
2.2 API Route Handlers - ✅ IMPLEMENTED
- File:
src/app/api/auth/[...nextauth]/route.js - NextAuth.js handlers properly configured
2.3 User Management System - ✅ IMPLEMENTED
- File:
src/lib/userManagement.js - Complete CRUD operations for users
- Password hashing and validation
- Role management functions
- User lookup by ID and email
✅ Phase 3: Authorization Middleware - COMPLETED
3.1 API Protection Middleware - ✅ IMPLEMENTED
- File:
src/lib/middleware/auth.js withAuth()function for protecting routes- Role hierarchy enforcement (admin=4, project_manager=3, user=2, read_only=1)
- Helper functions:
withReadAuth,withUserAuth,withAdminAuth,withManagerAuth - Proper error handling and status codes
3.2 Protected API Routes - ✅ IMPLEMENTED
Example in src/app/api/projects/route.js:
- GET requests require read_only access
- POST requests require user access
- All major API endpoints are protected
3.3 Session Provider - ✅ IMPLEMENTED
- File:
src/components/auth/AuthProvider.js - NextAuth SessionProvider wrapper
- Integrated into root layout
🔄 Phase 4: User Interface - PARTIALLY COMPLETED
4.1 Authentication Pages - 🔄 PARTIAL
- ✅ Sign-in page:
src/app/auth/signin/page.js- Complete with form validation - ❌ Sign-out page: Missing
- 🔄 Error page:
src/app/auth/error/page.js- Basic implementation - ❌ Unauthorized page: Missing
4.2 Navigation Updates - ✅ COMPLETED
- File:
src/components/ui/Navigation.js - User session integration with useSession
- Role-based menu items (admin sees user management)
- Sign-out functionality
- Conditional rendering based on auth status
4.3 User Management Interface - ❌ MISSING
- Backend exists in userManagement.js
- Admin UI for user CRUD operations needed
- Role assignment interface needed
❌ Phase 5: Security Enhancements - NOT STARTED
5.1 Input Validation Schemas - ❌ MISSING
- Zod schemas for API endpoints
- Request validation middleware
5.2 Rate Limiting - ❌ MISSING
- Rate limiting middleware
- IP-based request tracking
5.3 Security Headers - ❌ MISSING
- CSRF protection
- Security headers middleware
- Content Security Policy
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { BetterSQLite3Adapter } from "@auth/better-sqlite3-adapter";
import db from "./db.js";
import bcrypt from "bcryptjs";
import { z } from "zod";
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 { handlers, auth, signIn, signOut } = NextAuth({
adapter: BetterSQLite3Adapter(db),
session: {
strategy: "database",
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
try {
// Validate input
const validatedFields = loginSchema.parse(credentials);
// Check if user exists and is active
const user = db
.prepare(
`
SELECT id, email, name, password_hash, role, is_active,
failed_login_attempts, locked_until
FROM users
WHERE email = ? AND is_active = 1
`
)
.get(validatedFields.email);
if (!user) {
throw new Error("Invalid credentials");
}
// Check if account is locked
if (user.locked_until && new Date(user.locked_until) > new Date()) {
throw new Error("Account temporarily locked");
}
// Verify password
const isValidPassword = await bcrypt.compare(
validatedFields.password,
user.password_hash
);
if (!isValidPassword) {
// Increment failed attempts
db.prepare(
`
UPDATE users
SET failed_login_attempts = failed_login_attempts + 1,
locked_until = CASE
WHEN failed_login_attempts >= 4
THEN datetime('now', '+15 minutes')
ELSE locked_until
END
WHERE id = ?
`
).run(user.id);
throw new Error("Invalid credentials");
}
// Reset failed attempts and update last login
db.prepare(
`
UPDATE users
SET failed_login_attempts = 0,
locked_until = NULL,
last_login = CURRENT_TIMESTAMP
WHERE id = ?
`
).run(user.id);
// Log successful login
logAuditEvent(user.id, "LOGIN_SUCCESS", "user", user.id, req);
return {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
};
} catch (error) {
console.error("Login error:", error);
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user, account }) {
if (user) {
token.role = user.role;
token.userId = user.id;
}
return token;
},
async session({ session, token, user }) {
if (token) {
session.user.id = token.userId || token.sub;
session.user.role = token.role || user?.role;
}
return session;
},
async signIn({ user, account, profile, email, credentials }) {
// Additional sign-in logic if needed
return true;
},
},
pages: {
signIn: "/auth/signin",
signOut: "/auth/signout",
error: "/auth/error",
},
events: {
async signOut({ session, token }) {
if (session?.user?.id) {
logAuditEvent(session.user.id, "LOGOUT", "user", session.user.id);
}
},
},
});
// 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 || "unknown",
req?.headers?.["user-agent"] || "unknown"
);
} catch (error) {
console.error("Audit log error:", error);
}
}
2.2 API Route Handlers
Create src/app/api/auth/[...nextauth]/route.js:
import { handlers } from "@/lib/auth";
export const { GET, POST } = handlers;
Phase 3: Authorization Middleware
3.1 API Protection Middleware
Create src/lib/middleware/auth.js:
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";
import { z } from "zod";
// Role hierarchy for permission checking
const ROLE_HIERARCHY = {
admin: 4,
project_manager: 3,
user: 2,
read_only: 1,
};
export function withAuth(handler, options = {}) {
return async (req, context) => {
try {
const session = await auth();
// Check if user is authenticated
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(session.user.id);
if (!user?.is_active) {
return NextResponse.json(
{ error: "Account deactivated" },
{ status: 403 }
);
}
// Check role-based permissions
if (
options.requiredRole &&
!hasPermission(session.user.role, options.requiredRole)
) {
logAuditEvent(
session.user.id,
"ACCESS_DENIED",
options.resource || "api",
req.url
);
return NextResponse.json(
{ error: "Insufficient permissions" },
{ status: 403 }
);
}
// Check resource-specific permissions
if (options.checkResourceAccess) {
const hasAccess = await options.checkResourceAccess(
session.user,
context.params
);
if (!hasAccess) {
return NextResponse.json(
{ error: "Access denied to this resource" },
{ status: 403 }
);
}
}
// Validate request body if schema provided
if (
options.bodySchema &&
(req.method === "POST" ||
req.method === "PUT" ||
req.method === "PATCH")
) {
try {
const body = await req.json();
options.bodySchema.parse(body);
} catch (error) {
return NextResponse.json(
{ error: "Invalid request data", details: error.errors },
{ status: 400 }
);
}
}
// Add user info to request
req.user = session.user;
req.session = session;
// Call the original handler
return await handler(req, context);
} catch (error) {
console.error("Auth middleware error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
};
}
export function hasPermission(userRole, requiredRole) {
return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole];
}
// Helper for read-only operations
export function withReadAuth(handler) {
return withAuth(handler, { requiredRole: "read_only" });
}
// Helper for user-level operations
export function withUserAuth(handler) {
return withAuth(handler, { requiredRole: "user" });
}
// Helper for project manager operations
export function withManagerAuth(handler) {
return withAuth(handler, { requiredRole: "project_manager" });
}
// Helper for admin operations
export function withAdminAuth(handler) {
return withAuth(handler, { requiredRole: "admin" });
}
3.2 Client-Side Route Protection
Create src/components/auth/ProtectedRoute.js:
"use client";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export function ProtectedRoute({
children,
requiredRole = null,
fallback = null,
}) {
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
if (status === "loading") return; // Still loading
if (!session) {
router.push("/auth/signin");
return;
}
if (requiredRole && !hasPermission(session.user.role, requiredRole)) {
router.push("/unauthorized");
return;
}
}, [session, status, router, requiredRole]);
if (status === "loading") {
return (
<div className="flex justify-center items-center h-64">Loading...</div>
);
}
if (!session) {
return fallback || <div>Redirecting to login...</div>;
}
if (requiredRole && !hasPermission(session.user.role, requiredRole)) {
return fallback || <div>Access denied</div>;
}
return children;
}
function hasPermission(userRole, requiredRole) {
const roleHierarchy = {
admin: 4,
project_manager: 3,
user: 2,
read_only: 1,
};
return roleHierarchy[userRole] >= roleHierarchy[requiredRole];
}
Phase 4: User Interface Components
4.1 Authentication Pages
Pages to create:
src/app/auth/signin/page.js- Login formsrc/app/auth/signout/page.js- Logout confirmationsrc/app/auth/error/page.js- Error handlingsrc/app/unauthorized/page.js- Access denied page
4.2 Navigation Updates
Update src/components/ui/Navigation.js to include:
- Login/logout buttons
- User info display
- Role-based menu items
4.3 User Management Interface
For admin users:
- User listing and management
- Role assignment
- Account activation/deactivation
Phase 5: Security Enhancements
5.1 Input Validation Schemas
Create src/lib/schemas/ with Zod schemas for all API endpoints:
// src/lib/schemas/project.js
import { z } from "zod";
export const createProjectSchema = z.object({
contract_id: z.number().int().positive(),
project_name: z.string().min(1).max(255),
project_number: z.string().min(1).max(50),
address: z.string().optional(),
// ... other fields
});
export const updateProjectSchema = createProjectSchema.partial();
5.2 Rate Limiting
Implement rate limiting for sensitive endpoints:
// src/lib/middleware/rateLimit.js
const attempts = new Map();
export function withRateLimit(
handler,
options = { maxAttempts: 5, windowMs: 15 * 60 * 1000 }
) {
return async (req, context) => {
const key = req.ip || "unknown";
const now = Date.now();
const window = attempts.get(key) || {
count: 0,
resetTime: now + options.windowMs,
};
if (now > window.resetTime) {
window.count = 1;
window.resetTime = now + options.windowMs;
} else {
window.count++;
}
attempts.set(key, window);
if (window.count > options.maxAttempts) {
return NextResponse.json({ error: "Too many requests" }, { status: 429 });
}
return handler(req, context);
};
}
Implementation Checklist (Updated Status)
✅ Phase 1: Foundation - COMPLETED
- Install dependencies (NextAuth.js v5, bcryptjs, zod)
- Create environment configuration (.env.local)
- Extend database schema (users, sessions, audit_logs)
- Create initial admin user script
✅ Phase 2: Authentication - COMPLETED
- Configure NextAuth.js with credentials provider
- Create API route handlers (/api/auth/[...nextauth])
- Implement user management system
- Test login/logout functionality
✅ Phase 3: Authorization - COMPLETED
- Implement API middleware (withAuth, role hierarchy)
- Protect existing API routes (projects, contracts, tasks, notes)
- Create role-based helper functions
- Integrate session provider in app layout
🔄 Phase 4: User Interface - IN PROGRESS
- Create sign-in page with form validation
- Update navigation component with auth integration
- Add role-based menu items
- Create sign-out confirmation page
- Create error handling page
- Create unauthorized access page
- Build admin user management interface
❌ Phase 5: Security Enhancements - NOT STARTED
- Add input validation schemas to all endpoints
- Implement rate limiting for sensitive operations
- Add comprehensive audit logging
- Create security headers middleware
- Implement CSRF protection
- Add password reset functionality
Current Working Features
🔐 Authentication System
- Login/Logout: Fully functional with NextAuth.js
- Session Management: JWT-based with 30-day expiration
- Password Security: bcrypt hashing with salt rounds
- Account Lockout: 5 failed attempts = 15-minute lockout
- Role System: 4-tier hierarchy (admin, project_manager, user, read_only)
🛡️ Authorization System
- API Protection: All major endpoints require authentication
- Role-Based Access: Different permission levels per endpoint
- Middleware: Clean abstraction with helper functions
- Session Validation: Automatic session verification
📱 User Interface
- Navigation: Context-aware with user info and sign-out
- Auth Pages: Professional sign-in form with error handling
- Role Integration: Admin users see additional menu items
- Responsive: Works across device sizes
🗄️ Database Security
- User Management: Complete CRUD with proper validation
- Audit Schema: Ready for comprehensive logging
- Indexes: Optimized for performance
- Constraints: Role validation and data integrity
Next Priority Tasks
-
Complete Auth UI (High Priority)
- Sign-out confirmation page
- Unauthorized access page
- Enhanced error handling
-
Admin User Management (High Priority)
- User listing interface
- Create/edit user forms
- Role assignment controls
-
Security Enhancements (Medium Priority)
- Input validation schemas
- Rate limiting middleware
- Comprehensive audit logging
-
Password Management (Medium Priority)
- Password reset functionality
- Password strength requirements
- Password change interface
Security Best Practices
1. Password Security
- Minimum 8 characters
- Require special characters, numbers
- Hash with bcrypt (cost factor 12+)
- Implement password history
2. Session Security
- Secure cookies
- Session rotation
- Timeout handling
- Device tracking
3. API Security
- Input validation on all endpoints
- SQL injection prevention (prepared statements)
- XSS protection
- CSRF tokens
4. Audit & Monitoring
- Log all authentication events
- Monitor failed login attempts
- Track permission changes
- Alert on suspicious activity
Testing Status
✅ Completed Tests
- Authentication Flow: Login/logout working correctly
- API Protection: All endpoints properly secured
- Role Validation: Permission levels enforced
- Session Management: JWT tokens and expiration working
- Password Security: bcrypt hashing and verification functional
- Account Lockout: Failed attempt tracking and temporary lockout
🔧 Available Test Scripts
test-auth.mjs- Tests API route protection and auth endpointstest-auth-detailed.mjs- Comprehensive authentication flow testingtest-complete-auth.mjs- Full system authentication validationtest-logged-in-flow.mjs- Authenticated user session testing
✅ Verified Security Features
- Unauthorized API requests return 401
- Role-based access control working
- Session tokens properly validated
- Password attempts tracked and limited
- Admin user creation and management functional
Deployment Considerations
1. Environment Variables
- Use strong, random secrets
- Different keys per environment
- Secure secret management
2. Database Security
- Regular backups
- Encryption at rest
- Network security
- Access logging
3. Application Security
- HTTPS enforcement
- Security headers
- Content Security Policy
- Regular security updates
Migration Strategy
1. Development Phase
- Implement on development branch
- Test thoroughly with sample data
- Document all changes
2. Staging Deployment
- Deploy to staging environment
- Performance testing
- Security testing
- User acceptance testing
3. Production Deployment
- Database backup before migration
- Gradual rollout
- Monitor for issues
- Rollback plan ready
Resources and Documentation
NextAuth.js
Security Libraries
Best Practices
Next Steps: Choose which phase to implement first and create detailed implementation tickets for development.