A full-stack authentication system built without any JWT libraries, featuring custom token creation/verification using Node's crypto, refresh token rotation, secure notes API, and a modern Next.js 14 frontend with shadcn/ui.
- Features
- Prerequisites
- Installation & Setup
- Project Structure
- API Endpoints
- Testing with cURL
- Security Features
- Technical Implementation
- Dependencies
- Contributing
- My Blog on Medium
- Acknowledgments
- Custom JWT Implementation - HMAC-SHA256 signing without external libraries
- Secure Password Hashing - PBKDF2 with high iteration count
- Token Management - HTTP-only cookies for access & refresh tokens
- Refresh Token Rotation - Automatic token rotation on each refresh
- Authorization - Owner-based access control for resources
- Modern UI - Next.js 14 with App Router and shadcn/ui components
- Authentication Flow - Complete signup, login, and logout functionality
- Protected Routes - Secure dashboard with notes CRUD operations
- Real-time State - Context-based auth state management
- Responsive Design - Mobile-first approach with Tailwind CSS
- Node.js 18+
- MongoDB (local or cloud instance)
- npm or yarn
git clone https://github.com/sourabhaprasad/jwt-auth-no-lib.git
cd jwt-auth-no-lib
# Navigate to server directory
cd server
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
Edit the .env
file with your configuration:
PORT=4000
MONGODB_URI=mongodb://localhost:27017/jwt-auth
JWT_SECRET=your-super-secure-secret-key-here
# Navigate to client directory
cd ../client
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env.local
Edit the .env.local
file:
NEXT_PUBLIC_API_URL=http://localhost:4000
# Terminal 1 - Start backend server
cd server
npm run dev
# Terminal 2 - Start frontend development server
cd client
npm run dev
Visit http://localhost:3000 to access the application.
jwt-auth-no-lib/
├── server/ # Backend API
│ ├── config/ # Environment configuration
│ ├── controllers/ # Route handlers
│ ├── middleware/ # Authentication & authorization
│ ├── models/ # MongoDB schemas
│ ├── routes/ # API route definitions
│ ├── utils/ # JWT & password utilities
│ └── server.js # Express app entry point
└── client/ # Frontend application
├── app/ # Next.js App Router pages
├── components/ # React components
│ └── ui/ # shadcn/ui components
├── lib/ # Utilities & API client
└── types/ # TypeScript type definitions
Method | Endpoint | Description |
---|---|---|
POST |
/auth/signup |
Register a new user |
POST |
/auth/login |
Login and receive tokens |
POST |
/auth/refresh |
Refresh access token |
POST |
/auth/logout |
Logout and revoke tokens |
Method | Endpoint | Description |
---|---|---|
GET |
/notes |
List user's notes |
POST |
/notes |
Create a new note |
PUT |
/notes/:id |
Update note (owner only) |
DELETE |
/notes/:id |
Delete note (owner only) |
# Login
curl -X POST -c cookies.txt \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"password123"}' \
http://localhost:4000/auth/login
# Refresh tokens
curl -X POST -b cookies.txt -c cookies.txt \
http://localhost:4000/auth/refresh
# Create a note
curl -X POST -b cookies.txt \
-H "Content-Type: application/json" \
-d '{"title":"My Note","content":"Note content"}' \
http://localhost:4000/notes
# List notes
curl -X GET -b cookies.txt \
http://localhost:4000/notes
- PBKDF2 Password Hashing - Secure password storage with high iteration count
- Timing-Safe Comparison - Prevents timing attacks on signature validation
- Short-Lived Access Tokens - 15-minute expiration for security
- Refresh Token Rotation - Automatic token rotation on each refresh
- HTTP-Only Cookies - XSS protection for token storage
- Authorization Checks - Owner-based access control for all resources
- Custom JWT Implementation - No external dependencies for token handling
The custom JWT implementation is located in server/utils/jwt-utils.js
:
- Base64URL encoding/decoding
- HMAC-SHA256 signing using Node.js crypto
- Timing-safe signature validation
- Automatic expiration checking
- Login: Validate credentials → Issue access + refresh tokens (HTTP-only cookies)
- Requests: Verify access token from cookie → Process request
- Refresh: Validate refresh token → Rotate tokens → Issue new tokens
- Logout: Invalidate refresh token → Clear cookies
- Next.js 14 with App Router for modern React development
- shadcn/ui components with Tailwind CSS for consistent design
- Context API for global authentication state management
- Protected routes with automatic redirects for unauthenticated users
express
- Web frameworkmongoose
- MongoDB ODMcookie-parser
- Cookie parsing middlewaredotenv
- Environment variable management
next
- React framework with App Routerreact
&react-dom
- UI libraryshadcn/ui
- Component library (Radix UI + Tailwind)lucide-react
- Icon librarytiptap editor
- Text editor compatible with nextjs
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
For a detailed explanation of the JWT implementation and technical insights, check out my blog post: Building JWT Authentication From Scratch Without Libraries