Introduction
In the previous article, we learned how to:
Build multi-page React applications
Use React Router
Create navigation systems
Handle dynamic routes
Build protected frontend pages
Now it is time to secure our application using:
Authentication
Authorization
JWT Tokens
Password hashing
Authentication is one of the most important parts of modern web applications because it protects:
User accounts
Private data
Admin dashboards
Payment systems
User sessions
In this article, we will learn:
What authentication is
What JWT is
User registration
Login system
Password hashing with bcrypt
Token generation
Protected backend routes
Storing JWT tokens in React
What is Authentication?
Authentication means:
Verifying the identity of a user
Example:
Login with email and password
OTP verification
Google login
What is Authorization?
Authorization means:
Checking what a user is allowed to access
Example:
Admin panel access
User dashboard
Premium features
Authentication Flow in PERN Stack
React Frontend
↓
Login Request
↓
Express Backend
↓
Verify User
↓
Generate JWT Token
↓
Send Token to Frontend
↓
Frontend Stores Token
What is JWT?
JWT stands for:
JSON Web Token
JWT is used to securely identify users after login.
Instead of logging in repeatedly, users receive a token.
Structure of JWT
A JWT has 3 parts:
Header.Payload.Signature
Why JWT is Popular
JWT is:
Fast
Secure
Stateless
Easy to use
Most modern applications use JWT authentication.
Installing Required Packages
Inside backend folder install:
npm install jsonwebtoken bcrypt
Understanding Packages
| Package | Purpose |
|---|---|
| jsonwebtoken | Creates JWT tokens |
| bcrypt | Hashes passwords |
Why Password Hashing is Important
Never store plain passwords like:
123456
password
shivam123
If database gets hacked, all passwords become visible.
What is Hashing?
Hashing converts password into encrypted format.
Example:
password123
becomes:
$2b$10$8kjsdf89asdf...
Creating Users Table
Inside PostgreSQL:
CREATE TABLE users(
user_id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100) UNIQUE,
password VARCHAR(255)
);
User Registration API
Example Registration Route
app.post('/register', async (req, res) => {
try {
const { name, email, password } = req.body;
const hashedPassword = await bcrypt.hash(
password,
10
);
const newUser = await pool.query(
'INSERT INTO users(name, email, password) VALUES($1, $2, $3) RETURNING *',
[name, email, hashedPassword]
);
res.json(newUser.rows[0]);
} catch (err) {
console.error(err.message);
}
});
Understanding bcrypt.hash()
bcrypt.hash(password, 10)
| Part | Meaning |
|---|---|
| password | Original password |
| 10 | Salt rounds |
Higher salt rounds increase security.
What are Salt Rounds?
Salt rounds make password cracking harder.
More rounds:
More secure
Slightly slower
Usually:
10
is sufficient.
Login API
Now let’s verify user login.
Login Route Example
app.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await pool.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
if (user.rows.length === 0) {
return res.status(401).json(
'Invalid Email'
);
}
const validPassword = await bcrypt.compare(
password,
user.rows[0].password
);
if (!validPassword) {
return res.status(401).json(
'Invalid Password'
);
}
res.json('Login Successful');
} catch (err) {
console.error(err.message);
}
});
Understanding bcrypt.compare()
bcrypt.compare()
compares:
Original password
Hashed password
Generating JWT Token
Now let’s generate token after successful login.
JWT Token Example
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ user: user.rows[0].user_id },
'secretkey'
);
Understanding jwt.sign()
| Part | Meaning |
|---|---|
| Payload | User data |
| secretkey | Secret key |
Sending Token to Frontend
res.json({ token });
Frontend stores this token after login.
What is a Secret Key?
Secret key is used to:
Create tokens
Verify tokens
Example:
'mySuperSecretKey'
Never expose secret keys publicly.
Using Environment Variables
Install dotenv:
npm install dotenv
Create:
.env
Example:
JWT_SECRET=mysecretkey
Using dotenv
require('dotenv').config();
Access variable:
process.env.JWT_SECRET
Complete Login with JWT
const token = jwt.sign(
{ user: user.rows[0].user_id },
process.env.JWT_SECRET
);
res.json({ token });
Storing Token in React
Frontend stores token using:
localStorage
Example
localStorage.setItem('token', token);
Retrieving Token
const token = localStorage.getItem('token');
Why Tokens are Sent in Headers
Protected APIs require token verification.
Frontend sends token in request headers.
Example Authorization Header
headers: {
token: localStorage.token
}
Creating Protected Middleware
const jwt = require('jsonwebtoken');
module.exports = async (req, res, next) => {
try {
const token = req.header('token');
if (!token) {
return res.status(403).json(
'Not Authorized'
);
}
const verify = jwt.verify(
token,
process.env.JWT_SECRET
);
req.user = verify.user;
next();
} catch (err) {
res.status(401).json(
'Token Invalid'
);
}
};
Understanding jwt.verify()
jwt.verify()
checks:
Token validity
Secret key
User authenticity
Using Protected Middleware
app.get(
'/dashboard',
authorize,
async (req, res) => {
res.json('Welcome Dashboard');
}
);
Protected Route Flow
Frontend Sends Token
↓
Backend Verifies Token
↓
Access Granted or Denied
Logout System
Logout simply removes token.
Example:
localStorage.removeItem('token');
Checking User Authentication in React
const token = localStorage.getItem('token');
if (token) {
console.log('User Logged In');
}
Real-World Authentication Systems
Authentication is used in:
Banking apps
E-commerce websites
Social media apps
SaaS platforms
Online learning systems
Without authentication, applications become insecure.
Common Beginner Mistakes
Storing Plain Passwords
Wrong:
password: '123456'
Correct:
Use bcrypt hashing.
Exposing Secret Keys
Never write:
const secret = 'mysecret';
inside public frontend code.
Forgetting Middleware
Protected routes require:
authorize
middleware.
Not Using HTTPS
Production applications should always use:
HTTPS
for secure communication.
Best Practices
Use Environment Variables
Store:
Database URLs
JWT secrets
API keys
inside .env.
Hash Passwords Always
Never store original passwords.
Use Token Expiration
Example:
jwt.sign(payload, secret, {
expiresIn: '1h'
});
Token Expiration Benefits
Expiration improves:
Security
Session management
Account protection
Conclusion
You have now learned how authentication works in the PERN stack using JWT.
You learned:
Authentication
Authorization
Password hashing
JWT tokens
Login system
Registration system
Protected routes
Token storage
Now your PERN stack application is becoming secure and production-ready.
Comments
Post a Comment