How to Implement CORS and Authentication in NodeJS
Idris Olubisiđź’ˇ
Posted on January 8, 2022
In this tutorial, we will learn how to authenticate users, secure endpoints and Cross-Origin Resource Sharing (CORS) in NodeJs.
Prerequisites
You'll need the following to follow along with this tutorial:
- A working grasp of JavaScript.
- A good understanding of Node.js.
- A working knowledge of MongoDB or another database of your choice.
- Postman and a basic understanding of how to utilize it.
What is authentication and authorization
Security uses authentication and authorization, especially when gaining access to a system. But there's a big difference between getting into a house (authentication) and what you can do once you're there (authorization).
Authentication
Authentication is the process of confirming a user's identity by obtaining credentials and utilizing those credentials to validate the user's identity. If the certificates are valid, the authorization procedure begins.
You were already familiar with the authentication procedure because we all go through it daily, whether at work (logging onto your computer) or at home (passwords) (logging into a website). However, most "things" connected to the Internet require you to provide credentials to prove your identity.
Authorization
The process of granting authenticated users access to resources by verifying whether they have system access permissions is known as authorization. In addition, authorization allows you to restrict access privileges by granting or denying specific licenses to authenticated users.
After the system authenticates your identity, authorization occurs, providing you full access to resources such as information, files, databases, finances, locations, and anything else. On the other hand, approval impacts your ability to access the system and the extent to which you can do so.
Cross-Origin Resource Sharing (CORS)
CORS is an HTTP header-based system that allows a server to specify any other origins (domain, scheme, or port) from which a browser should enable resources to be loaded other than its own. CORS also uses a system in which browsers send a "preflight" request to the server hosting the cross-origin help to ensure that it will allow the actual request.
We will be using JSON web token standard for representing claims between two parties
What is JWT
JSON Web Tokens (JWT) are an open industry standard defined by RFC 7519 to represent claims between two parties. You can use jwt.io to decode, verify, and create JWT, for example.
JWT defines a concise and self-contained way for exchanging information between two parties as a JSON object. This information may be reviewed and trusted because it is signed. JWTs can be signed with a secret (using the HMAC algorithm) or a public/private key pair from RSA or ECDSA. We'll see some examples of how to use them in a bit.
Let's get started
Node.js development using a token for authentication
To get started, we'll need to set up our project.
Please navigate to a directory of your choice on your machine and open it in the terminal to launch Visual Studio Code.
Then execute:
code.
Note: If you don't have Visual Studio Code installed on your computer,
code .
won't work.
A - Create a directory and set it up npm
Create a directory and initialize npm
by typing the following command:
- Windows power shell
mkdir cors-auth-project
cd cors-auth-project
npm init -y
- Linux
mkdir cors-auth-project
cd cors-auth-project
npm init -y
B - Create files and directories
In step A, we initialized npm with the command npm init -y
, which automatically created a package.json.
We will create the model
, middleware
, config
directory and their files, for example, user.js
, auth.js
, database.js
using the commands below.
mkdir model middleware config
touch config/database.js middleware/auth.js model/user.js
We can now create the index.js
and app.js
files in the root directory of our project with the command.
touch app.js index.js
As seen in the illustration below:
C - Install dependencies
We'll install several dependencies like mongoose
, jsonwebtoken
, express
dotenv
bcryptjs
cors
and development dependency like nodemon
to restart the server as we make changes automatically.
Because I'll be utilizing MongoDB in this lesson, we'll install mongoose, and the user credentials will be checked against what we have in our database. As a result, the entire authentication process isn't limited to the database we'll use in this tutorial.
npm install cors mongoose express jsonwebtoken dotenv bcryptjs
npm install nodemon -D
D - Create a Node.js server and connect your database
Now, add the following snippets to your app.js
, index.js
, database.js
, and .env
files in that order to establish our Node.js server and connect our database.
In our database.js.
config/database.js
:
const mongoose = require("mongoose");
const { MONGO_URI } = process.env;
exports.connect = () => {
// Connecting to the database
mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => {
console.log("Successfully connected to database");
})
.catch((error) => {
console.log("database connection failed. exiting now...");
console.error(error);
process.exit(1);
});
};
In our app.js
:
auth-cors-project/app.js
require("dotenv").config();
require("./config/database").connect();
const express = require("express");
const app = express();
app.use(express.json());
// Logic goes here
module.exports = app;
In our index.js
:
auth-cors-project/index.js
const http = require("http");
const app = require("./app");
const server = http.createServer(app);
const { API_PORT } = process.env;
const port = process.env.PORT || API_PORT;
// server listening
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Our file, as you can see, requires various environment variables. If you haven't already, create a new .env
file and add your variables before running our application.
In our .env.
API_PORT=4001
MONGO_URI= // Your database URI
Edit the scripts object in our package.json
to look like the one below to start our server.
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
The above snippet was successfully inserted into theapp.js
, index.js
, and database.js
files. So, we started by creating our node.js server in index.js
and then importing the app.js
file, which already had routes configured.
Then, as mentioned in database.js, we used mongoose to build a database connection.
npm run dev
is the command to start our application.
Both the server and the database should be up and running without crashing.
E - Create user model and route
After registering for the first time, we'll establish our schema for the user details, and when logging in, we'll check them against the remembered credentials.
In the model folder, add the following snippet to user.js
.
model/user.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
first_name: { type: String, default: null },
last_name: { type: String, default: null },
email: { type: String, unique: true },
password: { type: String },
});
module.exports = mongoose.model("user", userSchema);
Let's create the routes for register
and login
, respectively.
In app.js
in the root directory, add the following snippet for the registration and login.
app.js
// importing user context
const User = require("./model/user");
// Register
app.post("/register", (req, res) => {
// our register logic goes here...
});
// Login
app.post("/login", (req, res) => {
// our login logic goes here
});
F - Implement register and login functionality
These two routes will be implemented in our application. Before storing the credentials in our database, we'll use JWT to sign and bycrypt
to encrypt.
We will: - Get user input from the /register
route.
- Verify the user's input.
- Check to see if the user has already been created.
- Protect the user's password by encrypting it.
- Make a user account in our database.
- Finally, construct a JWT token that is signed.
Modify the /register
route structure we created earlier, as shown below.
app.js
// ...
app.post("/register", async (req, res) => {
// Our register logic starts here
try {
// Get user input
const { firstName, lastName, email, password } = req.body;
// Validate user input
if (!(email && password && firstName && lastName)) {
res.status(400).send("All input is required");
}
// check if user already exist
// Validate if user exist in our database
const oldUser = await User.findOne({ email });
if (oldUser) {
return res.status(409).send("User Already Exist. Please Login");
}
//Encrypt user password
encryptedUserPassword = await bcrypt.hash(password, 10);
// Create user in our database
const user = await User.create({
first_name: firstName,
last_name: lastName,
email: email.toLowerCase(), // sanitize
password: encryptedUserPassword,
});
// Create token
const token = jwt.sign(
{ user_id: user._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "5h",
}
);
// save user token
user.token = token;
// return new user
res.status(201).json(user);
} catch (err) {
console.log(err);
}
// Our register logic ends here
});
// ...
Note: Update your
.env
file with aTOKEN_KEY
, which can be a random string.
Using Postman to test the endpoint, we'll get the below response after a successful registration.
We will: - Get user input for the /login
route.
- Verify the user's input.
- Check to see if the user is genuine.
- Compare the user password to the one we saved earlier in our database.
- Finally, construct a JWT token that is signed.
Make the /login
route structure that we defined earlier look like this.
// ...
app.post("/login", async (req, res) => {
// Our login logic starts here
try {
// Get user input
const { email, password } = req.body;
// Validate user input
if (!(email && password)) {
res.status(400).send("All input is required");
}
// Validate if user exist in our database
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.password))) {
// Create token
const token = jwt.sign(
{ user_id: user._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "5h",
}
);
// save user token
user.token = token;
// user
return res.status(200).json(user);
}
return res.status(400).send("Invalid Credentials");
// Our login logic ends here
});
// ...
Using Postman to test, we'll get the response shown below after a successful login.
G - Create middleware for authentication
A user can be created and logged in successfully. Despite this, we'll establish a route that requires a user token in the header, which will be the JWT token we created before.
Add the following snippet inside auth.js
.
middleware/auth.js
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const token =
req.body.token || req.query.token || req.headers["x-access-token"];
if (!token) {
return res.status(403).send("A token is required for authentication");
}
try {
const decoded = jwt.verify(token, config.TOKEN_KEY);
req.user = decoded;
} catch (err) {
return res.status(401).send("Invalid Token");
}
return next();
};
module.exports = verifyToken;
Create the /welcome
route and edit app.js with the following code to test the middleware.
app.js
const auth = require("./middleware/auth");
app.post("/welcome", auth, (req, res) => {
res.status(200).send("Welcome to FreeCodeCamp 🙌");
});
When we try to access the /welcome route we just built without sending a token in the header with the x-access-token key, we get the following response.
We can now re-test by adding a token in the header with the key x-access-token.
The response is seen in the image below.
Implementing Cross-Origin Resource Sharing (CORS)
CORS is a node.js package that provides a Connect/Express middleware that can be used to enable CORS with a variety of parameters.
- Easy to Use (Enable All CORS Requests)
Adding the following snippet to app.js
allows us to add cors to our application and enable all CORS requests.
// ...
const cors = require("cors") //Newly added
const app = express();
app.use(cors()) // Newly added
app.use(express.json({ limit: "50mb" }));
// ...
- Enable CORS for a Single Route
Using the /welcome
route as an example, We may activate CORS for a single route in our application by adding the following snippet in app.js.
app.get('/welcome', cors(), auth, (req, res) => {
res.status(200).send("Welcome to FreeCodeCamp 🙌 ");
});
- Configuring CORS
As shown below, we can set options in the cors package by adding parameters to configure it.
// ...
const corsOptions = {
origin: 'http://example.com',
optionsSuccessStatus: 200 // for some legacy browsers
}
app.get('/welcome', cors(corsOptions), auth, (req, res) => {
res.status(200).send("Welcome to FreeCodeCamp 🙌 ");
});
// ...
Kindly check out NPM CORS PACKAGE to read more about Cross-Origin Resource Sharing.
You can click here to check the complete code on GitHub.
Conclusion
In this article, we learned about JWT, authentication, authorization, and CORS and how to create an API in Node.js that uses a JWT token for authentication.
Thank you!
I'd love to connect with you at Twitter | LinkedIn | GitHub | Portfolio
See you in my next blog article. Take care!!!
Posted on January 8, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 26, 2024