This document describes the comprehensive JWT authentication system implemented in the FreelanceHub project.
The authentication system uses JWT tokens stored in HTTP-only cookies for security, with role-based access control for different user types (students and businesses).
- Middleware (
middleware.ts) - Root-level route protection - JWT Utilities (
src/lib/auth/jwt.ts) - Token creation and verification - Server Cookie Utilities (
src/lib/utils/cookies.ts) - Server-side secure cookie management - Client Cookie Utilities (
src/lib/utils/client-cookies.ts) - Client-side cookie helpers - Auth Context (
src/context/AuthContext.tsx) - Client-side state management - Protected Route Component (
src/components/auth/ProtectedRoute.tsx) - Client-side route protection - API Authentication Utilities (
src/lib/utils/auth.ts) - Server-side auth helpers
- HTTP-only cookies for JWT storage (prevents XSS attacks)
- Secure flag in production (HTTPS only)
- SameSite: lax for CSRF protection
- Token expiration (7 days by default)
- Role-based access control
- Automatic token refresh
- Server-side authentication verification
- Student - Can browse jobs, submit proposals
- Business - Can post jobs, manage contracts
Register a new user account.
Request Body:
{
"name": "John Doe",
"email": "john@example.com",
"phone": "1234567890",
"password": "securepassword",
"role": "student"
}Response:
{
"success": true,
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "student",
"phone": "1234567890"
},
"message": "Registration successful"
}Authenticate user and receive JWT token.
Request Body:
{
"email": "john@example.com",
"password": "securepassword"
}Response:
{
"success": true,
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "student",
"phone": "1234567890"
},
"message": "Login successful"
}Get current user information (for client-side auth verification).
Response:
{
"authenticated": true,
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "student",
"phone": "1234567890"
}
}Clear authentication cookies.
Response:
{
"success": true,
"message": "Logged out successfully"
}Refresh JWT token.
Response:
{
"success": true,
"user": {
"id": "user_id",
"name": "John Doe",
"email": "john@example.com",
"role": "student",
"phone": "1234567890"
},
"message": "Token refreshed successfully"
}import { useAuth } from '@/context/AuthContext';
function MyComponent() {
const { user, isAuthenticated, login, logout } = useAuth();
if (!isAuthenticated) {
return <div>Please log in</div>;
}
return (
<div>
<h1>Welcome, {user?.name}!</h1>
<button onClick={logout}>Logout</button>
</div>
);
}import ProtectedRoute from '@/components/auth/ProtectedRoute';
function BusinessDashboard() {
return (
<ProtectedRoute allowedRoles={['business']}>
<div>Business Dashboard Content</div>
</ProtectedRoute>
);
}import { getUserFromRequest, createUnauthorizedResponse } from '@/lib/utils/auth';
export async function GET(request: NextRequest) {
const user = getUserFromRequest(request);
if (!user) {
return createUnauthorizedResponse('Authentication required');
}
// Only business users can access this endpoint
if (user.role !== 'business') {
return createForbiddenResponse('Insufficient permissions');
}
// Your API logic here
return NextResponse.json({ data: 'protected data' });
}import { verifyAuthToken } from '@/lib/utils/auth';
export async function POST(request: NextRequest) {
const payload = await verifyAuthToken(request);
if (!payload) {
return createUnauthorizedResponse('Invalid token');
}
// Use payload.userId and payload.role
return NextResponse.json({ success: true });
}Add these to your .env.local file:
JWT_SECRET=your-super-secret-jwt-key-here
MONGODB_URI=your-mongodb-connection-string- Never store sensitive data in JWT payload - Only include user ID, role, and email
- Use HTTPS in production - Cookies with secure flag require HTTPS
- Implement rate limiting - Prevent brute force attacks
- Validate input - Always validate user input on both client and server
- Log security events - Monitor for suspicious activity
- Regular token rotation - Consider implementing refresh token rotation
- Server-side authentication verification - Always verify auth status server-side
The system provides consistent error responses:
- 401 Unauthorized - Authentication required or invalid token
- 403 Forbidden - Insufficient permissions
- 400 Bad Request - Invalid input data
- 500 Internal Server Error - Server-side errors
The root middleware automatically protects all routes except those listed in PUBLIC_PATHS. It also handles:
- Token verification
- Role-based access control
- Automatic redirects for unauthenticated users
- Cookie cleanup for invalid tokens
- Uses
document.cookiefor reading user data - Cannot access httpOnly tokens (security feature)
- Relies on server API calls for authentication verification
- Manages UI state and user experience
- Uses
cookies()fromnext/headers - Has access to httpOnly tokens
- Performs actual authentication verification
- Sets secure cookies
- Register a new account
- Login and verify cookies are set
- Access protected routes
- Test role-based access
- Logout and verify cookies are cleared
# Login
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password"}' \
-c cookies.txt
# Check auth status
curl -X GET http://localhost:3000/api/auth/me \
-b cookies.txt
# Access protected endpoint
curl -X GET http://localhost:3000/api/jobs \
-b cookies.txt-
"JWT_SECRET environment variable is not set"
- Add JWT_SECRET to your .env.local file
-
"Token verification failed"
- Check if JWT_SECRET matches between server restarts
- Verify token hasn't expired
-
"Authentication required"
- Ensure cookies are being sent with requests
- Check if middleware is properly configured
-
"Insufficient permissions"
- Verify user role matches required roles for the route
-
"next/headers only works in Server Components"
- Use client-side cookie utilities for client components
- Use server-side cookie utilities only in API routes
Enable debug logging by setting:
DEBUG_AUTH=trueThis will log authentication events to the console.