Building my own mean framework : a-mean - Part 2
TL;DR
In this series of articles we will be building a very basic mean framework with login and registration functionality.
This is the part 2 of that series. If you have missed the part 1. You can check out here. In this post we will be creating login - registration system and authenticate with passport.
Part 2 : Creating authentication in the back-end
1. Install these packages via npm
npm install bcrypt@2.0.1 jsonwebtoken mongoose passport passport-jwt mongoose --save
2. configuring mongodb
i. create a config
folder inside server
& create a main.js
file
mkdir server/config && touch server/config/main.js
ii. put the below code in the main.js
file
module.exports = {
"secret":"f122f050696aa0fa2c26c7bb2c7b2a646dc8ee65552fe3f9e49a7a5da21145441b243690e03a2d65",
"database":"mongodb://localhost:27017/my-database"
};
Note : We will be needing the secret key for securing passport (replace the secret key with your own). Replace localhost:27017
with your mongodb host and port, I am using mongodb in localhost on default port. replace my-database
with your database name.
3. Update app.js
with the below code
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var morgan = require('morgan');
var config = require('./server/config/main');
var passport = require('passport');
var jwt = require('jsonwebtoken');
var mongoose = require('mongoose');
require('./server/config/passport')(passport);
// Create API group routes
var apiRoutes = express.Router();
// Use body-parser to get POST requests for API use
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(morgan('dev'));
// Connect to database
mongoose.connect(config.database, { useNewUrlParser: true }, function(err) {
if (err) throw err;
});
app.use(passport.initialize());
//routes
require('./server/routes/routes.js')(apiRoutes);
// Set url for API group routes
app.use('/api', apiRoutes);
module.exports = app;
4. Create user
Schema
i. Create a folder models
inside server
and create user.js
file inside models
mkdir server/models && touch server/models/user.js
ii. put the below code in user.js
file
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
created_at: {
type: Date,
default: Date.now
},
});
UserSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
// Create method to compare password input to password saved in database
UserSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
5. Configuring passport
i. Create passport.js
inside the config
folder inside server
folder
touch server/config/passport.js
ii. put the following in the passport.js
file
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
};
6. Response beautifier
i. Create a folder helpers
inside server
and create beautifier.js
file inside helpers
mkdir server/helpers && touch server/helpers/beautifier.js
ii. put the following in beautifier.js
'use strict';
exports.beautify = (x,m) => {
const y = {};
y.message = m;
y.data = x;
return y;
}
7. Create userController
i. create a controllers
folder inside the server
and create userController.js
inside the controllers
folder
mkdir server/controllerstouch server/controllers/userController.js
ii. put the following content in the userController.js
'use strict';
var User = require('../models/user');
var jwt = require('jsonwebtoken');
var config = require('../config/main');
var jb = require('../helpers/beautifier');
exports.login = function(req, res) {
User.findOne({
email: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(500).send('User not found');
} else {
// Check if password matches
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: '7 days'
});
res.json({'access_token' : token});
} else {
res.status(500).send('Passwords did not match');
}
});
}
});
};
exports.register = function(req, res) {
if(!req.body.email || !req.body.password) {
res.status(500).send('Please enter email and password');
}
else {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
// Attempt to save the user
newUser.save(function(err) {
if (err) {
res.status(500).send('That email address already exists');
} else {
res.json(jb.beautify('','Registration Successful'));
}
});
}
};
exports.dashboard = function(req, res) {
User.findOne({
_id: req.user._id
}, function(err, user) {
if (err) throw err;
res.json(jb.beautify({ user: user },'User retrieved successfully'));
});
};
exports.unauthorized = function(req, res) {
res.json('Unauthorised');
};
8. Update routes.js
with the following code
'use strict';
var passport = require('passport');
var userController = require('../controllers/userController');
module.exports = function(apiRoutes) {
/* GET home page. */
apiRoutes.get('/', function(req, res, next) {
res.send('Express RESTful API with nodemon');
});
// Register new users
apiRoutes.post('/register', userController.register);
// Authenticate the user and get a JSON Web Token to include in the header of future requests.
apiRoutes.post('/login', userController.login);
apiRoutes.get('/unauthorized', userController.unauthorized);
// Protect dashboard route with JWT
apiRoutes.get('/dashboard', passport.authenticate('jwt', { session: false }), userController.dashboard);
};
9. To serve the project run npm run dev
**If you use postman as your api client for testing, here is the collection link : https://www.getpostman.com/collections/797dd4fa6cb53182b2b1
This brings us to the end of the Part 2 of the mean framework series.
Links to the rest of the parts will be added later along with a Github repository with the full framework. If you have missed the previous parts, you can check them out here: Part 1
Feel free to Comment, Share, Clap this post. Thanks for reading!