Repository files navigation # Secure To-Do List Application (MERN Stack)
A complete, full-stack MERN (MongoDB, Express.js, React, Node.js) web application that combines a **JWT-based User Authentication API** and a **Personal To-Do List REST API** into a single cohesive project.
Users can securely register and log in to manage their personal tasks with end-to-end access control, protected routes, and a responsive modern user interface.
---
## Table of Contents
- [Project Overview](#project-overview)
- [Key Features](#key-features)
- [Technology Stack](#technology-stack)
- [Project Structure](#project-structure)
- [Installation Guide](#installation-guide)
- [Environment Configuration](#environment-configuration)
- [MongoDB Setup](#mongodb-setup)
- [Running the Application](#running-the-application)
- [API Documentation](#api-documentation)
- [JWT Authentication Explanation](#jwt-authentication-explanation)
- [Postman Testing Guide](#postman-testing-guide)
- [Security Best Practices](#security-best-practices)
---
## Project Overview
This project was built by combining two core modules:
1. **User Authentication API**: Secure registration, login, password encryption using bcryptjs, JWT generation, and protected `/me` profile retrieval.
2. **To-Do List REST API**: Full CRUD (Create, Read, Update, Delete) task operations and state toggling where every task is strictly bound to its owner's MongoDB ID.
### Key Architecture Highlights
- **Strict Data Isolation**: Users can **only** read, edit, toggle, or delete tasks that belong to their own account.
- **Stateless Authorization**: Protected endpoints require a valid JSON Web Token (`Bearer `).
- **Responsive Client**: Built with React 18, React Router, and Axios with custom request and response interceptors.
---
## Key Features
### Backend Features
- **User Registration**: Validates email format, prevents duplicate emails, and securely hashes passwords with `bcryptjs`.
- **User Authentication**: Compares salted hashes and issues signed JSON Web Tokens (JWT) with configurable expiry.
- **Protected Routes**: Centralized `authMiddleware` that parses the `Authorization: Bearer ` header and injects the authenticated user into `req.user`.
- **Task Management**:
- `POST /api/tasks` — Create new tasks with title and optional description.
- `GET /api/tasks` — Fetch all tasks belonging exclusively to the authenticated user.
- `GET /api/tasks/:id` — Retrieve a single task by ID with ownership verification.
- `PUT /api/tasks/:id` — Update task title, description, or completion status.
- `PATCH /api/tasks/:id/toggle` — Toggle task completion status between `true` and `false`.
- `DELETE /api/tasks/:id` — Delete a task with ownership verification.
- **Centralized Error Handling**: Unified responses for validation errors, duplicate keys (11000), cast errors (invalid ObjectIds), and expired/invalid tokens.
### Frontend Features
- **Modern Responsive Design**: Works seamlessly across mobile, tablet, and desktop viewports.
- **Authentication Flows**:
- Register with client-side validation and password match checks.
- Login with token and user persistence in `localStorage`.
- Seamless redirection from `/register` -> `/login` -> `/dashboard`.
- **Dashboard**:
- Live statistics: **Total Tasks**, **Completed Tasks**, and **Pending Tasks**.
- Interactive search bar to filter tasks by title or description.
- Status filter buttons: **All**, **Pending**, and **Completed**.
- Create and edit tasks with validation and feedback alerts.
- Mark tasks complete or pending with a single click.
- Delete confirmation.
- One-click Logout with automatic session cleanup.
- **Route Protection**:
- Unauthenticated access to `/dashboard` redirects to `/login`.
- Authenticated visits to `/login` or `/register` automatically redirect to `/dashboard`.
- **Axios Interceptors**: Automatically injects JWT into headers and clears expired credentials on `401 Unauthorized`.
---
## Technology Stack
### Frontend
- **React.js (v18)** — Component-based UI library
- **React Router (v6)** — Declarative client-side routing and protected routes
- **Axios** — Promise-based HTTP client with request/response interceptors
- **Vite** — Fast frontend build tool and dev server
- **Vanilla CSS** — Custom responsive design system (Plus Jakarta Sans, cards, badges, focus rings)
- **JavaScript (ES6+)** — (No TypeScript, purely JavaScript as specified)
### Backend
- **Node.js** — JavaScript runtime environment
- **Express.js** — Fast, unopinionated web framework for Node.js
- **MongoDB** — NoSQL document database
- **Mongoose** — Elegant MongoDB object modeling for Node.js
- **bcryptjs** — Salt-hashed password encryption
- **jsonwebtoken (JWT)** — Token creation and verification
- **cors** — Cross-Origin Resource Sharing middleware
- **dotenv** — Environment variable loader
- **nodemon** — Development server auto-reloader
---
## Project Structure
```
project-root/
│
├── backend/
│ ├── config/
│ │ └── db.js # MongoDB connection setup
│ ├── controllers/
│ │ ├── authController.js # User registration, login, and profile
│ │ └── taskController.js # Task CRUD and toggle logic
│ ├── middleware/
│ │ ├── authMiddleware.js # JWT Bearer token verification
│ │ └── errorMiddleware.js # 404 and central error handling
│ ├── models/
│ │ ├── User.js # User Schema with bcrypt hashing
│ │ └── Task.js # Task Schema with User reference
│ ├── routes/
│ │ ├── authRoutes.js # /api/auth routes
│ │ └── taskRoutes.js # /api/tasks routes
│ ├── .env # Local environment configuration
│ ├── .env.example # Example environment template
│ ├── package.json # Backend dependencies and scripts
│ └── server.js # Express server entry point
│
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ │ ├── Navbar.jsx # Top navigation header & user profile
│ │ │ ├── TaskForm.jsx # Task creation and editing form
│ │ │ └── TaskItem.jsx # Individual task card with actions
│ │ ├── pages/
│ │ │ ├── Login.jsx # User login page
│ │ │ ├── Register.jsx # User registration page
│ │ │ └── Dashboard.jsx # Main user dashboard with stats and tasks
│ │ ├── services/
│ │ │ └── api.js # Axios instance with auth interceptors
│ │ ├── context/
│ │ │ └── AuthContext.jsx # Global authentication state
│ │ ├── App.jsx # Application routing and route guards
│ │ ├── main.jsx # React DOM mounting
│ │ └── index.css # Responsive styling and design system
│ ├── index.html # HTML template
│ ├── vite.config.js # Vite configuration
│ ├── package.json # Frontend dependencies and scripts
│ ├── .env # Local frontend environment variables
│ └── .env.example # Frontend environment template
│
└── README.md # Project documentation
```
---
## Installation Guide
### Prerequisites
- **Node.js**: v18.0.0 or higher ([Download Node.js](https://nodejs.org/ ))
- **npm**: v9.0.0 or higher
- **MongoDB**: Either a local MongoDB instance running on port 27017 or a free [MongoDB Atlas](https://www.mongodb.com/cloud/atlas ) cluster connection URI.
### Step 1: Install Backend Dependencies
```bash
cd backend
npm install
```
### Step 2: Install Frontend Dependencies
```bash
cd ../frontend
npm install
```
---
## Environment Configuration
### Backend Environment (`backend/.env`)
Create a file named `.env` inside the `backend/` directory by copying `.env.example`:
```bash
cp backend/.env.example backend/.env
```
Contents of `backend/.env`:
```env
PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/secure_todo_app
JWT_SECRET=your_super_secret_jwt_key_here_change_in_production
JWT_EXPIRES_IN=7d
```
> **Security Note:** Never commit your `.env` file to version control. Replace `JWT_SECRET` with a long, random string in production.
### Frontend Environment (`frontend/.env`)
Create a file named `.env` inside the `frontend/` directory by copying `.env.example`:
```bash
cp frontend/.env.example frontend/.env
```
Contents of `frontend/.env`:
```env
VITE_API_URL=http://localhost:5000/api
```
---
## MongoDB Setup
You can use either a **Local MongoDB Community Server** or **MongoDB Atlas Cloud Database**.
### Option A: Local MongoDB Community Server (Recommended for offline local development)
1. Download and install [MongoDB Community Server](https://www.mongodb.com/try/download/community ).
2. Start the MongoDB service:
- On Windows: Run `net start MongoDB` or verify the service in `services.msc`.
- On macOS/Linux: `sudo systemctl start mongod` or `brew services start mongodb/brew/mongodb-community`.
3. In `backend/.env`, set:
```env
MONGO_URI=mongodb://127.0.0.1:27017/secure_todo_app
```
### Option B: MongoDB Atlas (Free Cloud Database)
1. Go to [MongoDB Atlas](https://www.mongodb.com/cloud/atlas ) and sign in/up.
2. Create a free **M0 Sandbox** cluster.
3. In **Database Access**, create a database user (e.g. `todoUser`) and note down the password.
4. In **Network Access**, click **Add IP Address** and choose **Allow Access From Anywhere (`0.0.0.0/0`)** for development.
5. Click **Connect** -> **Connect your application** -> **Driver: Node.js**.
6. Copy the connection string and paste it into `backend/.env`:
```env
MONGO_URI=mongodb+srv://:@cluster0.mongodb.net/secure_todo_app?retryWrites=true&w=majority
```
*(Replace `` and `` with your database credentials).*
---
## Running the Application
### 1. Start the Backend Server
From the root directory:
```bash
cd backend
npm run dev
```
The backend will run at: **`http://localhost:5000`**
Expected terminal output:
```
Server is running in development mode on port 5000
MongoDB Connected: 127.0.0.1
```
*(To run without nodemon, use `npm start`)*
### 2. Start the Frontend Application
In a new terminal window, from the root directory:
```bash
cd frontend
npm run dev
```
The React application will run at: **`http://localhost:5173`** (or the port specified by Vite).
Open your browser and navigate to `http://localhost:5173`.
---
## API Documentation
Base URL: `http://localhost:5000/api`
### 1. Authentication Endpoints (`/api/auth`)
| Method | Endpoint | Access | Description |
| :--- | :--- | :--- | :--- |
| `POST` | `/api/auth/register` | Public | Register a new user |
| `POST` | `/api/auth/login` | Public | Authenticate user & get JWT token |
| `GET` | `/api/auth/me` | Protected | Get current user's profile |
---
### 2. Task Endpoints (`/api/tasks`)
*(All task endpoints require `Authorization: Bearer `)*
| Method | Endpoint | Access | Description |
| :--- | :--- | :--- | :--- |
| `POST` | `/api/tasks` | Protected | Create a new task for the logged-in user |
| `GET` | `/api/tasks` | Protected | Get all tasks belonging to the logged-in user |
| `GET` | `/api/tasks/:id` | Protected | Get a specific task by ID |
| `PUT` | `/api/tasks/:id` | Protected | Update task title, description, or completed |
| `PATCH`| `/api/tasks/:id/toggle` | Protected | Toggle task completed between true/false |
| `DELETE`| `/api/tasks/:id` | Protected | Delete a task |
---
## JWT Authentication Explanation
### How It Works:
1. **User Login**: When the user sends their email and password to `POST /api/auth/login`, the server verifies the password using `bcrypt.compare()`.
2. **Token Generation**: If valid, the server generates a JSON Web Token using `jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' })`.
3. **Client Storage**: The frontend stores this token in `localStorage`.
4. **Attaching Header**: For all subsequent protected requests, Axios attaches the token to the HTTP header:
```http
Authorization: Bearer
```
5. **Middleware Verification (`authMiddleware.js`)**:
- Reads the `Authorization` header.
- Verifies the signature using `jwt.verify(token, process.env.JWT_SECRET)`.
- Decodes the user's `id`.
- Queries MongoDB to find the user (`User.findById(decoded.id).select('-password')`).
- Attaches the user object to `req.user`.
- Calls `next()` to execute the controller.
6. **Error Handling**: If the token is invalid or expired, the middleware stops execution and returns `401 Unauthorized`. The frontend Axios interceptor intercepts this and redirects the user to `/login`.
---
## Postman Testing Guide
Follow this sequential walkthrough to test the entire API using Postman.
### 1. Register a User
- **Method**: `POST`
- **URL**: `http://localhost:5000/api/auth/register`
- **Headers**:
- `Content-Type: application/json`
- **Body** (raw JSON):
```json
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}
```
- **Expected Status**: `201 Created`
- **Sample Response**:
```json
{
"success": true,
"message": "User registered successfully",
"user": {
"id": "679f2b841234567890abcdef",
"name": "John Doe",
"email": "john@example.com"
}
}
```
---
### 2. Login User
- **Method**: `POST`
- **URL**: `http://localhost:5000/api/auth/login`
- **Headers**:
- `Content-Type: application/json`
- **Body** (raw JSON):
```json
{
"email": "john@example.com",
"password": "password123"
}
```
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"message": "Login successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "679f2b841234567890abcdef",
"name": "John Doe",
"email": "john@example.com"
}
}
```
> **Important:** Copy the `token` string from the response. You will need it for all subsequent requests!
---
### 3. Get Current User (`/api/auth/me`)
- **Method**: `GET`
- **URL**: `http://localhost:5000/api/auth/me`
- **Authorization**:
- Type: `Bearer Token`
- Token: ``
*(Or in Headers: `Authorization: Bearer `)*
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"user": {
"id": "679f2b841234567890abcdef",
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2026-10-02T15:00:00.000Z"
}
}
```
---
### 4. Create Task
- **Method**: `POST`
- **URL**: `http://localhost:5000/api/tasks`
- **Authorization**: `Bearer `
- **Headers**:
- `Content-Type: application/json`
- **Body** (raw JSON):
```json
{
"title": "Learn React",
"description": "Complete React practice assignment and exercises"
}
```
- **Expected Status**: `201 Created`
- **Sample Response**:
```json
{
"success": true,
"message": "Task created successfully",
"task": {
"_id": "679f2c101234567890abcdef",
"title": "Learn React",
"description": "Complete React practice assignment and exercises",
"completed": false,
"user": "679f2b841234567890abcdef",
"createdAt": "2026-10-02T15:05:00.000Z",
"updatedAt": "2026-10-02T15:05:00.000Z"
}
}
```
> **Note:** Copy the task's `_id` to test single task endpoints.
---
### 5. Get All Tasks
- **Method**: `GET`
- **URL**: `http://localhost:5000/api/tasks`
- **Authorization**: `Bearer `
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"count": 1,
"tasks": [
{
"_id": "679f2c101234567890abcdef",
"title": "Learn React",
"description": "Complete React practice assignment and exercises",
"completed": false,
"user": "679f2b841234567890abcdef",
"createdAt": "2026-10-02T15:05:00.000Z",
"updatedAt": "2026-10-02T15:05:00.000Z"
}
]
}
```
---
### 6. Get Task By ID
- **Method**: `GET`
- **URL**: `http://localhost:5000/api/tasks/679f2c101234567890abcdef`
- **Authorization**: `Bearer `
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"task": {
"_id": "679f2c101234567890abcdef",
"title": "Learn React",
"description": "Complete React practice assignment and exercises",
"completed": false,
"user": "679f2b841234567890abcdef"
}
}
```
---
### 7. Update Task
- **Method**: `PUT`
- **URL**: `http://localhost:5000/api/tasks/679f2c101234567890abcdef`
- **Authorization**: `Bearer `
- **Headers**:
- `Content-Type: application/json`
- **Body** (raw JSON):
```json
{
"title": "Master React and Node.js",
"description": "Build high-performance full-stack MERN applications",
"completed": true
}
```
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"message": "Task updated successfully",
"task": {
"_id": "679f2c101234567890abcdef",
"title": "Master React and Node.js",
"description": "Build high-performance full-stack MERN applications",
"completed": true,
"user": "679f2b841234567890abcdef"
}
}
```
---
### 8. Toggle Task Status
- **Method**: `PATCH`
- **URL**: `http://localhost:5000/api/tasks/679f2c101234567890abcdef/toggle`
- **Authorization**: `Bearer `
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"message": "Task marked as pending",
"task": {
"_id": "679f2c101234567890abcdef",
"title": "Master React and Node.js",
"completed": false
}
}
```
---
### 9. Delete Task
- **Method**: `DELETE`
- **URL**: `http://localhost:5000/api/tasks/679f2c101234567890abcdef`
- **Authorization**: `Bearer `
- **Expected Status**: `200 OK`
- **Sample Response**:
```json
{
"success": true,
"message": "Task deleted successfully"
}
```
---
## Security Best Practices
1. **Password Hashing**: Uses `bcryptjs` with salt work factor of 10. Passwords are never stored in plaintext and never transmitted back in API responses.
2. **Token Security**: Tokens are signed with a cryptographically secure key stored in `.env`.
3. **Data Segregation**: All task queries (`find`, `findById`, `updateOne`, `deleteOne`) strictly scope by `user: req.user._id` so no user can access or modify another user's records.
4. **Input Sanitization**: Email format validation, password length checks, and title trimming prevent empty or malformed inputs.
5. **CORS Security**: Cross-Origin Resource Sharing is enabled to allow verified client requests while blocking malicious origin requests.
6. **Graceful Error Handling**: Database errors, invalid IDs, and unauthorized attempts return clean JSON errors without exposing server internals.
---
## License
This project is licensed under the ISC License. Created for educational and production reference.
# TodoApp
You can’t perform that action at this time.