📖 Table of Contents
- Introduction to Authentication
- What is JWT (JSON Web Token)?
- Setting Up the Project
- Installing Dependencies
- Creating a User Model
- Setting Up JWT Authentication Middleware
- Creating Authentication Routes (Login, Register, Protected Route)
- Testing the Authentication System
- Environment Variables for Security
- Best Practices for Authentication
- Conclusion
1. Introduction to Authentication
Authentication ensures that only verified users can access certain resources in your application. In this guide, we’ll use JWT (JSON Web Token) for secure authentication in an Express.js application.
2. What is JWT (JSON Web Token)?
- JWT is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object.
- Tokens are signed using a secret key or a public/private key pair.
Structure of a JWT Token:
Header.Payload.Signature
- Header: Metadata about the token.
- Payload: User data and claims.
- Signature: Verifies the integrity of the token.
3. Setting Up the Project
Ensure you have a basic Express.js application ready. If not, follow this guide.
Initialize a new Node.js project:
npm init -y
npm install express jsonwebtoken bcryptjs dotenv
4. Installing Dependencies
- express: Web framework for Node.js.
- jsonwebtoken: Handles JWT generation and verification.
- bcryptjs: Hashes passwords securely.
- dotenv: Loads environment variables.
Install all dependencies:
npm install express jsonwebtoken bcryptjs dotenv
npm install --save-dev @types/jsonwebtoken @types/bcryptjs
5. Creating a User Model
For MongoDB:
File: src/models/userModel.ts
import mongoose, { Schema, Document } from 'mongoose';
interface IUser extends Document {
username: string;
email: string;
password: string;
}
const UserSchema: Schema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
export default mongoose.model<IUser>('User', UserSchema);
For PostgreSQL:
File: src/models/userModel.ts
import db from '../config/knex';
export const createUser = (user: { username: string; email: string; password: string }) => db('users').insert(user);
export const getUserByEmail = (email: string) => db('users').where({ email }).first();
6. Setting Up JWT Authentication Middleware
File: src/middleware/authMiddleware.ts
import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';
interface AuthRequest extends Request {
user?: any;
}
export const authenticateJWT = (req: AuthRequest, res: Response, next: NextFunction) => {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) {
return res.status(403).json({ message: 'Authorization required' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || '');
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ message: 'Invalid Token' });
}
};
7. Creating Authentication Routes (Login, Register, Protected Route)
7.1 Register Route
File: src/controllers/authController.ts
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import User from '../models/userModel';
import { Request, Response } from 'express';
export const registerUser = async (req: Request, res: Response) => {
const { username, email, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = new User({ username, email, password: hashedPassword });
await newUser.save();
res.status(201).json({ message: 'User registered successfully' });
};
7.2 Login Route
export const loginUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ message: 'Invalid credentials' });
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(400).json({ message: 'Invalid credentials' });
}
const token = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_SECRET || '', { expiresIn: '1h' });
res.json({ token });
};
7.3 Protected Route
export const protectedRoute = async (req: any, res: Response) => {
res.json({ message: `Hello, ${req.user.email}! You have access.` });
};
7.4 Authentication Routes
File: src/routes/authRoutes.ts
import express from 'express';
import { registerUser, loginUser, protectedRoute } from '../controllers/authController';
import { authenticateJWT } from '../middleware/authMiddleware';
const router = express.Router();
router.post('/register', registerUser);
router.post('/login', loginUser);
router.get('/protected', authenticateJWT, protectedRoute);
export default router;
7.5 Update app.ts
import authRoutes from './routes/authRoutes';
app.use('/api/auth', authRoutes);
8. Testing the Authentication System
8.1 Register a User
-
Endpoint:
POST /api/auth/register
- Request Body:
{
"username": "testuser",
"email": "test@example.com",
"password": "password123"
}
- Response:
{
"message": "User registered successfully"
}
8.2 Login User
-
Endpoint:
POST /api/auth/login
- Request Body:
{
"email": "test@example.com",
"password": "password123"
}
- Response:
{
"token": "JWT_TOKEN_HERE"
}
8.3 Access Protected Route
-
Endpoint:
GET /api/auth/protected
- Header:
Authorization: Bearer JWT_TOKEN_HERE
- Response:
{
"message": "Hello, test@example.com! You have access."
}
9. Environment Variables for Security
File: .env
JWT_SECRET=your_jwt_secret_key
Ensure you keep this secret key secure and never expose it publicly.
10. Best Practices for Authentication
- Always hash passwords using bcrypt.
- Store JWT secrets securely in
.env
. - Use short expiration times for tokens.
- Implement refresh tokens for better security.
- Avoid sending sensitive data in JWT payloads.
11. Conclusion
- Authentication Flow: User registration → Login → JWT Token Generation → Access Protected Routes.
- JWT is stateless and scalable for modern applications.
- Secure your routes using middleware.
✨ Your Express.js app now has JWT-based authentication! 🚀
Author: Mohin Sheikh
Follow me on GitHub for more insights!
Top comments (0)