In this lesson, we will be allowing users to create accounts, disguising their passwords to prevent hackers from stealing them, saving the accounts in a MongoDB database, and allowing users to log in with their account info.
In your project directory in Terminal, use this command to create a package.json
file for your project
npm init -y
Any project that is using Node.js needs a package.json
file. The file contains the project’s dependencies, source control information, and specific metadata.
Now install all the packages that our project will need using npm.
npm i express body-parser ejs mongoose bcrypt
I will show you how to set up the app.js
file, but you’ll have to design the ejs (or html) and css files on your own.
In general, though, you’ll want to include something like this in your login & signup files:
<form action="/login" method="POST">
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" name="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" class="btn btn-dark">Login</button>
</form>
You will need a form that has an action parameter that posts to a route. You will take the user’s input using named input tags that can be referenced in the server file.
In your app.js
file, require all the packages we installed earlier. Also create a saltRounds variable and set it to 10. We will use it later to salt our passwords.
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10;
More standard setup:
const app = express();
app.use(express.static("public"));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
mongoose.connect("mongodb://localhost:27017/authtestDB", {useNewUrlParser: true});
Create the User Model in MongoDB:
const userSchema = new mongoose.Schema ({
email: String,
password: String
});
const User = new mongoose.model("User", userSchema);
Set up the get routes so users can get to the different pages:
app.get("/", function(req, res){
res.render("home");
});
app.get("/login", function(req, res){
res.render("login");
});
app.get("/register", function(req, res){
res.render("register");
});
Handle users entering their information into the login and register forms:
Posting to the Register Route
As you can see, we are using the bcrypt hash
function. The function takes in the password given by the user in the request, the number of salt rounds (defined above), or times we want our password to be salted , and a callback function that can either return an error or the hashed (encrypted) password.
A new user based on our Mongo User Model is created using the username from the request and the encrypted password. Then, that user is saved into the MongoDB. If successful, the server will navigate the user to pages that require authentication.
app.post("/register", function(req, res){
bcrypt.hash(req.body.password, saltRounds, function(err, hash) {
const newUser = new User({
email: req.body.username,
password: hash
});
newUser.save(function(err){
if (err) {
console.log(err);
} else {
res.render("secrets");
}
});
});
});
Posting to the Login Route
First, using the findOne
function, we see if there are any emails in our database that match the one inputted by the user. This findOne
function can return either an error or a found user/ matching user. If a user is found for the inputted email, bcrypt will proceed to compare the inputted password with the password for the email in the database. If it’s another match (result === true), the server will navigate the user to pages that require authentication.
app.post("/login", function(req, res){
const username = req.body.username;
const password = req.body.password;
User.findOne({email: username}, function(err, foundUser){
if (err) {
console.log(err);
} else {
if (foundUser) {
bcrypt.compare(password, foundUser.password, function(err, result) {
if (result === true) {
res.render("secrets");
}
});
}
}
});
});