Build secure social media REST APIs using JWT for token-based access in Node.js and Express. Ready to dive in?
Imagine you're building a social media app. You want users to log in and interact securely with the system, without asking for their passwords every time they access a protected resource. Here's where token-based authentication comes in handy! ๐ก๏ธ
Today, I'll walk you through creating a basic REST API in Node.js that uses JWT (JSON Web Tokens) to control access to specific routes. By the end of this guide, you'll have a working example of secure API access using token authentication. ๐
Let's get started!
Step 1: Setting up your project
To start, we need to create a simple Node.js project. Open your terminal and follow these steps:
Initialize a Node.js project:
mkdir social-api
cd social-api
npm init -yInstall the necessary dependencies: We'll need express for our server, jsonwebtoken for generating and verifying tokens, and bcrypt for hashing passwords.
npm install express jsonwebtoken bcryptjsInstall nodemon for auto-reloading: It's a handy tool to restart your server when code changes.
npm install --save-dev nodemonUpdate the package.json to include a start script for nodemon:
"scripts": {
"start": "nodemon index.js"
}Step 2: Setting up the server with Express
Create a file named index.js in your project folder. Let's set up a basic Express server:
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});Explanation:
Here, we initialize an Express application and tell it to listen on port 3000. The express.json() middleware allows us to parse JSON request bodies.
Step 3: Creating routes for user registration and login
Now, let's add routes for user registration and login. First, we need a mock user database (just an in-memory array for simplicity):
const users = [];
app.post('/register', async (req, res) => {
const { username, password } = req.body;
// Hash the password
const hashedPassword = await bcrypt.hash(password, 10);
users.push({ username, password: hashedPassword });
res.status(201).send('User registered');
});Explanation:
- The
/registerroute allows a user to register by providing a username and password. - We use
bcrypt.hash()to hash the password before saving it to ourusersarray. Hashing ensures the password is stored securely.
Step 4: Implementing login and generating a JWT
Next, let's create the /login route where users can authenticate. If the login is successful, we generate a JWT and send it back.
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).send('User not found');
}
// Compare the entered password with the stored hash
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(400).send('Invalid password');
}
// Generate JWT
const token = jwt.sign({ username }, 'secretkey', { expiresIn: '1h' });
res.json({ token });
});Explanation:
- We check if the user exists, then compare the entered password with the hashed password using
bcrypt.compare(). - If valid, we use
jwt.sign()to create a token, passing theusernameas the payload and using a secret key ('secretkey'in this example). The token expires in 1 hour (expiresIn: '1h'). - Finally, we return the token as a JSON response.
Step 5: Protecting routes using JWT middleware
Now that we have a way to generate tokens, let's protect certain routes using a middleware that verifies the token. For example, a user's profile page should only be accessible if they are logged in.
function authenticateToken(req, res, next) {
const token = req.headers['authorization'];
if (!token) return res.sendStatus(401);
jwt.verify(token, 'secretkey', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.get('/profile', authenticateToken, (req, res) => {
res.json({ message: `Welcome, ${req.user.username}` });
});Explanation:
- The
authenticateTokenmiddleware extracts the token from theAuthorizationheader. jwt.verify()checks if the token is valid. If it's expired or invalid, we send a 403 status code.- If valid, the decoded
useris attached to the request object, allowing the user's data to be accessed in the/profileroute.
Step 6: Testing the API
Here's how the API works:
- Register a new user:
Send a POST request to
/registerwith ausernameandpassword. - Login and get the token:
Send a POST request to
/loginwith the same credentials. The API responds with a JWT token. - Access the profile route:
Send a GET request to
/profile, passing the token in theAuthorizationheader.
Authorization: Bearer <token>If successful, you'll see a message like:
{ "message": "Welcome, johndoe" }Step 7: Wrapping it up
You now have a fully functioning token-based authentication system using JWT in Node.js. You can expand this further by adding more features, like token refresh mechanisms, and role-based access, or integrating it with databases like MongoDB for real-world applications. ๐
Conclusion
In this blog, we covered the basics of building a Node.js API that uses JWT to authenticate users and protect routes. By following this simple approach, you ensure that sensitive operations are locked behind a secure authentication layer, keeping your users' data safe. ๐
This is just the beginning! Once you've mastered token-based authentication, you can easily scale and enhance it for more complex apps. Happy coding! ๐