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.
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
In our .env config file we’ll add our secret key:
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.
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
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.
Now we will alter /server/app.js to make all requests to ‘/user’ go to our new auth router
We now have the API for login and registering, each function returning the token and username back to the browser on success.
Now lets do some alterations to the client to organize things a bit better
First, rename actions/actions.js to newsActions.js.
In News.js , NewsArticle.js, newsItemDetail.js, and NewsSubmit.js change the references to use the renamed newsActions.js.
Next create some new constants in constants/actionTypes.js
We’ll now create a new file actions/authActions.js and add our new authentication functions
On both login and register, if the response is ok, we take the token and username from the response and place it into localstorage.
We then dispatch a userLoggedIn action
Next, In clients/src/presentation create the following two files:
Now we’ll create a container to hold our Login and Registration components
Now lets make a new reducer for our authentication. in src/reducers create authReducer.js
Although our reducer function itself is should be a pure function, we can set our default state using the values from localstorage so that on page refresh our logged in state is maintained.
Now we’ll add the new reducer into our store in /stores/store.js
To complete our authentication we need to add our middleware for checking for the presence of the Authorization token on secure routes.
Our middleware verifys the token using our secret and then sets variables in req.userData for our secure requests to use.
At this stage we now have our server side authentication API for login and register. On the client we can login and register, with it storing the JWT token and username in localstorage.
We’ll now extend the API to include the ability for logged in users to post comments..
Lets start with a new Schema for our comment
Then we’ll add this new comment schema to our News schema as a subdocument.
We then need to add some changes to our NewsController to create a new comment and add it to the news items comments.
We now need to import our authCheckMiddle and apply it to our route /news/:id/comment
App.js integration of auth check
Next we’ll create a new route within the news router for calling our newsController’s createComment method, passing in the req.userData.username that the authCheckMiddleware provided
Lets again switch back to the client and create some components for our comments functionality.
First our CommentsPanel container in containers/CommentsPanel.js
Next in /presentation lets create two Components for our Comment element and also our create comment form
In our NewsArticle container we’ll add in our CommentsPanel
By adding the comments to our mapStateToProps function, the news story should re-render when a new comment is added
For our add comment action we need to first add the constant NEWS_ADDCOMMENT
Next, In the function which makes the request for adding the comment we will get the JWT from localstorage and pass it in the Authorization header.
With the response returned from the API, we dispatch an addComment action which will add the new comment to our page without requiring us to re-request the news story data.
To do this we need to make changes to news Reducer to append the comments details to the news stories comments array.
With that done we now have a functioning comments system.
In the next part we’ll do some tidying up and validation… To be continued..