In this article, we will look through the authentication process. We will work on Nodejs and we will be using express, bcrypt, express-session jwt and other necessary packages. Here we will first look through the basic authentication process to understand the authentication process.
Once after that, we will learn what should and should not do on the real application, how to hash the password before saving it to the database and validate on login. Then we will look through authentication using JWT, for API (bearer authentication).
Prerequisite
If you are looking at this post, I assume that you already have some basic understanding of Nodejs. If you are here just to understand the flow of authentication, you can follow along as well.
Authentication
Authentication is the process of determining whether someone or something is, in fact, who or what it says it is. Authentication technology provides access control for systems by checking to see if a user's credentials match the credentials in a database of authorized users or in a data authentication server.
Authentication Vs Authorization, I think talking about Authorization may also help us to understand the authentication.
Authentication is the process of verifying that "you are who you say you are", Authorization is the process of verifying that "you are permitted to do what you are trying to do".
Info! Only necessary code snippet are show. Find full project on Github.
Basic Authentication
We are using express, express-session, ejs is used as templating engine.
importexpressfrom'express';
importsessionfrom'express-session'
constapp = express();
app.set("view engine", "ejs");
app.use(express.urlencoded({
extended:true
}));
app.use(session({
secret:"aSecret-UseENV",
resave:false,
saveUninitialized:false
}))
Let's implement basic authentication. Here we will signup and login.
app.post("/signup", async (req, res, next) => {
letname = req.body.name;
letemail = req.body.email;
letpassword = req.body.password;
letnewUser = newUser({
name:name,
email:email,
password:password
})
try {
letuser = awaitnewUser.save();
res.render('signup-result', {
user:user,
message:"Account Created",
message2:false,
success:true
});
} catch (error) {
res.render('signup-result', {
message:"Account Creation Failed",
message2:'The Email may have been already used.',
success:false
});
}
})
Here, we simply get the name, email, and password from the form and save it to the database.
When the user login, we can now get the provided email and password, compare it to the database, and if the data is matched, we can authenticate the user and set the season.
As you can see on the above code snippet, we are finding the user with that email and that password. If Found, the user is found else, an error message is thrown.
Now, let's focus on req.session.user = user; this line. As we are using the express-session as middleware, the req object has the session object. Here we are assigning the user property on the session object to the user (the user found by matching the password and the email).
In doing so, (on modifying the session object) express-session will do two things.
Create a session Id for this request and attach that session id as a cookie to the response object.
Save the session object, in such a way that it can get this exact data using the session id. for simplicity it is saved as key-value pair, session Id being the key and the session object being the value.
As we know the cookies, it will be sent to the subsequent requests, In the express-session middleware, it checks for the session Id, if it matches with anything that is on the express-session data store. It will populate the session object with the saved value.
Thus, we can check the object on the session object, If it is defined, the user is authenticated, else he/she is not.
constisAuth = (req, res, next) => {
if (req.session.user) {
//if user exists on session
next();
} else {
// else redirect to login
res.redirect('/login');
}
}
// a protected route
app.get("/me", isAuth, (req, res, next) => {
letuser = req.session.user;
res.render('me', {
user:user
});
})
That's it. If you understand that's good. else I would suggest you to read the Basic Authentication section again.
Hashing the Password with bcrypt
In the above section, we saved the plain text of the password in the database. In the real world application, we will never save the user passwords on plain text. We should hash the password before saving it to the database.
import { hash, compare } from'bcrypt';
app.post("/signup", async (req, res, next) => {
letname = req.body.name;
letemail = req.body.email;
letpassword = req.body.password;
// let's make use of bcrypt to hash
lethashedPassword = awaithash(password, 10);
letnewUser = newUser({
name:name,
email:email,
password:hashedPassword
})
try {
letuser = awaitnewUser.save();
res.render('signup-result', {
user:user,
message:"Account Created",
message2:false,
success:true
});
} catch (error) {
res.render('signup-result', {
message:"Account Creation Failed",
message2:'The Email may have been already used.',
success:false
});
}
})
app.post("/login", (req, res, next) => {
letemail = req.body.email;
letpassword = req.body.password;
//now we will get the user, then compare the hashedPassword (saved in DB)
Now, on signup, we will hash the password before saving it to the database. On log in, we will get the user based on the email only. Then we can use the compare method from bcrypt to validate if the entered password and hashed password are the same or not. If the method returns true, the password matched, else the password did not match with the email.
Bearer Authentication / JWT Authentication
Or we can say API authentication.
The signup will be the same but will respond as API this time. In Login, we get the user information, sign it using a secret. and respond back. We are encoding the data in such a way that the data can be decoded using only the same secret.
More preciously if the token will be invalid if the user attempts to modify it in any way. The token will be invalid when validating if the wrong secret (other than the server's secret) is used to sign it.
constJWT_SECRET = 'secretFromEnv';
app.post("/signup", async (req, res, next) => {
letname = req.body.name;
letemail = req.body.email;
letpassword = req.body.password;
// let's make use of bcrypt to hash
lethashedPassword = awaithash(password, 10);
letnewUser = newUser({
name:name,
email:email,
password:hashedPassword
})
try {
letuser = awaitnewUser.save();
user = user.toObject();
let { password, ...userWithoutPass } = user;
console.log(userWithoutPass);
res.status(201).json({
message:"Account created",
user:userWithoutPass
})
} catch (error) {
console.log(error.toString());
//
res.status(400).json({
message:"error on creating account."
})
}
})
app.post("/login", (req, res, next) => {
letemail = req.body.email;
letpassword = req.body.password;
//now we will get the user, then compare the hashedPassword (saved in DB)
In the isAuth middleware, we check if the authentication header is presented. If it is, we will get the bearer token, remove the bearer part, verify the token. and allow to next chain if validated.
As we can see in the above image, we can send API requests to protected routes using the Bearer token as an authentication mechanism.
Conclusion
In This article, we look through various methods of authentication. Here we look at template-based authentication, using express-session bcrypt and ejs.
We Implement Bearer Authentication using JWT. Here we try to understand the authentication workflow as well.
0 Comments