In today's digital landscape, ensuring the security of your application is paramount, especially when dealing with user data. One method that has emerged as a robust solution for securing APIs is JSON Web Token (JWT) authentication. In this blog post, we will explore how to implement JWT authentication in a Node.js application as part of our CRUD app with MongoDB and TypeScript.
Before we jump into implementation, let’s clarify what JWT is. JSON Web Tokens are an open standard used to share security information between two parties. This transfer is compact, URL-safe, and can be verified and trusted since it is digitally signed.
A JWT consists of three parts:
Let’s break that down further in the context of our CRUD application.
To follow along, ensure you have Node.js installed, and you're working in a project set up with TypeScript. You can initialize your project with:
mkdir my-crud-app cd my-crud-app npm init -y npm install express mongoose jsonwebtoken bcryptjs dotenv npm install --save-dev typescript @types/node @types/express @types/mongoose @types/jsonwebtoken @types/bcryptjs
Also, don't forget to set up your tsconfig.json
.
First, let’s create a user model. In your models
directory, create a file named User.ts
:
import mongoose, { Schema, Document } from 'mongoose'; export interface IUser extends Document { username: string; password: string; } const UserSchema: Schema = new Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true } }); export const User = mongoose.model<IUser>('User', UserSchema);
In our user model, we are defining a simple schema that consists of a username
and password
.
Now we need to create a route to register users. Create a file named auth.ts
in your routes
directory:
import express from 'express'; import bcrypt from 'bcryptjs'; import { User } from '../models/User'; import jwt from 'jsonwebtoken'; const router = express.Router(); router.post('/register', async (req, res) => { try { const { username, password } = req.body; // Check if user already exists const existingUser = await User.findOne({ username }); if (existingUser) { return res.status(400).json({ message: 'User already exists' }); } const hashedPassword = await bcrypt.hash(password, 10); const user = new User({ username, password: hashedPassword }); await user.save(); // Generate JWT token const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET || 'your_secret', { expiresIn: '1h' }); res.status(201).json({ token }); } catch (error) { res.status(500).json({ message: 'Server error' }); } }); export default router;
bcryptjs
for hashing passwords and jsonwebtoken
for creating JWTs.Next, let’s create a route to authenticate a user. We’ll extend our auth.ts
file:
router.post('/login', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username }); if (!user || !(await bcrypt.compare(password, user.password))) { return res.status(401).json({ message: 'Invalid credentials' }); } const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET || 'your_secret', { expiresIn: '1h' }); res.json({ token }); });
Now, we need to protect certain routes. Create a middleware function that verifies the JWT token:
import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; export const verifyToken = (req: Request, res: Response, next: NextFunction) => { const token = req.headers['authorization']?.split(' ')[1]; if (!token) { return res.status(403).json({ message: 'Access denied' }); } jwt.verify(token, process.env.JWT_SECRET || 'your_secret', (err, decoded) => { if (err) { return res.status(401).json({ message: 'Invalid token' }); } // Add user information to the request for further usage req.user = decoded; next(); }); };
Authorization
header.Let’s create a protected route for retrieving user information in your routes
directory:
router.get('/user', verifyToken, (req, res) => { res.json({ id: req.user.id, username: req.user.username }); });
verifyToken
middleware to ensure only authenticated users can access it. If the token is valid, the user’s information is sent back.To complete the implementation, don’t forget to initialize your Express app and connect to MongoDB:
import express from 'express'; import mongoose from 'mongoose'; import dotenv from 'dotenv'; import authRoutes from './routes/auth'; dotenv.config(); const app = express(); app.use(express.json()); mongoose.connect(process.env.MONGODB_URI || 'your_mongodb_uri', { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB connected')) .catch(err => console.log(err)); app.use('/auth', authRoutes); app.listen(5000, () => console.log('Server running on port 5000'));
Implementing JWT authentication not only secures your application but also creates a scalable and practical way of managing user sessions. With this framework, you can further enhance your CRUD application with secure access controls and protect your user data effectively.
31/08/2024 | NodeJS
14/10/2024 | NodeJS
08/10/2024 | NodeJS
23/07/2024 | NodeJS
31/08/2024 | NodeJS
14/10/2024 | NodeJS
14/10/2024 | NodeJS
14/10/2024 | NodeJS
23/07/2024 | NodeJS
28/11/2024 | NodeJS