JWT based user Authentication using ReactJs, Node Express, and MySQL
Part 2: User registration and login page with Cookies, Sessions, and Hashing
This series of articles demonstrates a JWT based user authentication system using ReactJS, Node Express, and MySQL. This series contains three main articles. Last time during the first article, we explained how to build a user registration and login page without JWT. During the second article, we will explain how to add Cookies, Sessions, and hashing to the user registration and login page, and in the third article, we will explain integrating JWT into the existing system.
In this second article, the following topics will be covered.
- Hashing a password using Bcrypt
- Implement the Sessions and Cookies
Note: You can access the first article code here.
1. Hashing a password using Bcrypt
The authentication is to provide users to verify that they provide the correct credentials whenever they want access to the application using the username and password. For that, we need a way to store these usernames and passwords in our database for future comparisons. However, storing passwords on the server-side for authentication is a difficult task. Let’s explore one of the mechanisms that make password storage secure and easier by hashing.
1.1 Install and include the bcrypt library
First, we need to install “bcrypt” inside the server folder using the following command.
npm install --save bcrypt
Second, we need to import the bcrypt module inside the server/index.js.
const bcrypt = require('bcrypt');
1.2. Set a value for saltRounds
We need to define a variable called “saltRound” which defines the number of hashing rounds is done during the bcrypt hashing. Higher values of “saltRound” take more time to the hashing algorithm. In this application, we use ten as our “saltRound” value.
const saltRound = 10;
1.3 Hash the Password
To use the “bcrypt”, we need to change the “/register ” route inside the server/ index.js.
..........
app.post('/register', (req, res)=> {
..........bcrypt.hash(password,saltRound, (err, hash) => { if (err) {
console.log(err)
}
db.execute(
"INSERT INTO users (username, password) VALUES (?,?)",
[username, hash],
(err, result)=> {
console.log(err);
}
);
})
});
..........
Inside the “/register” route, we need to call bcrypt.hash() function. To call that function, we need to pass the password, saltRound, and a callback function to insert the username and hashed password into the database or log the error in the console if an error occurs.
To insert the user information into a database, we need to execute the MySQL insert query with values username and hashed password to the “users” table. Here, “users” is the MySQL table we use to store user information, and “(username, password)” are the column names of the “users” table.
Finally, when we log into an account we need to compare the username and password which is entered with the existing user information in the database. So, the following changes should be done to our “/login” route inside the server/ index.js file.
..........
app.post('/login', (req, res) => {
..........
db.execute(
"SELECT * FROM users WHERE username = ?;",
[username],
(err, result)=> {
if (err) {
res.send({err: err});
} if (result.length > 0) {
bcrypt.compare(password, result[0].password, (error, response) => {
if (response) {
res.send(result);
} else{
res.send({message: "Wrong username/ password comination!"});
}
});
} else {
res.send({ message: "User doesn't exists"});
}
}
);
});
..........
The SELECT statement is used to select data from the table and “users” is the table name. “*” character is used to select ALL columns from a table. And bcrypt compares the password is correctly entered with the username.
Note: In the table of MySQL workbench password column, data type should be VARCHAR(500) or more than 500.
2. Implementing the Sessions and Cookies
2.1. Prepare libraries
Before the setup of “sessions” and “cookies”, we need to install three packages to our server application as follows.
npm install --save express-session body-parser cookie-parser
Here, the “body-parser” is responsible for parsing the incoming request bodies in a middleware before you handle it. Then the “cookie-parser” is used to parse the cookie header to store data on the browser whenever a session is established on the server-side. And “express-session” is an HTTP server-side framework used to create and manage a session middleware.
Then we need to import all of these packages to our server/ index.js file as follows.
const bodyParser = require(“body-parser”);
const cookieParser = require(“cookie-parser”);
const session = require(“express-session”);
Second, we need to add the cookie-parser and body-parser modules to the server/ index.js file. Here, extended allows choosing between parsing the URL-encoded data with the qs library (when true).
app.use(
..........
);app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));..........
});
2.2. Initialize the session
We need to initialize the session as follows inside the server/ index.js file.
app.use (
session ({
key: "userId",
secret: "subscribe",
resave: false,
saveUninitialized: false,
cookie: {
expires: 60 * 60 * 24,
},
})
);
Here, the “key” is the name of the cookie and the “secret” is very important and it’s used to access data from the server-side. So, for real applications use a very strong secret key. Furthermore, resave enables the session to be stored back to the session store, even if the session was never modified during the request. It takes a Boolean value. The “saveUninitialized” allows any uninitialized session to be sent to the store. Finally, the cookie sets the cookie expiry time. Let’s set it as a 24 hr (60 x 60 x 24 seconds). After the cookie-parser dependency and body-parser add the following code in the server/ index.js file.
2.3. Create a session
In the logging part, before we send the results we need to create the session called “user” and this contains the result that we fetch. So, we can call the session forever until the cookie expires in the server/ index.js file as follow.
app.post('/login', (req, res) => {
..........
db.execute(
..........if (result.length > 0) {
bcrypt.compare(password, result[0].password, (error, response) => {
if (response) {
req.session.user = result;
console.log(req.session.user);
res.send(result);
} else{
..........
}
});
} else {
..........
}
}
);
});
2.4. sending cookies using Axios
To send the information from the client application to the server application to see a session exists, we have to be very careful. The XMLHttpRequest from a different domain cannot set cookie values for their domain unless withCredentials is set to true before making the request. So, the following changes were done to the client/src/App.js file.
function App() {
..........
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
..........};
2.5. Check already logged users using Axios
Then, we need to create a useEffect to run whenever we refresh the page. So, the following changes were done to the client/src/App.js file. In here “SET ROLE” changes the active roles within the current session.
function App() {
..........useEffect(() => {
Axios.get("http://localhost:3001/login").then((response) => {
if (response.data.loggedIn == true) {
setRole(response.data.user[0].role);
}
});
}, []);
return (
..........
}export default App;
2.6. Indicate login status using session variables
Finally, We need to create an “app. get” function inside the server application as the following code inside the server/ index.js file.
app.post('/register', (req, res)=> {
..........
});app.get("/login", (req, res) => {
if (req.session.user) {
res.send({ loggedIn: true, user: req.session.user });
} else {
res.send({ loggedIn: false });
}
});app.post('/login', (req, res) => {
..........
});
app.listen(3001, () => {
console.log("running server");
});
Tutorial followed by the “PedroTech” YouTube channel.
Here is the link: https://www.youtube.com/watch?v=W-sZo6Gtx_E
You can access the full code here and the link to the Third part will be added soon.