A full-stack Expense Tracker application built using Node.js, Express.js, MySQL, Sequelize ORM, bcrypt, and MVC Architecture. Users can securely sign up, log in, and manage their daily expenses.
This project implements the Secure User Authentication System for the Expense Tracker application.
Users can:
- Register securely using bcrypt password hashing
- Login using JWT Authentication
- Create personal expenses
- View only their own expenses
- Delete only their own expenses
- Store expenses linked to their account
- Access protected routes using JWT tokens
The project follows the MVC (Model-View-Controller) Architecture and uses Sequelize ORM for database operations.
- User Registration
- Login Authentication
- bcrypt Password Hashing
- JWT Token Generation
- JWT Token Verification
- Protected Routes
- Authentication Middleware
- Add Expense
- View User Specific Expenses
- Delete Own Expenses
- Expense Categories
- Expense Persistence
- Expense Reload on Refresh
- User Table
- Expense Table
- One-To-Many Relationship
- Foreign Key Mapping
## π JWT Authentication
### Why not send User ID from Frontend?
Sending userId directly from frontend is insecure because users can modify the request and access another user's data.
Example:
```json
{
"userId": 5
}
```
A malicious user could change:
```json
{
"userId": 1
}
```
and access someone else's expenses.
Therefore user identity must be verified using JWT.
---
### Token Generation
After successful login:
```text
User Login
β
Verify Password
β
Generate JWT Token
β
Send Token To Frontend
```
Example:
```js
jwt.sign(
{
userId: user.id,
name: user.name
},
'secretkey'
);
```
---
### Token Verification
Frontend sends token:
```text
Authorization: JWT_TOKEN
```
Backend verifies token:
```js
jwt.verify(token, 'secretkey');
```
If token is valid:
```js
req.user = decodedToken;
```
User information becomes available in all protected routes.## π User & Expense Relationship
### One User
Can Have
### Many Expenses
```text
User
β
βββ Expense 1
βββ Expense 2
βββ Expense 3
```
### Sequelize Relationship
```js
User.hasMany(Expense);
Expense.belongsTo(User);
```
This automatically creates:
```text
userId
```
inside Expense Table.## π Expense Table Schema
| Field | Type |
|------------|---------|
| id | INTEGER |
| amount | FLOAT |
| description| STRING |
| category | STRING |
| userId | INTEGER |## π‘ Protected Routes
### Add Expense
```http
POST /expense/add-expense
```
Protected using:
```js
auth
```
middleware.
---
### Get Expenses
```http
GET /expense/get-expenses
```
Returns only logged-in user's expenses.
---
### Delete Expense
```http
DELETE /expense/delete-expense/:id
```
User can delete only expenses created by them.## π° User Specific Expense Flow
```text
User Login
β
JWT Token Generated
β
Token Stored In LocalStorage
β
Expense Added
β
Token Sent In Header
β
Middleware Verifies User
β
userId Extracted
β
Expense Saved With userId
β
Expense Linked To User
```## π Authentication Middleware
File:
```text
middleware/auth.js
```
Responsibilities:
```text
Read JWT Token
Verify Token
Extract User Information
Attach User To Request
Allow Access To Protected Routes
```Database stored passwords like:
123456
admin123
password
This is unsafe because anyone with database access can view user passwords.
Passwords are stored as hashes:
$2b$10$RkK9j...
$2b$10$gXfP7...
Actual passwords are never stored in plain text.
User Password
β
βΌ
bcrypt.hash()
β
βΌ
MySQL Database
User Password
β
βΌ
bcrypt.compare()
β
βΌ
Authentication Result
- Node.js
- Express.js
- bcrypt
- MySQL
- Sequelize ORM
- JavaScript
- HTML5
- CSS3
- Axios
- CORS
expense-tracker
β
βββ controllers
β βββ user.js
β βββ expense.js
β
βββ models
β βββ user.js
β βββ expense.js
β
βββ routes
β βββ user.js
β βββ expense.js
β
βββ util
β βββ database.js
β
βββ views
β βββ signup.html
β βββ signup.css
β βββ signup.js
β βββ login.html
β βββ login.css
β βββ login.js
β βββ expense.html
β βββ expense.css
β βββ expense.js
β
βββ images
β
βββ app.js
βββ package.json
βββ README.md
POST /user/signup{
"name": "Yash",
"email": "yash@gmail.com",
"password": "123456"
}Validate User
β
Check Existing Email
β
bcrypt.hash()
β
Store Hashed Password
β
Success Response
{
"message": "User created successfully"
}Status Code:
201 Created
{
"message": "User already exists"
}Status Code:
409 Conflict
POST /user/login{
"email": "yash@gmail.com",
"password": "123456"
}Find User
β
bcrypt.compare()
β
Password Match ?
β
Login Success
{
"message": "User login successful"
}Status Code:
200 OK
{
"message": "User not authorized"
}Status Code:
401 Unauthorized
{
"message": "User not found"
}Status Code:
404 Not Found
POST /expense/add-expense{
"amount": 500,
"description": "Petrol",
"category": "Fuel"
}
{
"success": true,
"expense": {
"id": 1,
"amount": 500,
"description": "Petrol",
"category": "Fuel"
}
}
201 Created
GET /expense/get-expenses
200 OK
DELETE /expense/delete-expense/:id
200 OK
User
β
Frontend Form
β
Axios POST Request
β
Route
β
Controller
β
Model
β
MySQL Database
β
Response
β
Frontend Alert
| Field | Type |
|---|---|
| id | INTEGER |
| name | STRING |
| STRING | |
| password | STRING |
## π° Expense Flow
```text
User Login
β
Expense Page Opens
β
Enter Expense Details
β
Axios POST Request
β
Expense Route
β
Expense Controller
β
Expense Model
β
MySQL Database
β
Expense Displayed on Screen
## π MVC Architecture
### View
```text
signup.html
signup.css
signup.js
login.html
login.css
login.jscontrollers/user.js
Responsibilities:
Receive Requests
Validate Data
Check Existing Users
Verify Login Credentials
Send Responses
models/user.js
Responsibilities:
Database Operations
Store User Data
Manage User Records
expense-tracker
β
βββ controllers β Controller Layer
β βββ user.js
β βββ expense.js
β
βββ models β Model Layer
β βββ user.js
β βββ expense.js
β
βββ routes β Route Layer
β βββ user.js
β βββ expense.js
β
βββ util
β βββ database.js β Database Configuration
β
βββ views β View Layer
β β
β βββ signup.html
β βββ signup.css
β βββ signup.js
β β
β βββ login.html
β βββ login.css
β βββ login.js
β β
β βββ expense.html
β βββ expense.css
β βββ expense.js
β
βββ images
β βββ signup-page.png
β βββ login-page.png
β βββ expense-page.png
β βββ add-expense.png
β βββ expense-table.png
β βββ delete-expense.png
β βββ mysql-data.png
β βββ mvc-architecture.png
β
βββ app.js β Application Entry Point
βββ package.json
βββ package-lock.json
βββ README.md
βββββββββββββββββββββββ
β VIEW β
β HTML β’ CSS β’ JS β
ββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββ
β ROUTES β
β routes/user.js β
ββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββ
β CONTROLLER β
β controllers/user.js β
ββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββ
β MODEL β
β models/user.js β
ββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββ
β MYSQL DATABASE β
βββββββββββββββββββββββ
* Premium Membership
* Leaderboard Feature
* Expense Pagination
* Download Expense Reports
* Forgot Password
* Razorpay Integration
* AWS Deployment
* Monthly Analytics
* AI Powered Expense InsightsNode.js β’ Express.js β’ MySQL β’ JavaScript
β If you found this project helpful, consider giving it a star on GitHub!