In part 5 of this tutorial we’ll add authentication to our app and build a comments system.

1.8 JWT, Login, Registration

In order to build our Login and Registration we need a way of authenticating the user.
To do this we are going to use JWT. JWT (JSON Web Token) is an open standard that defines a way of securely transmitting data between two parties as a JSON object.

With it we can have the server generate an encrypted token for the client, which in further requests the client can use to prove it’s identity.

JWT’s are composed of 3 sections, separated by dots.

The header - The header contains information on the algorithm used to create the token.

The payload - The payload contains the ‘claim’ - this is often a userID or username that the client claims to be and possibly other data also.

The signature - The signature is composed of both of the above encoded, concatenated together and then encrypted using a secret key.

(header.payload.signature).

In our application we will use JWT as follows:

Our client sends a login request.

The server checks the details provided, if they are correct it will create a JWT containing the user’s id and username, using a secretKey. This JWT will then be returned back to the client and placed into localstorage.

Each time an API request is made that requires us to authenticate we will pass the JWT in the authorization header.
On our server we will apply a middleware authentication check to API routes that we wish to authenticate. This middleware will verify and decode the JWT, extract the username and id and then include this username/id within the request for our API to use in the request.

To begin, lets install jsonwebtoken and bcrypt

npm install jsonwebtoken bcrypt --save

In our .env config file we’ll add our secret key:

...JWTSECRET=rogueowlseverywhere...

This will get loaded in with our other env variables (Although I have included the file in the repo it is best not to commit this file)

Next we need to define our user model.

varmongoose=require('mongoose');varbcrypt=require('bcrypt');varSALT_WORK_FACTOR=10;constUserSchema=newmongoose.Schema({username:{type:String,index:{unique:true}},password:String,created:{type:Date,required:true,default:newDate()}});UserSchema.methods.comparePassword=functioncomparePassword(password,callback){bcrypt.compare(password,this.password,callback);};// On save, hash the passwordUserSchema.pre('save',functionsaveHook(next){varuser=this;if(!user.isModified('password')){returnnext();}returnbcrypt.genSalt(SALT_WORK_FACTOR,function(err,salt){if(err){returnnext(err);}returnbcrypt.hash(user.password,salt,function(hashError,hash){if(hashError){returnnext(hashError);}user.password=hash;returnnext();});});});module.exports=mongoose.model('User',UserSchema);

Within our UserSchema we defined a method which will compare the password from a login attempt with our users hashed password.

We also created a hook so that when a new user is created, the password they provided is encrypted using bcrypt before being saved.

Next, In server/controller create a new file called AuthController.js

varbcrypt=require('bcrypt');varjwt=require('jsonwebtoken');varUser=require('../models/User')module.exports={login:function(username,password,callback){User.findOne({username:username},function(err,user){if(err){callback(err,null);return;}if(!user){//User not foundcallback(err,null);}else{user.comparePassword(password,function(err,isMatch){if(err){callback(err,null);return}if(isMatch){varauthToken=jwt.sign({username:user.username,_id:user._id},process.env.JWTSECRET);callback(null,authToken);}else{callback(err,null);}});};});},register:function(username,password,callback){varnewUser=newUser({username,password});newUser.save(function(err,user){if(err){callback(err,null);return;}varauthToken=jwt.sign({username:user.username,_id:user._id},process.env.JWTSECRET);callback(null,authToken);});}}

This contains our login and register methods. Login looks up the passed in username, if it exists it then calls our password compare method. If the password is correct we create a signed JWT using our secret key and then fire off our callback.
Register takes the provided username and password (for the purposes of the tutorial we are not validating ) , creates a user, creates a new JWT token for them and returns it in the callback.

Next we’ll create a new route in /routes/auth.js to handle our login and register and call the auth controller.