Implementing an OAuth Server With NodeJS and Express
Last Updated :
28 Jul, 2024
OAuth (Open Authorization) is the standard protocol that allows third-party applications to access user data without exposing the user credentials. Implementing the OAuth server in Node.js using Express involves setting up endpoints for the authorization and token of the exchange. It can ensure the secure access to the user resources.
Prerequisites
There are several approaches to implementing the OAuth server in the Node.js:
Using oAuth2orize
OAuth2orize is the popular toolkit for the implementing the OAuth2 servers in the Node.js. It can provides the framework for handling the various OAuth2 flows and including the authorization code, implicit and client credentials.
Steps:
- Install the oauth2orize and require modules.
- Create the Express server.
- Define the OAuth2 endpoints of authorization and token.
- Implement the authorization and token exchange logic.
Using the Simple-OAuth2
Simple-OAuth2 is the lightweight library for the creating OAuth2 clients, While it can primarily focuses on the client-side implementation and it can be adapted for the server side use of the application.
Steps:
- Install the simple-oauth2 and required modules.
- Create the Express Server.
- Define the OAuth2 endpoints of authorization and token.
- Implement the token management logic of the application.
Using the Custom Implementation
Custom implementation can involves the manually handling the OAuth2 flows using Express middleware and JWT for the token management.
Steps:
- Install the required modules like express, jsonwebtoken and bcryptjs.
- Create the Express server.
- Define the OAuth2 endpoints of authorization and token.
- Implement the authorization and token exchange logic using JWT.
Implementing the OAuth Server With Node.js and Express
Step 1: Initialize the node application
npm init -y
Step 2: Install the required modules
We can install the following modules into the project.
npm i express oauth2orize passport passport-http bcryptjs jsonwebtoken body-parser mongoose
Refer the below image for better understanding.
Installing dependenciesFolder Structure
Folder StructureDependencies
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.4.4",
"oauth2orize": "^1.12.0",
"passport": "^0.7.0",
"passport-http": "^0.3.0"
}
Example
JavaScript
//controller/passport.js
const passport = require('passport');
const BasicStrategy = require('passport-http').BasicStrategy;
const User = require('../models/user');
const Client = require('../models/client');
const bcrypt = require('bcryptjs');
passport.use(new BasicStrategy(
async function (username, password, done) {
try {
console.log(`Authenticating user: ${username}`);
const user = await User.findOne({ username: username });
if (!user) {
console.log('User not found');
return done(null, false);
}
const isMatch = await bcrypt.compare(password, user.password);
if (isMatch) {
return done(null, user);
} else {
console.log('Password does not match');
return done(null, false);
}
} catch (err) {
console.log('Error:', err);
return done(err);
}
}
));
passport.use('client-basic', new BasicStrategy(
async function (clientId, clientSecret, done) {
try {
console.log(`Authenticating client: ${clientId}`);
const client = await Client.findOne({ id: clientId });
if (!client || client.secret !== clientSecret) {
console.log('Client not found or secret does not match');
return done(null, false);
}
return done(null, client);
} catch (err) {
console.log('Error:', err);
return done(err);
}
}
));
module.exports = passport;
JavaScript
//config/auth2.js
const oauth2orize = require("oauth2orize");
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const Client = require("../models/client");
const passport = require("../config/passport");
const server = oauth2orize.createServer();
server.grant(
oauth2orize.grant.code((client, redirectUri, user, ares, done) => {
const code = jwt.sign({ clientId: client.id, userId: user.id }, "secret", {
expiresIn: "10m",
});
done(null, code);
})
);
server.exchange(
oauth2orize.exchange.code((client, code, redirectUri, done) => {
try {
const payload = jwt.verify(code, "secret");
const token = jwt.sign(
{ clientId: client.id, userId: payload.userId },
"secret",
{ expiresIn: "1h" }
);
done(null, token);
} catch (err) {
done(err);
}
})
);
exports.authorization = [
passport.authenticate("basic", { session: false }),
server.authorization((clientId, redirectUri, done) => {
Client.findOne({ id: clientId }, (err, client) => {
if (err) {
return done(err);
}
if (!client) {
return done(null, false);
}
return done(null, client, redirectUri);
});
}),
server.decision(),
];
exports.token = [
passport.authenticate(["basic", "client-basic"], { session: false }),
server.token(),
server.errorHandler(),
];
JavaScript
// model/user.js
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
// Pre-save hook to hash the password
UserSchema.pre("save", async function (next) {
if (!this.isModified("password")) return next();
try {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
next();
} catch (err) {
next(err);
}
});
module.exports = mongoose.model("User", UserSchema);
JavaScript
// model/client.js
const mongoose = require('mongoose');
const ClientSchema = new mongoose.Schema({
id: { type: String, required: true, unique: true },
secret: { type: String, required: true },
redirectUris: [{ type: String }]
});
module.exports = mongoose.model('Client', ClientSchema);
JavaScript
//scripts/createUser.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const User = require('../models/user');
mongoose.connect('mongodb://localhost:27017/oauth-server', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const createUser = async () => {
try {
const hashedPassword = await bcrypt.hash('password1', 10);
const user = new User({
username: 'user1',
password: hashedPassword
});
await user.save();
console.log('User created');
} catch (err) {
console.error('Error creating user:', err);
} finally {
mongoose.connection.close();
}
};
createUser().catch(console.error);
JavaScript
//scripts/createClient.js
const mongoose = require('mongoose');
const Client = require('../models/client');
mongoose.connect('mongodb://localhost:27017/oauth-server', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const createClient = async () => {
const client = new Client({
id: 'client1',
secret: 'secret1',
redirectUris: ['http://localhost:3000/callback']
});
await client.save();
console.log('Client created');
mongoose.connection.close();
};
createClient().catch(console.error);
JavaScript
//routes/index.js
const express = require("express");
const router = express.Router();
const oauth2Controller = require("../controllers/oauth2");
router.get("/authorize", oauth2Controller.authorization);
router.post("/token", oauth2Controller.token);
module.exports = router;
JavaScript
//server.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const passport = require('passport');
const oauth2orize = require('oauth2orize');
const app = express();
app.use(bodyParser.json());
// Database setup
mongoose.connect('mongodb://localhost:27017/oauth-server', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// Passport configuration
require('./config/passport');
// Routes
const oauth2Routes = require('./routes/index');
app.use('/oauth2', oauth2Routes);
app.listen(3000, () => {
console.log('OAuth server is running on port 3000');
});
Step 5: Run the application
Implementing an OAuth Server With NodeJS and ExpressCreate the user
This example project can demonstrates the basic setup for the OAuth server with Node.js and Express using OAuth2orize, We can further enhance and secure this implementation based on the specified requirements.
Similar Reads
Subscription Management System with NodeJS and ExpressJS In this article, weâll walk through the step-by-step process of creating a Subscription Management System with NodeJS and ExpressJS. This application will provide users with the ability to subscribe to various plans, manage their subscriptions, and include features like user authentication and autho
5 min read
How to Implement Search and Filtering in a REST API with Node.js and Express.js ? Search and filtering are very basic features that an API must possess to serve data to the client application efficiently. By handling these operations on the server-side, we can reduce the amount of processing that has to be done on the client application, thereby increasing its performance.In this
5 min read
Getting Started with Express JS Express JS is a versatile, minimalist web framework for NodeJS that simplifies the development of back-end applications and APIs for web and mobile applications. Its flexibility and powerful features enable you to create robust and scalable web projects with minimal code, making it a popular choice
6 min read
How To Implement JWT Authentication in Express App? Authentication is important in web apps to make sure only the right people can access certain pages or information. It helps keep user data safe and prevents unauthorized access. Implementing JSON Web Token (JWT) authentication in an Express.js application secures routes by ensuring that only authen
6 min read
Customer Relationship Management (CRM) System with Node.js and Express.js CRM systems are important tools for businesses to manage their customer interactions, both with existing and potential clients. In this article, we will demonstrate how to create a CRM system using Node.js and Express. We will cover the key functionalities, prerequisites, approach, and steps require
15+ min read