Adding authentication to an application is one of the most challenging 😖 but also a very important part for developers, but today I will teach you 🥰 how to do it, come on let's make an authentication page with me today in just 10 minutes ⚡.
1.Let's initialize npm and install all the necessary packages that we are going to use.
npm init -y
npm i express bcryptjs body-parser dotenv ejs jsonwebtoken mongoose cookie-parser
2.Now create 2 directories views and public and also create server.js file now your folder structure should look like this 👇.
3.Now include the packages in your server.js and create an express server
here we included all the packages and required code to configure our express server that we will need throughout the journey in this article 🤠.
const express = require('express');
const bodyparser=require("body-parser");
const mongoose= require('mongoose');
const jwt = require('jsonwebtoken');
var cookieParser = require('cookie-parser');
const port = process.env.PORT || 3000;
const app = express();
require('dotenv').config();
const bcrypt = require('bcryptjs');
const salt = 10;
app.set('view engine', 'ejs');
app.use(bodyparser.urlencoded({extended:true}));
app.use(express.json());
app.use(cookieParser());
app.use(express.static("public"));
app.listen(port,()=>{
console.log(`Running on port ${port}`);
})
4.Now Create 3 files in the views folder 👇.
5.Now lets create our login signup and the protected page.
// signin.ejs
<form action="/login" method="post">
<label for="">Email</label>
<input type="email" name="email" id="">
<label for="">Password</label>
<input type="text" name="password" id="">
<button type="submit">SignIN</button>
</form>
<form action="/signup" method="get">
<button type="submit">
Do not have an account
</button>
</form>
// signup.ejs
<form action="/signup" method="post">
<label for="">Email</label>
<input type="email" name="email" id="">
<label for="">Password</label>
<input type="text" name="password" id="">
<button type="submit">SignUP</button>
</form>
//home.ejs
This is the protected page
6.Now we will create our .env file and store our secret key of JWT and mongodb connection url and add to our server.
// get our urls and secrets
const JWT_SECRET=process.env.jwt;
const MONGODB_URL=process.env.mongodb;
// making connnection with our database
mongoose.connect(MONGODB_URL, {useFindAndModify: false,useNewUrlParser: true, useUnifiedTopology: true,useCreateIndex: true});
Now your server should look like this 👇.
const express = require('express');
const bodyparser=require("body-parser");
const mongoose= require('mongoose');
const jwt = require('jsonwebtoken');
var cookieParser = require('cookie-parser');
const port = process.env.PORT || 3000;
const app = express();
require('dotenv').config();
const bcrypt = require('bcryptjs');
const salt = 10;
app.set('view engine', 'ejs');
app.use(bodyparser.urlencoded({extended:true}));
app.use(express.json());
app.use(cookieParser());
app.use(express.static("public"));
// get our urls and secrets
const JWT_SECRET=process.env.jwt;
const MONGODB_URL=process.env.mongodb;
// making connnection with our database
mongoose.connect(MONGODB_URL, {useFindAndModify: false,useNewUrlParser: true, useUnifiedTopology: true,useCreateIndex: true});
app.listen(port,()=>{
console.log(`Running on port ${port}`);
})
7.Now we will create Our Schema for User Authentication and our signup method.
// Schema For User Auth
const userSchema = new mongoose.Schema({
email:{type:String,required:true,unique:true},
password:{type:String,required:true}
},{collection:'users'}
const User= mongoose.model("User",userSchema);
)
app.post('/signup',async (req,res)=>{
// geting our data from frontend
const {email,password:plainTextPassword}=req.body;
// encrypting our password to store in database
const password = await bcrypt.hash(plainTextPassword,salt);
try {
// storing our user data into database
const response = await User.create({
email,
password
})
return res.redirect('/');
} catch (error) {
console.log(JSON.stringify(error));
if(error.code === 11000){
return res.send({status:'error',error:'email already exists'})
}
throw error
}
})
8.Now we will create our Login method here we will use JWT to create an auth token and store it in our browser as a cookie
// user login function
const verifyUserLogin = async (email,password)=>{
try {
const user = await User.findOne({email}).lean()
if(!user){
return {status:'error',error:'user not found'}
}
if(await bcrypt.compare(password,user.password)){
// creating a JWT token
token = jwt.sign({id:user._id,username:user.email,type:'user'},JWT_SECRET,{ expiresIn: '2h'})
return {status:'ok',data:token}
}
return {status:'error',error:'invalid password'}
} catch (error) {
console.log(error);
return {status:'error',error:'timed out'}
}
}
// login
app.post('/login',async(req,res)=>{
const {email,password}=req.body;
// we made a function to verify our user login
const response = await verifyUserLogin(email,password);
if(response.status==='ok'){
// storing our JWT web token as a cookie in our browser
res.cookie('token',token,{ maxAge: 2 * 60 * 60 * 1000, httpOnly: true }); // maxAge: 2 hours
res.redirect('/');
}else{
res.json(response);
}
})
9.And finally we will make routes for our remaining pages and check for auth for getting into our protected page
const verifyToken = (token)=>{
try {
const verify = jwt.verify(token,JWT_SECRET);
if(verify.type==='user'){return true;}
else{return false};
} catch (error) {
console.log(JSON.stringify(error),"error");
return false;
}
}
// get requests
app.get('/',(req,res)=>{
const {token}=req.cookies;
if(verifyToken(token)){
return res.render('home');
}else{
res.redirect('/login')
}
})
app.get('/login',(req,res)=>{
res.render('signin');
})
app.get('/signup',(req,res)=>{
res.render('signup')
})
app.listen(port,()=>{
console.log(`Running on port ${port}`);
})
10.Finally Your server.js should look like this 👇.
const express = require('express');
const bodyparser=require("body-parser");
const mongoose= require('mongoose');
const jwt = require('jsonwebtoken');
var cookieParser = require('cookie-parser');
const port = process.env.PORT || 3000;
const app = express();
require('dotenv').config();
const bcrypt = require('bcryptjs');
const salt = 10;
app.set('view engine', 'ejs');
app.use(bodyparser.urlencoded({extended:true}));
app.use(express.json());
app.use(cookieParser());
app.use(express.static("public"));
// get our urls and secrets
const JWT_SECRET=process.env.jwt;
const MONGODB_URL=process.env.mongodb;
// making connnection with our database
mongoose.connect(MONGODB_URL, {useFindAndModify: false,useNewUrlParser: true, useUnifiedTopology: true,useCreateIndex: true});
// Schema For User Auth
const userSchema = new mongoose.Schema({
email:{type:String,required:true,unique:true},
password:{type:String,required:true}
},{collection:'users'}
)
const User= mongoose.model("User",userSchema);
app.post('/signup',async (req,res)=>{
// geting our data from frontend
const {email,password:plainTextPassword}=req.body;
// encrypting our password to store in database
const password = await bcrypt.hash(plainTextPassword,salt);
try {
// storing our user data into database
const response = await User.create({
email,
password
})
return res.redirect('/');
} catch (error) {
console.log(JSON.stringify(error));
if(error.code === 11000){
return res.send({status:'error',error:'email already exists'})
}
throw error
}
})
// user login function
const verifyUserLogin = async (email,password)=>{
try {
const user = await User.findOne({email}).lean()
if(!user){
return {status:'error',error:'user not found'}
}
if(await bcrypt.compare(password,user.password)){
// creating a JWT token
token = jwt.sign({id:user._id,username:user.email,type:'user'},JWT_SECRET,{ expiresIn: '2h'})
return {status:'ok',data:token}
}
return {status:'error',error:'invalid password'}
} catch (error) {
console.log(error);
return {status:'error',error:'timed out'}
}
}
// login
app.post('/login',async(req,res)=>{
const {email,password}=req.body;
// we made a function to verify our user login
const response = await verifyUserLogin(email,password);
if(response.status==='ok'){
// storing our JWT web token as a cookie in our browser
res.cookie('token',token,{ maxAge: 2 * 60 * 60 * 1000, httpOnly: true }); // maxAge: 2 hours
res.redirect('/');
}else{
res.json(response);
}
})
const verifyToken = (token)=>{
try {
const verify = jwt.verify(token,JWT_SECRET);
if(verify.type==='user'){return true;}
else{return false};
} catch (error) {
console.log(JSON.stringify(error),"error");
return false;
}
}
// get requests
app.get('/',(req,res)=>{
const {token}=req.cookies;
if(verifyToken(token)){
return res.render('home');
}else{
res.redirect('/login')
}
})
app.get('/login',(req,res)=>{
res.render('signin');
})
app.get('/signup',(req,res)=>{
res.render('signup')
})
app.listen(port,()=>{
console.log(`Running on port ${port}`);
})
Hurray!! You have successfully added authentication in your website 🥳🥳🥳🥳.
Connect me on Twitter :-
https://twitter.com/nyctonio
Do check out my Github for source code :-https://github.com/nyctonio/jwtauth
Top comments (29)
JWT For session is a bad design.
Don't do that. It's discouraging!
Next time, try to say kudos for the write-up, then suggest something better 🙂
🥺 thank you
No this is not a session based authorization it is a token based but what I did is stored that token in the cookie rather than sending them through the Authorization header for every HTTP request we can do in that way too but I thought that cookie one will be easier to understand for beginners.
Nice post 👍👍...
I tend to use Express: app.use(express.json())
app.use(bodyparser.json()) outputs depreciation warnings here...just my preference though
You're right.
The recommended way is to call the methods directly on the express object.
Because...
In the latest version of Express, some bodyParser's methods have been added natively. That's why you don't have to install or require bodyParser again.
Yes you are right from now i will also use the same thanks for pointing it out 🤍
@icecoffee is this the answer ?!
Here the author is talking about server side authentication.
Please take a look at auth0.com/blog/adding-salt-to-hash... to understand how to properly set a
salt
. A global value for your application is Not Good. The value does not need to be a secret, but it needs to be unique for every record you are hashing (unique per-password/user/record).You typically store the salt with the hash either in a row in the same DB table or even prepended/appended to the hash with a delimeter. All that matters is the salt is unique per input.
From the Auth0 article:
Thank you very much learned something new 🥰.
Yeah!
Can you explain about ":" which used in password:plainTextPassword
I am destructing req.body and storing password as plaintextpassword
you can understand this with this example
Thanks you so much, I understood well.
On the area where you encrypted the password, after destructuring request body, you recreated the password.
Which is supposed to produce an error identifier already exist because const variable cannot be recreated.
Actually on destructuring the request body i am creating email and plaintextpassword and after that i created const password so there is no recreation 😇 thats why it didn't produced error.
Why don’t use header authorization bearer in request ? Is the best practices
Yes we can implement it in that way too which will also make it not vulnerable to CSRF but I thought that cookie one will be more beginner friendly 😬.
Amazing Blog Ritesh.
Thanks harshit
Quite Awesome....!!
Amazing pal...Great Job