To read this post, you have to be familiar with basic Express, Mongoose and solid Javascript background.
Libraries I’ve used in this example:
- Express: web framework
- Mongoose: modeling User object in mongodb
- Cookie-parser: to work with cookie
- Jsonwebtoken: to create JWT
- Bcrypt: to encrypt passwork
- Hbs: Express.js view engine for handlebars.js
Project structure:
- index.js – main file
- user-model.js
- user-routes.js
- templates
- index.hbs
- login.hbs
- assets
- script.js
Let’s get started!
Step 1: Create user-model.js file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const mongoose = require( 'mongoose' ); const bcrypt = require( 'bcrypt' ); // Create userSchema with 2 properties: email and password const userSchema = new mongoose.Schema( { email: { type: String, minlength: 6, required: true, unique: true, trim: true }, password: { type: String, required: true, trim: true } } ); // Hash password when add/update user userSchema.pre( 'save', async function( next ) { var user = this; if ( user.isModified( 'password' ) ) { user.password = await bcrypt.hash( user.password, 8 ); } next(); } ); const User = mongoose.model( 'User', userSchema ); module.exports = User; |
to create test user, just use this code on anywhere of your project, after created users, please commented out these lines:
1 2 3 4 5 6 |
var user = new User( { email: 'any.email', password: 'any.password' } ); user.save(); |
Step 2: Create user-routes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
const express = require( 'express' ); const bcrypt = require( 'bcrypt' ); const User = require( './user-model.js' ); const route = express.Router(); const jwt = require( 'jsonwebtoken' ); // Login page route.get( '/login', async ( req, res ) => { res.render( 'login' ); } ); // Checking user login's information route.post( '/login', async ( req, res ) => { // Find user by email var user = await User.findOne( { email: req.body.email } ); // Check if user is not existed if ( ! user ) { return res.send( { errorMessage: 'Email is not existed' } ); } // Check if password is matched var isMatch = await bcrypt.compare( req.body.password, user.password ); if ( ! isMatch ) { return res.send( { errorMessage: 'Password is invalid' } ); } // Create JWT var token = await jwt.sign( { _id: user._id.toString(), email: user.email }, 'my-token-key' ); // Save JWT to cookie res.cookie( 'my-token', token, { maxAge: 10000, httpOnly: true } ); // Send destinated url to client res.send( { url: 'http://localhost:3000' } ); } ); module.exports = route; |
Notice: I don’t check validation of inputs, we can do it later because this is just a basic example to understanding how to authenticate user with JWT in NodeJS app.
Step 3: Create login.hbs (login template)
1 2 3 4 5 6 7 8 9 10 11 |
<div style="width: 600px; margin: 0px auto;"> <h2>Login</h2> <form method="post"> <p><input type="text" name="email" placeholder="Email"></p> <p><input type="password" name="password" placeholder="Password"></p> <p><input type="submit" value="Login"></p> </form> </div> <script type="text/javascript" src="script.js"></script> |
Step 4: Create script.js to send login data to server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
document.addEventListener( 'submit', ( e ) => { e.preventDefault(); fetch( '/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( { email: document.querySelector( 'input[name="email"]' ).value, password: document.querySelector( 'input[name="password"]' ).value } ) } ).then( async res => { var data = await res.json(); if ( data.hasOwnProperty( 'url' ) ) { window.location = data.url; } } ); } ); |
Notice: you must use async await
to get response data correctly.
Step 5: Create index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
const express = require( 'express' ); const userRoutes = require( './user-routes.js' ); const mongoose = require( 'mongoose' ); const path = require( 'path' ); const cookie = require( 'cookie-parser' ); const jwt = require( 'jsonwebtoken' ); const app = express(); // Connect mongodb mongoose.connect( 'mongodb://127.0.0.1:27017/authentication-project', { useNewUrlParser: true, useCreateIndex: true }, ( error, client ) => {} ); // Set view engine is hbs app.set( 'view engine', 'hbs' ); // By default, hbs templates are located in Views folder, we use Templates folder instead, that's why we need this line app.set( 'views', path.join( __dirname, 'templates' ) ); // Set location for statis resources app.use( express.static( path.join( __dirname, 'assets' ) ) ); // Able to read req.body in JSON format app.use( express.json() ); // Able to use cookie app.use( cookie() ); // Middleware Authentication app.use( ( req, res, next ) => { // Check if it's not login page and my-token cookie is not existed > redirect to login page if ( '/login' !== req.path && ! req.cookies.hasOwnProperty( 'my-token' ) ) { return res.redirect( 'http://localhost:3000/login' ); } // Check if it's login page and my-token is existed > redirect to home page else if ( '/login' === req.path && req.cookies.hasOwnProperty( 'my-token' ) ) { return res.redirect( 'http://localhost:3000/' ); } next(); } ); // Homepage app.get( '/', ( req, res ) => { var decoded = jwt.verify( req.cookies['my-token'], 'my-token-key' ); res.render( 'index', { name: decoded.email } ); } ); app.use( userRoutes ); app.listen( 3000, () => { console.log( 'listening on port 3000' ); } ); |
Step 6: Create index.hbs
1 |
Hello {{name}} |
All done! to get project’s source code, please visit my github
Happy coding!