A complete full-stack authentication and user management system built with Next.js 15, Node.js, and PostgreSQL. This project demonstrates modern authentication patterns including session management, password hashing, JWT tokens, and secure microservice architecture.
- Session-Based Authentication - Secure server-side session management
- Password Hashing - Bcrypt-style password hashing with salt
- JWT Token Support - JSON Web Token generation and verification
- API Key Authentication - Secure API key validation middleware
- Session Encryption - Encrypted session data for client-side storage
- CORS Enforcement - Origin allow-list controlled via
ALLOWED_ORIGINS
- User Registration - New user creation with validation
- User Login/Logout - Complete authentication flow
- Session Verification - Real-time session validation
- Password Security - Secure password storage and verification
- Multi-Device Sessions - Support for multiple active sessions
- User Profile Management - Full CRUD operations for user data
- App Router - Modern Next.js 15 App Router architecture
- Server Actions - Server-side form handling and actions
- Middleware - Route protection and authentication middleware
- TypeScript - Full TypeScript support for type safety
- Tailwind CSS - Modern utility-first CSS framework
- Server Components - Optimized server-side rendering
- RESTful API - Clean REST API design
- Microservice Architecture - Standalone authentication service
- PostgreSQL Database - Robust relational database with ACID compliance
- Database Migrations - Version-controlled schema management
- API Documentation - Comprehensive JSDoc documentation
- Docker Support - Full containerization with Docker Compose
This project follows a microservice architecture pattern with clear separation of concerns:
- Frontend Service (Next.js) - User interface and client-side logic
- API Service (Node.js) - Authentication and user management microservice
- Database Service (PostgreSQL) - Data persistence and session storage
- Container Orchestration (Docker Compose) - Service coordination and deployment
nextjs-auth-example/
├── .gitmodules # Git submodule configuration
├── README.md # This file
├── docker-compose.yml # Docker orchestration for all services
├── ENV_VARIABLES.md # Environment variables documentation
├── REQUIREMENTS.md # Technical requirements and acceptance criteria
├── test-login.sh # Authentication testing script
├── api/ # Node.js Authentication API (git submodule)
│ ├── src/ # API source code
│ │ ├── app.js # Main Express application
│ │ ├── utils.js # Password hashing and encryption utilities
│ │ ├── middleware/ # Authentication middleware
│ │ ├── models/ # User and Session models
│ │ ├── routes/ # API routes (auth, users)
│ │ └── db/ # Database migrations and seeds
│ ├── docker-compose.yml # API's own docker-compose (not used in this setup)
│ ├── Dockerfile # API container definition
│ └── package.json # API dependencies
└── frontend/ # Next.js Frontend Application
├── src/ # Frontend source code
│ ├── app/ # Next.js App Router
│ │ ├── (auth)/ # Authentication routes (login, signup)
│ │ ├── (routes)/dashboard/ # Protected dashboard pages
│ │ ├── actions/ # Server Actions for auth flows
│ │ ├── lib/ # Redirect helpers and schemas
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── components/ # UI components
│ └── middleware.ts # Route protection middleware
├── public/ # Static assets
├── Dockerfile # Frontend container definition (development)
└── package.json # Frontend dependencies
- Docker and Docker Compose installed
- Git with submodule support
- Node.js (optional, for local development)
Recommended model: Claude Sonnet 4
Follow the Quick Start guide to get the application running with Docker. Be sure to set up the environment variables as described in
ENV_VARIABLES.md.
or access the generated prompt file directly in copilot chat:
/getting-started
-
Update the repository with submodules:
git submodule update --init --recursive
-
Copy environment files: copy the
.env.examplefile to.envand update the values as needed.cp .env.example .env
copy the api/.env.example file to api/.env and update the values as needed.
cp api/.env.example api/.env
copy the frontend/.env.example file to frontend/.env and update the values as needed.
cp frontend/.env.example frontend/.env
-
Start all services:
docker compose up -d
-
Wait for services to be ready (about 30-60 seconds for first startup):
# Check service status docker compose ps # Watch logs docker compose logs -f
-
Access the applications:
- Auth Frontend: http://localhost:3001 (Next.js authentication UI)
- API: http://localhost:8000 (Authentication API endpoints)
- Database: localhost:5432 (PostgreSQL with user and session data)
-
Test the authentication flow:
sh ./test-login.sh sh ./test-signup.sh
Or test manually:
- Visit http://localhost:3001
- Click "Sign Up" to create a new account
- Login with your credentials
- Access the protected dashboard
- User visits
/signuppage - Fills out registration form (first name, last name, email, password)
- Frontend sends request to API
/usersendpoint - API validates input and hashes password
- User record created in PostgreSQL database
- User redirected to login page
- User visits
/loginpage - Enters email and password
- Frontend sends request to API
/user-authendpoint - API verifies credentials against database
- API creates encrypted session and JWT token
- Session stored in database with expiration
- Encrypted session set as HTTP-only cookie
- User redirected to dashboard
- User accesses protected route
- Next.js middleware checks for session cookie
- Frontend calls API
/user-auth/verify-sessionendpoint - API decrypts session and validates against database
- If valid, user data returned; if invalid, user redirected to login
- Session automatically renewed on activity
- User clicks logout button
- Frontend calls API
/user-auth/logoutendpoint - API invalidates session in database
- Session cookie cleared from browser
- User redirected to login page
- Image: postgres:15-alpine
- Port: 5432
- Database: nodeapi
- Schema: Users table with authentication fields, Sessions table for session management
- Features:
- User data storage with hashed passwords
- Session persistence with automatic cleanup
- Database migrations and seeding
- Health checks and automatic restart
- Built from:
./api/Dockerfile - Port: 8000
- Environment: Development mode with hot reload
- Features:
- RESTful authentication endpoints
- Session-based authentication with encryption
- JWT token generation and verification
- API key authentication middleware
- Password hashing with salt
- CORS configuration for frontend integration
- Comprehensive error handling and logging
- Built from:
./frontend/Dockerfile - Port: 3001 (configurable via
FRONTEND_PORT) - Environment: Development mode with hot reload
- Features:
- Modern Next.js 15 App Router architecture
- Server-side rendering with Server Components
- Authentication pages (login, signup)
- Protected routes with middleware
- Server Actions for form handling
- TypeScript support
- Tailwind CSS for styling
- Session management with HTTP-only cookies
# Start all services in detached mode
docker compose up -d
# Or start with logs visible
docker compose up
# Start specific services
docker compose up -d db api # Database and API only
docker compose up -d frontend # Frontend only# View all service logs
docker compose logs -f
# View specific service logs
docker compose logs -f api
docker compose logs -f frontend
docker compose logs -f db# Check service status
docker compose ps
# Restart a service
docker compose restart frontend
# Rebuild a service
docker compose build frontend
docker compose up -d frontend
# Stop all services
docker compose down
# Stop and remove volumes (⚠️ deletes database data)
docker compose down -v# Test complete authentication flow
./test-login.sh
# Or test individual endpoints:
# 1. Create a new user
curl -X POST http://localhost:8000/users \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-api-key-12345-change-in-production" \
-d '{
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"password": "securepassword123"
}'
# 2. Login user
curl -X POST http://localhost:8000/user-auth \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-api-key-12345-change-in-production" \
-d '{
"email": "[email protected]",
"password": "securepassword123"
}'
# 3. Verify session (use session from login response)
curl -X POST http://localhost:8000/user-auth/verify-session \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-api-key-12345-change-in-production" \
-d '{
"session": "encrypted-session-data"
}'
# 4. Logout user
curl -X POST http://localhost:8000/user-auth/logout \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-api-key-12345-change-in-production" \
-d '{
"session": "encrypted-session-data"
}'# Run database migrations
docker compose exec api npm run migrate
# Seed the database with sample data
docker compose exec api npm run seed
# Reset database (migrate + seed)
docker compose exec api npm run db:reset
# Access PostgreSQL directly
docker compose exec db psql -U postgres -d nodeapi
# Check database tables
docker compose exec db psql -U postgres -d nodeapi -c "\dt"
# View users table
docker compose exec db psql -U postgres -d nodeapi -c "SELECT id, email, first_name, last_name, created_at FROM users;"
# View active sessions
docker compose exec db psql -U postgres -d nodeapi -c "SELECT id, user_id, expires_at, created_at FROM sessions WHERE expires_at > NOW();"- Frontend: Changes to files in
./frontend/src/will trigger hot reload - API: Changes to files in
./api/src/will trigger nodemon restart - Database: Schema changes require running migrations
The application uses environment variables for configuration. See ENV_VARIABLES.md for complete documentation.
ENV_VARIABLES.md documents every variable, default, and consumer. Common defaults you will encounter in local development:
AUTH_FRONTEND_URL=http://localhost:3001andFRONTEND_URL=http://localhost:3000control cross-app redirects.API_PORT=8000,FRONTEND_PORT=3001, andDOCS_PORT=8001(optional) expose services from Docker.- Database credentials:
DB_HOST=db,DB_PORT=5432,DB_NAME=nodejs_app,DB_USER=postgres,DB_PASSWORD=postgres(replace in production). The Postgres container also respects the matchingPOSTGRES_*variables. - Secrets:
API_KEY,JWT_SECRET, andSESSION_SECRETmust be aligned across the auth API and frontend; never use the development defaults outside local environments. - CORS and client URLs:
ALLOWED_ORIGINSdefaults tohttp://localhost:3000,http://localhost:3001;API_URLandNEXT_PUBLIC_API_URLdefault to the API origin for server/client requests.
For production deployment, ensure you:
- Change all default passwords and secrets
- Use strong, randomly generated API keys and secrets
- Configure proper CORS origins
- Enable HTTPS
- Use environment-specific database credentials
- Secure Password Storage - Passwords hashed with salt using Node.js crypto
- Session Management - Database-backed sessions with automatic expiration
- JWT Token Support - Stateless authentication for API clients
- Session Encryption - Client-side session data encrypted for security
- Multi-Device Support - Users can have multiple active sessions
- Automatic Cleanup - Expired sessions automatically removed from database
- API Key Authentication - All API endpoints protected with API keys
- CORS Allow List - Origin restrictions managed via
ALLOWED_ORIGINS - HTTP-Only Cookies - Session cookies inaccessible to JavaScript
- Secure Headers - Security headers configured for production
- Input Validation - Comprehensive input validation and sanitization
- Rate Limiting - Protection against brute force attacks
- Modern UI - Clean, responsive design with Tailwind CSS
- TypeScript - Full type safety throughout the application
- Server Components - Optimized server-side rendering
- Route Protection - Middleware-based route protection
- Form Handling - Server Actions for secure form processing
- Error Handling - Comprehensive error handling with user feedback
- RESTful Design - Clean, intuitive API endpoints
- Microservice Architecture - Standalone authentication service
- Database Migrations - Version-controlled database schema
- Comprehensive Logging - Detailed logging for debugging and monitoring
- Health Checks - Service health monitoring endpoints
- API Documentation - Auto-generated JSDoc documentation
- Hot Reload - Automatic reload on code changes
- Docker Support - Full containerization for consistent development
- Database Seeding - Sample data for development and testing
- Environment Configuration - Flexible environment variable setup
- Git Submodules - Modular architecture with git submodules
-
Authentication failures:
# Check API key configuration docker compose exec api printenv | grep API_KEY # Verify JWT and session secrets docker compose exec api printenv | grep -E "(JWT_SECRET|SESSION_SECRET)" # Check database connectivity docker compose exec api npm run migrate
-
Session not persisting:
# Check session table in database docker compose exec db psql -U postgres -d nodeapi -c "SELECT * FROM sessions;" # Check cookie settings in browser developer tools # Verify CORS configuration docker compose exec api printenv | grep ALLOWED_ORIGINS
-
Frontend not connecting to API:
# Check API URL configuration docker compose exec frontend printenv | grep API_URL # Verify API service is running curl -H "X-API-Key: dev-api-key-12345-change-in-production" http://localhost:8000/health
-
Database connection errors:
# Check if database is healthy docker compose exec db pg_isready -U postgres # View database logs docker compose logs db # Restart database service docker compose restart db
-
Port conflicts:
# Check if ports are in use lsof -i :3000 # Frontend lsof -i :8000 # API lsof -i :5432 # Database # Stop conflicting services or change ports in docker-compose.yml
-
Frontend build errors:
# Clear Next.js cache docker compose exec frontend rm -rf .next # Rebuild frontend docker compose build frontend --no-cache
-
API startup issues:
# Check API logs docker compose logs api # Restart API service docker compose restart api
-
Submodule issues:
# Update submodules git submodule update --remote # Reset submodules git submodule foreach --recursive git reset --hard
# Access container shell
docker compose exec api sh
docker compose exec frontend sh
docker compose exec db sh
# Check container resources
docker compose top
# View detailed container info
docker compose ps --services
docker inspect <container_name>
# Check Docker networks
docker network ls
docker network inspect nextjs-auth-example_default- Use .dockerignore: Exclude unnecessary files from build context
- Volume mounting: Development files are mounted for hot reload
- Health checks: Database waits for PostgreSQL to be ready
- Dependency caching: Node modules are cached in separate volumes
-
Create production environment file:
cp .env.example .env.production # Edit .env.production with production values -
Set secure secrets:
# Generate secure API key (32+ characters) API_KEY=$(openssl rand -base64 32) # Generate secure JWT secret (64+ characters) JWT_SECRET=$(openssl rand -base64 64) # Generate secure session secret (64+ characters) SESSION_SECRET=$(openssl rand -base64 64)
-
Configure database:
# Use production database credentials DB_HOST=your-production-db-host DB_NAME=your-production-db-name DB_USER=your-production-db-user DB_PASSWORD=your-secure-db-password DB_SSL=true -
Set CORS origins:
ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com FRONTEND_URL=https://yourdomain.com
# Build production images
docker compose -f docker-compose.prod.yml build
# Deploy to production
docker compose -f docker-compose.prod.yml up -d
# Run health checks
curl https://yourdomain.com/health# Run migrations on production database
docker compose -f docker-compose.prod.yml exec api npm run migrate
# Do NOT run seeds in production
# Seeds are for development data only- Change all default passwords and secrets
- Use HTTPS in production
- Configure proper CORS origins
- Enable database SSL connections
- Set up proper firewall rules
- Use strong, randomly generated secrets
- Configure rate limiting
- Set up monitoring and logging
- Regular security updates
# Health check endpoint
curl https://yourdomain.com/api/health
# Monitor application logs
docker compose -f docker-compose.prod.yml logs -f
# Monitor database
docker compose -f docker-compose.prod.yml exec db psql -U postgres -d nodeapi -c "SELECT COUNT(*) FROM sessions WHERE expires_at > NOW();"If you prefer to run services locally instead of Docker:
- Node.js 20+
- PostgreSQL 15+
- npm or yarn
-
Start PostgreSQL (locally or via Docker):
docker run --name postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:15-alpine
-
Setup API:
cd api npm install cp .env.example .env # Configure database connection npm run migrate npm run seed npm run serve
-
Setup Frontend:
cd frontend npm install cp .env.example .env.local # Configure API connection npm run dev
# Run authentication flow test
./test-login.sh
# Test API endpoints
npm test # If tests are available
# Load testing
# Use tools like Apache Bench or Artillery for load testing-
User Registration Flow:
- Navigate to http://localhost:3001/signup
- Fill out the registration form
- Verify email validation
- Check user created in database
-
Authentication Flow:
- Navigate to http://localhost:3001/login
- Login with valid credentials
- Verify redirect to dashboard
- Check session created in database
-
Session Management:
- Access protected routes
- Verify session validation
- Test session expiration
- Test logout functionality
-
API Testing:
- Use Postman or curl to test API endpoints
- Verify authentication required
- Test error responses
- Fork the repository
- Create a feature branch
- Make your changes
- Test the authentication flow
- Update documentation if needed
- Submit a pull request
- Follow TypeScript best practices
- Use proper error handling
- Add JSDoc comments for API endpoints
- Test authentication flows thoroughly
- Update environment variable documentation
- Follow security best practices
- Next.js 15 Documentation
- Express.js Documentation
- PostgreSQL Documentation
- Docker Compose Documentation
- JWT.io - JWT token debugger
- OWASP Authentication Cheat Sheet
This project is licensed under the ISC License. See the LICENSE file for details.
For issues and questions:
- Check the troubleshooting section above
- Review the technical requirements in
REQUIREMENTS.md - Check environment variable documentation in
ENV_VARIABLES.md - Open an issue on the project repository