Hi, Today we are going to implement API authentication with JWT in node.js application. Authentication is most important feature in every application. Even if you are beginner feel free to try this tutorial, we gonna start from scratch. We also gonna write Auth middleware, which allow only authenticated people to access the route.
Authentication With JWT (Json Web Token) In React
For better understanding watch Demo Video
Source Code
Let's Start Coding...
App Overview :
Project Structure
Following table shows the overview of the Rest APIs that be exported :
Methods | Urls | Actions |
---|---|---|
POST | /api/users | Create user |
POST | /api/auth | Authenticate user |
GET | /api/users/me | Get authenticated user details |
Create Node.js App and Install dependencies
$ mkdir node-auth-jwt
$ cd node-auth-jwt
$ npm init --yes
$ npm install express mongoose jsonwebtoken bcrypt joi dotenv
express : Express is minimal and flexible Node.js web applicaton framework.
mongoose : Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js.
jsonwebtoken : It's a compact URL of representing claims to be transferred between two parties.
bcrypt : It's a password hashing function.
joi : Joi is an object schema description language and validator for javascript objects.
dotenv : It loads environment variables from a .env file.
Setup Express Web Server
/index.js
require("dotenv").config();
const express = require("express");
const app = express();
app.use(express.json());
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}...`));
Configure Environmental Variables
/.env
DB = "mongodb://localhost/node-auth-api/"
JWTPRIVATEKEY = "secretkey"
SALT = 10
Configure MongoDB Database
/db.js
const mongoose = require("mongoose");
module.exports = async () => {
try {
const connectionParams = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
};
await mongoose.connect(process.env.DB, connectionParams);
console.log("connected to database.");
} catch (error) {
console.log("could not connect to database", error);
}
};
Import db.js in index.js and call it
//...
const connection = require("./db");
const express = require("express");
const app = express();
connection();
app.use(express.json());
//...
Create User Model
/models/user.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const jwt = require("jsonwebtoken");
const Joi = require("joi");
const userSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
});
userSchema.methods.generateAuthToken = function () {
const token = jwt.sign({ _id: this._id }, process.env.JWTPRIVATEKEY);
return token;
};
const User = mongoose.model("user", userSchema);
const validate = (user) => {
const schema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().required(),
});
return schema.validate(user);
};
module.exports = { User, validate };
What we have done :
- We have created user table with name, email and password.
- With JWT, we generate token with payload of user id.
- With Joi, we gonna validate data.
Register Route
/routes/users.js
const { User, validate } = require("../models/user");
const bcrypt = require("bcrypt");
const express = require("express");
const router = express.Router();
router.post("/", async (req, res) => {
try {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
const user = new User(req.body);
const salt = await bcrypt.genSalt(Number(process.env.SALT));
user.password = await bcrypt.hash(user.password, salt);
await user.save();
res.send(user);
} catch (error) {
console.log(error);
res.send("An error occured");
}
});
module.exports = router;
Login Route
/routes/auth.js
const { User } = require("../models/user");
const bcrypt = require("bcrypt");
const Joi = require("joi");
const express = require("express");
const router = express.Router();
router.post("/", async (req, res) => {
try {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).send("Invalid email or password");
const validPassword = await bcrypt.compare(
req.body.password,
user.password
);
if (!validPassword)
return res.status(400).send("Invalid email or password");
const token = user.generateAuthToken();
res.send(token);
} catch (error) {
console.log(error);
res.send("An error occured");
}
});
const validate = (user) => {
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required(),
});
return schema.validate(user);
};
module.exports = router;
Auth Middleware
/middleware/auth.js
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
try {
const token = req.header("x-auth-token");
if (!token) return res.status(403).send("Access denied.");
const decoded = jwt.verify(token, process.env.JWTPRIVATEKEY);
req.user = decoded;
next();
} catch (error) {
res.status(400).send("Invalid token");
}
};
User Get Route
/routes/users.js
const auth = require("../middleware/auth");
//...
router.get("/me", auth, async (req, res) => {
try {
const user = await User.findById(req.user._id).select("-password -__v");
res.send(user);
} catch (error) {
console.log(error);
res.send("An error occured");
}
});
module.exports = router;
Import Routes in Index.js
//...
const users = require("./routes/users");
const auth = require("./routes/auth");
//...
app.use(express.json());
app.use("/api/users", users);
app.use("/api/auth", auth);
//...
That's it Run the server and test the APIs. If you found any mistakes or making code better please let me know in comment. I hope you have learned something.
Thank you...
Top comments (5)
Easy to understand for getting start node js express
Amazing article 👍. Spot on
In the Auth.js file what mean req.user = decoded?
How do you redirect to login page in the authentication middleware when the token is no longer valid, I mean instead of just sending status code 403, I want to redirect the user to login page.
Is this an advanced form? and can it be used in a large applications?