do not explicitly create a app.get("secrets")
route, instead res.render("secrets")
after they submit in register page.
create a Database and User collection with Mongoose Schema and model, save the req.body.username
and req.body.password
as a new user document to the database.
create post method for register , after saving their email and password, render secrets page. again create a post method for login, if no error and find username match a record in DB, also if password match record's password, render the secrets page.
install and require mongoose-encryption package
add the encrypt package as a plugin for mongoose.
var secret = process.env.SOME_LONG_UNGUESSABLE_STRING;
userSchema.plugin(encrypt, { secret: secret , encryptedFields:["password"]});
note , put the plugin after Schema and before Model. to optionally encrypt files, add encryptedFields:["password"]
mongoose-encryption will encrypt when save(), and decrypt when find().
use node package dotenv
and put this on top of you app.
require('dotenv').config()
touch a .env
file in root folder of the project.
put your key value pairs in .env
# This is a comment
SECRET_KEY=YOURSECRETKEYGOESHERE # comment
SECRET_HASH="something-with-a-#-hash"
to fetch the value in .env you can call with process.env.KEY
has function is almost impossible to reverse at current computing power
install md5
package and require it
just use md5
as a method to hash your info
const newUser = new User({
email: req.body.username,
password: md5(req.body.password)
});
the password stored in db will be like d8578edf8458ce06fbc5bb76a58c5ca4
the trick for login is also md5
the password user imputed.
app.post("/login", (req,res)=>{
User.findOne({email:req.body.username}, (err,target)=>{
if (err){console.log(err)} else {
if (target){
if(target.password===md5(req.body.password)){
res.render("secrets")
}
}
}
})
})
salting means append a randomly generated set of string to your password before the hash function.
install and require bcrypt
and set value for saltRounds.
const bcrypt = require("bcrypt");
const saltRounds=10;
the hash value is accessible in the callback function
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});
in our case, our plainPassword is req.body.password
, and we use the hash in callback function to save new user document.
app.post("/register", (req, res) => {
bcrypt.hash(req.body.password, saltRounds, function(err, hash) {
// Store hash in your password DB.
const newUser = new User({
email: req.body.username,
password: hash
});
newUser.save((err) => {
err ? console.log(err) : res.render("secrets");
})
})
});
for login route we can check the hash
// Load hash from your password DB.
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
in our app it looks like
app.post("/login", (req,res)=>{
User.findOne({email:req.body.username}, (err,target)=>{
if (err){console.log(err)} else {
if (target){ //we found such a username in our database
// Load hash from your password DB.
bcrypt.compare(req.body.password, target.password, function(err, result) { //now we compare password clients entered, and the password we found in database
if (result){res.render("secrets")} // if results is true we render secrets page.
});
}
}
})
})
when client makes a specific post request to server, the server will send cookies to client. in future, when client send get request to server again, the cookies will go to server also.
session is period of time you don't need to re-enter your password to browse the website.
after login session starts and cookies created, after logout, session over and cookies destoried,
- passport
- passport-local
- passport-local-mongoose
- express-session
install and require them
const session = require('express-session');
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
//don't need to require passport-local, it is a dependency
use the session package before mongoose connect
app.use(session({
secret:"this is just my temporary secret",
resave:false,
saveUninitialized:false
}));
and then append follwing for passport packages
app.use(passport.initialize());
app.use(passport.session());
add the plugin between mongooseChema and Model
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
the next step is about serialize , put this after the mongoose model we created
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
when we close the tab and browsing other site, if we didn't close our browser, when we enter the same route, we are kept as logged in, we don't need to enter password again.
Created
Wednesday, June 22, 2022 at 5:13:51 PM
Expires
When the browsing session ends
to achieve this , we need to create post method for register, and get method for secrets.
//create post for register
app.post("/register", (req, res) => {
User.register({username:req.body.username}, req.body.password, (err,user)=>{//the register method take 3 params, username, passowrd, and an callback function.
if (err) {
console.log(err);
res.redirect("/register")
} else {
passport.authenticate("local")(req,res, ()=>{
res.redirect("/secrets")
})
}
})
});
// create the get route for secrets
app.get("/secrets", (req,res)=>{
req.isAuthenticated()?res.render("secrets"): res.redirect("/login")
})
//create post for login
app.post("/login", (req,res)=>{
const user = new User ({
username:req.body.username,
password:req.body.password
})
req.login(user, (err)=>{
err? console.log(err):passport.authenticate("local")(req,res, ()=>{
res.render("secrets")
})
})
});
every login attempt will create a temporary user object for passport to check against db, if login passed, render the page.
//create a app.get() for logout
app.get('/logout', (req, res)=>{
req.logout(function(err){
if (err){
console.log(err);
} else {res.redirect('/')}
});
});
note , the logout method requires a callback function now. just add a callback to log err if needed.
Please keep reading continueREAD.md
for Google authentication and submit secrets.