Implementing OAuth 2.0 in a Node.js API using Express and JWT
In today's digital landscape, securing APIs is paramount. OAuth 2.0 has emerged as the go-to standard for authorization, enabling applications to interact securely with third-party services. In this article, we will delve into implementing OAuth 2.0 in a Node.js API using the Express framework and JSON Web Tokens (JWT). We'll cover everything from the basic definitions of OAuth 2.0 to step-by-step coding instructions, ensuring that you can create a secure and scalable API.
What is OAuth 2.0?
OAuth 2.0 is an authorization framework that allows third-party services to exchange tokens for access to user data without needing to share passwords. Instead of giving away sensitive information, users can grant limited access to their resources. This makes OAuth 2.0 a vital component for modern web applications.
Key Concepts of OAuth 2.0
- Resource Owner: The user who owns the data and can grant access.
- Client: The application requesting access to the resource owner's data.
- Authorization Server: The server that issues access tokens after authenticating the resource owner.
- Resource Server: The server hosting the resource that the client wants to access.
Use Cases for OAuth 2.0
- Social Media Logins: Allowing users to log in using their Google or Facebook accounts.
- API Access: Granting limited access to third-party applications without sharing credentials.
- Mobile Applications: Securing user data in mobile apps by verifying user identities.
Setting Up Your Node.js Environment
Prerequisites
Before we start coding, ensure you have the following installed:
- Node.js: Version 12 or later.
- npm: Node package manager.
- MongoDB: For storing user data (optional but recommended).
Initializing Your Project
- Create a new project directory:
bash
mkdir oauth2-node-api
cd oauth2-node-api
- Initialize npm:
bash
npm init -y
- Install required packages:
bash
npm install express mongoose jsonwebtoken body-parser dotenv
Building the API
Step 1: Setting Up the Express Server
Create a file named server.js
and add the following code:
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
app.use(bodyParser.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Basic route
app.get('/', (req, res) => {
res.send('OAuth 2.0 API using Node.js and Express');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Step 2: Create User Model
Create a models
folder and add a User.js
file:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
module.exports = mongoose.model('User', UserSchema);
Step 3: Implementing User Registration and Login
In your server.js
, add the following routes for user registration and login:
const User = require('./models/User');
const bcrypt = require('bcrypt');
// User Registration
app.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ username, password: hashedPassword });
try {
await user.save();
res.status(201).send('User registered successfully');
} catch (error) {
res.status(400).send('Error registering user');
}
});
// User Login
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (user && await bcrypt.compare(password, user.password)) {
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(400).send('Invalid credentials');
}
});
Step 4: Protecting Routes with JWT
To protect your API routes, create a middleware function:
const jwt = require('jsonwebtoken');
const authenticateJWT = (req, res, next) => {
const token = req.headers['authorization'];
if (token) {
jwt.verify(token.split(' ')[1], process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
};
// Protected route example
app.get('/protected', authenticateJWT, (req, res) => {
res.send('This is a protected route');
});
Step 5: Testing Your API
You can test your API using tools like Postman or cURL:
- Register a new user:
-
POST to
http://localhost:3000/register
with JSON body:{"username": "testuser", "password": "password123"}
. -
Login to get a token:
-
POST to
http://localhost:3000/login
with JSON body:{"username": "testuser", "password": "password123"}
. -
Access the protected route:
- GET
http://localhost:3000/protected
with the Authorization header:Bearer YOUR_TOKEN
.
Conclusion
Implementing OAuth 2.0 in a Node.js API using Express and JWT not only enhances security but also provides a robust framework for user authentication and authorization. By following the steps outlined in this guide, you can create a secure API that protects user data while allowing seamless interaction between different services.
As you continue to develop your API, consider exploring advanced topics such as refresh tokens, scopes, and integrating with third-party OAuth providers. Happy coding!