Table Of Contents
- What is authentication & authorization ?!
- What is (JWT)JSON Web Token?
- Why using Authentication & Authorization ?
- How apply Authentication ?
- How apply Authorization ?
- Conclusion
Introduction
User authentication & authorization is one of the important part of any web application.
There are several kinds of way to handle authentication, we can rely on third party service like Passport.
But in this article we will use very simple & self developed approach, which will help us to understand core part of authentication.
Technology Used
Token: JWT
Password Hash: bcryptjs
Database: MongoDB
What is authentication & authorization ?!
In simple words, Authentication is the process of verifying who a user is (who you are), and
Authorization is the process of verifying what they have access to (what you are allowed to do).
What is (JWT)JSON Web Token ?!
JWTs are a good way of securely transmitting information between parties because they can be signed (Information Exchange). Even though we can use JWT with any type of communication method, today JWT is very popular for handling authentication and authorization via HTTP.
Here is the diagram of how JWT works:
Structure of a JWT :-
The first section of the JWT is the header, which is a Base64-encoded string.
which contains the hashing algorithm, which was used to generate the sign and the type of the token.
If you decoded the header it would look something similar to this:
{ "alg": "HS256", "typ": "JWT"}
The second section is the payload that contains the JSON object of user data like (id or id_type) that was sent back to the user.
Since this is only Base64-encoded, it can easily be decoded by anyone.
{"sub": "1234567890", "name": "John Doe", "userId": "1516239022", "user_type_id":1 }
- The final section is the signature of the token using the algorithm that is mentioned in the header section
Why using Authentication & Authorization ?!
To protect the data in a security system, administrators should be able to, among other things, implement detailed user access privileges, select the information that can be shared internally with partners and authorities, and control how long data is kept.
A common example is entering a username and password when you log in to a website.
Entering the correct login information lets the website know
1) Who you are and
2) That it is actually you accessing the website.
There will be two types of users (admin & user)
Authentication that all user need to login and register first ,
Authorization that administrators and the regular user. Administrators will be able to view special events
whereas the regular user will be able to view ordinary events
To get started in this trip, I have REST APIs already created (including 4 Routes) GitHub repo
1- POST login route ( everyone has access)
2- POST register route ( everyone has access)
3- GET events array (only regular user has access)
4- GET special events array (only admin user has access)
How apply Authentication ?!
Step (1)
To get started, in your terminal initialize an empty Node.js project with default settings:
$ npm init -y
Then, let's install the Express framework, JWT, bcryptjs and mongoose:
$ npm install --save express jsonwebtoken bcryptjs
Then To create basic structure for different kind of endpoints like registration or login we will use express as router. And we will create a folder for routers (routers/index.js)
Then, let's create a file called
(middleware/auth.js)
, which will be our authentication service and create a file called(controllers/user)
, which will be our controller for user functionsNow, let's create our Server and use these modules and configure them in the Express app (server.js):
const express = require('express');
const app = express();
app.use(express.json());
// Import Routes
const authRoute = require('./routes/index');
// Route Middlewares
app.use('/api', authRoute);
const port = 3000;
app.listen(port, function(){console.log("Server running on localhost:" + port);});
Step (2)
- Now go for folder for routers
(routers/index.js)
to configure express Router import userController
const router = require('express').Router();
const userController = require('../controllers/user');
// Register a new User
router.post('/register', userController.register);
// Login
router.post('/login', userController.login);
module.exports = router;
Step (3)
Now go for folder for routers (controllers/user)
to add userController functions
1.Connect to DB
mongoose.connect(db, function(err){
if(err){
console.error('Error! ' + err)
} else {
console.log('Connected to mongodb')
}
});
- Create register function
- Hash password using bcrypt module
- Create an user object
- Save User in the database
- Create payload then Generate an access token (if you ask about what is payload go for Structure of a JWT section)
exports.register = async (req, res) => {
//Hash password
const salt = await bcrypt.genSalt(10);
const hasPassword = await bcrypt.hash(req.body.password, salt);
// Create an user object
let user = new User({
email: req.body.email,
name: req.body.name,
password: hasPassword,
user_type_id: req.body.user_type_id
})
// Save User in the database
user.save((err, registeredUser) => {
if (err) {
console.log(err)
} else {
// create payload then Generate an access token
let payload = { id: registeredUser._id, user_type_id: req.body.user_type_id || 0 };
const token = jwt.sign(payload, config.TOKEN_SECRET);
res.status(200).send({ token })
}
})
}
After the authentication service is up and running, let's send a POST request and see if registration works or not.
- I will be using the rest-client Postman to do this. Feel free to use any rest-client you prefer or something like Insomnia to do this.
- Let's send a post request to the
http://localhost:3000/api/register
endpoint with the following JSON:{ "email":"d@a.hh", "name" : "lotfy", "password": "123456", "user_type_id":1 }
- You should get the access token as the response:
{ "Token": "eyJhbGciOiJIUz..." }
- Let's send a post request to the
Step (4)
Now we register for new user and token receive token as response
we need to login with user credentials
- create login function
- compare Hashed password with credentials
- create payload then Generate an access token and return token in headers
exports.login = async (req, res) => {
User.findOne({ email: req.body.email }, async (err, user) => {
if (err) {
console.log(err)
} else {
if (user) {
const validPass = await bcrypt.compare(req.body.password, user.password);
if (!validPass) return res.status(401).send("Mobile/Email or Password is wrong");
// Create and assign token
let payload = { id: user._id, user_type_id: user.user_type_id };
const token = jwt.sign(payload, config.TOKEN_SECRET);
res.status(200).header("auth-token", token).send({ "token": token });
}
else {
res.status(401).send('Invalid mobile')
}
}
})
}
let's send a POST request and see if Login works or not.
- Let's send a post request to the
http://localhost:3000/api/login
endpoint with the following JSON:{ "email":"d@a.hh", "password": "123456" }
- You should get the sucess with 200 code and access token in the header of response:
"auth-token": "eyJhbGciOiJIUz..."
How apply Authorization ?!
As we mentioned in Authentication steps (register/login)
We added property called "user_type_id" which is identifier for type of user is request this data
Step (1)
Now we need if the request for logged in user or some hack my APIs
let's move to our auth file in (middleware/auth.js)
- Check if request have an access token
- Remove Bearer from string which added in header of request
- Verify User Token with my token
secretKey
exports.verifyUserToken = (req, res, next) => {
let token = req.headers.authorization;
if (!token) return res.status(401).send("Access Denied / Unauthorized request");
try {
token = token.split(' ')[1] // Remove Bearer from string
if (token === 'null' || !token) return res.status(401).send('Unauthorized request');
let verifiedUser = jwt.verify(token, config.TOKEN_SECRET); // config.TOKEN_SECRET => 'secretKey'
if (!verifiedUser) return res.status(401).send('Unauthorized request')
req.user = verifiedUser; // user_id & user_type_id
next();
} catch (error) {
res.status(400).send("Invalid Token");
}
}
- Now we need know type for logged-in user
exports.IsUser = async (req, res, next) => {
if (req.user.user_type_id === 0) {
next();
}
return res.status(401).send("Unauthorized!");
}
exports.IsAdmin = async (req, res, next) => {
if (req.user.user_type_id === 1) {
next();
}
return res.status(401).send("Unauthorized!");
}
Step (2)
Now we verified user but need to know is this route for administrators or regular user
with my authentication middleware to check routes with user type to file (routers/index.js)
- If these valid conditions call get user events function
// Auth user only
router.get('/events', verifyUserToken, IsUser, userController.userEvent);
// Auth Admin only
router.get('/special', verifyUserToken, IsAdmin, userController.adminEvent);
let's send a request and see if special route works for admin user with user_type_id == 1.
Let's send a post request to the
http://localhost:3000/api/special
endpoint with the following authorization header with TYPEBearer Token
and value of token:eyJhbGciOiJIUz...
You should get the success with 200 code and the special events array as a response:
{ "_id": "1", "name": "Auto Expo Special",
"description": "lorem ipsum", "date": "2012-04-23T18:25:43.511Z" } .....
Conclusion ?!
In this article, we have introduced you to JWT and how to implement JWT with Express. I hope that now you have a piece of good knowledge about how JWT works
The end Thank you for going through this tutorial. If you notice any errors please report them to me. If you got stuck on any step, please refer to this GitHub repo.
You can contact me through:
Gmail: mhmdlotfy9@gmail.com
or Linkedin
Top comments (6)
Good Job 👌
Perfect 👏👌
thanks 😍
Perfect, Thank You
I realy like it ,Thank you very much.
Thank you!