Authentication
LEVEL 1
<aside> 💡 LEVEL 1
In this level the data is stored in MongoDB.
</aside>
Step 1) Connect to database.
mongoose.connect("mongodb://localhost:27017/userDB");
Step 2) define schema.
const userSchema = new mongoose.Schema({
email: String,
password: String
});
Step 3) Define model
const User = new mongoose.model("User", userSchema);
Step4) Register a new user
app.post('/register', function(req, res){
const newUser = new User({
email: req.body.username,
password: req.body.password
});
newUser.save(function(err){
if(err){
console.log(err);
} else {
res.render('secrets');
}
});
});
Step 5) Code to verify user in login page.
app.post('/login', function(req, res){
const username = req.body.email;
const password = req.body.password;
User.findOne({email: username}, function(err, foundUser){
if(err){
console.log(err);
} else {
if(foundUser){
if (foundUser.password === password){
res.render('secrets');
}
}
}
});
});
<aside> 📌 SUMMARY: In Level 1 the email and password is not encrypted hence the database can be hacked easily and is not so secure. Hence moving on to LEVEL 2.
</aside>
LEVEL 2
<aside> 💡 LEVEL 2
In this level the data is stored in MongoDB ie password and email are encrypted.
Using NPM package called mongoose-encryption.
</aside>
Refer above link to know more about mongoose-encryption package.
Mongoose Encryption package encrypts data when it is saved into database and decrypts when find method is called data will automatically be decrypted.
Step 1) Install Package and call the function.
install mongoose encryption package.
npm i mongoose-encryption
const encrypt = require('mongoose-encryption');
Step 2) Define a constant secret variable.
Secret String Instead of Two Keys
For convenience, you can also pass in a single secret string instead of two keys.
const secret = "Thisismysecretwhichnooneshouldknow";
Step 3) Encrypt the user data with secret key.
Rest of the code in Level 2 remains same as the mongoose encryption package encrypts when
save() method is called and decrypts when find() method is called.
userSchema.plugin(encrypt, { secret: secret , encryptedFields: ["password"] });
Using Environment variables to keep Secrets safe
When the above code is uploaded to Github it can be easily misused by Hacker to secure the code by using dotenv package from npm.
Step 4) Install dotenv package
npm i dotenv
Import and configure dotenv
require('dotenv').config()
Create a .env file in the root of your project:
touch .env
In .env file some indentations are to be followed
S3_BUCKET="YOURS3BUCKET"
SECRET=Thisisourlittlesecret.
Assigning secret variable from .env file
const secret = process.env.SECRET;
<aside> 💡 Now that secret is written in .env file how to not upload it to GIthub.
</aside>
Simple add .gitignore file and add this folder name in that ……Simple !
<aside> 📌 SUMMARY: In Level 2 the password is encrypted hence the database when hacked cannot be easily decrypted, but still it is not that secure. Hence moving on to LEVEL 3.
</aside>
Recommended by LinkedIn
LEVEL 3
<aside> 💡 LEVEL 3
In this level the Password is encrypted with Hash function.
This can be done using npm package called md5
</aside>
For more information refer above link.
Step 1) Install the package and call the function
npm install md5
const md5 = require('md5');
Step 2) Encrypting password with hash function
app.post('/register', function(req, res){
const newUser = new User({
email: req.body.username,
password: md5(req.body.password)
});
newUser.save(function(err){
if(err){
console.log(err);
} else {
res.render('secrets');
}
});
});
Once the password is encrypted with hash function it cannot be decrypted by any chance i.e it is very difficult to do so.
Step 3) To match the password when user login
In Step 2 password is encoded at register level to verify the user we cannot decrypt it so we just encrypt the user entered password at login page also and match them.
app.post('/login', function(req, res){
const username = req.body.email;
const password = md5(req.body.password);
User.findOne({email: username}, function(err, foundUser){
if(err){
console.log(err);
} else {
if(foundUser){
if (foundUser.password === password){
res.render('secrets');
}
}
}
});
});
<aside> 📌 SUMMARY: In Level 3 Password is encrypted using HASH function which has no key. But there is one problem simple passwords can be easily matched and can be determined. Hence Moving on to LEVEL 4.
</aside>
LEVEL 4
<aside> 💡 LEVEL 4
In this level the Password is encrypted with Bcrypt function.
This can be done using npm package called bcrypt. Bcrypt is slow in generating number of hashes per second which gives a major head blow when hacker tries hacking our passwords.
</aside>
A salt is a random string that makes the hash unpredictable. Bcrypt is a popular and trusted method for salt and hashing passwords.
Step 1) Setting up Node version and Bcrypt=
curl -o- <https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh> | bash
nvm --version
nvm install <latest version of node> <-- Example -->
nvm install 16.17.0
npm i bcrypt
If you get any errors or warnings checkout above link it has Github repo link there in issues you can find a solution for your problem.
npm install --force --global bcrypt@latest
LEVEL 5
<aside> 💡 LEVEL 5
In this level the Password is encrypted with passport and many packages.
In this level cookies are also being created to ensure that logged in user may not need to login again and again.
</aside>
Install necessary packages
npm i passport passport-local passport-local-mongoose express-session
Initialise the packages installed in app.js
const session = require('express-session')
const passport = require('passport');
const passportLocalMongoose = require('passport-local-mongoose');
app.use(session({
secret: "Our little secret.",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
Initialise plugin for user to create cookies
mongoose.connect("mongodb://localhost:27017/userDB");
const userSchema = new mongoose.Schema({
email: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
To authenticate user in register page
Syntax
User.register({username:'username', active: false}, 'password', function(err, user) {
if (err) { ... }
const authenticate = User.authenticate();
authenticate('username', 'password', function(err, result) {
if (err) { ... }
// Value 'result' is set to false. The user could not be authenticated since the user is not active
});
});
Example code
app.post('/register', function(req, res){
User.register({username: req.body.username}, req.body.password, function(err, user){
if(err){
console.log(err);
res.redirect('/register');
} else {
passport.authenticate("local")(req, res, function(){
res.redirect('/secrets');
});
}
});
});
By using cookies we can check whether user has logged in or not
app.get('/secrets', function(req, res){
if(req.isAuthenticated()){
res.render('secrets');
}else{
res.redirect('/login');
}
});
In login route to verify user
app.post('/login', function(req, res){
const user = new User({
username: req.body.username,
password: req.body.password
});
req.login(user, function(err){
if(err){
console.log(err);
} else {
passport.authenticate("local")(req, res, function(){
res.redirect('/secrets');
});
}
});
Logout Route being Initialised
app.get('/logout',function(req,res){
req.logout();
res.redirect('/');
});