Now that we have understood the token based authentication using the JSON Web Tokens,
and also understood the advantages of using this approach.
The fact that we are now building a Rest API based server,
in this course means
the JSON Web Token based authentication
is most suited for this server that we are building.
So, let's update our Rest API server to make use of JSON Web Tokens in this exercise.
To begin updating the server at your terminal,
let's first install the JSON Web Token and the passport JWT Node module.
So, at the prompt type npm, install,
passport JWT JSON Web Token and minus minus save.
As you can see, I'm using JSON Web Token 8.3.0 and passport JWT 4.0.0 in this course.
Now that we have completed the installation let's go ahead and update our express server.
Going now to our code,
let's add in a file named
conflict.js in the root folder of our project.
Now, this conflict.js file I'm going to use it
to store some configuration information for our server.
Now, this is a way of centralizing all the configuration for our server.
In this conflict.js file,
let me export a JSON object here.
So we'll say, secretKey,
and this is where
I will specify the secret key that I'm going to be using for signing my JSON Web Token,
and also let me specify a Mongo URL here,
which will be the URL for
my MongoDB server localhost 27017.
Once we have completed that,
then we will go to authenticate.js file,
and in authenticate.js file we will
now create the JWT strategy.
This is the JSON web token strategy which is provided by
our passport JWT node module that we have just included and so we'll say,
generativity strategy passport generativity.strategy.
This will provide us with
a JSON Web Token based strategy for configuring our passport module,
then extract JWT, which I
will also get from passport JWT.
We'll require passport JWT,
then we'll say "Extract JWT."
Then let's import
the JSON Web Token module
that we just installed.
Once we have imported these,
let's go ahead and start configuring a few things.
Along with these, let me import the config that I have
created the config.js file that I've just added to my project.
Once I complete this then let me go ahead and
introduce a few additional functions which I will export from here.
We'll say, exports.getToken,
this function when supply with a parameter there which I will simply call user,
which will be a JSON object,
this will create the token and give it for us.
To create the token, we will be using the jsonwebtoken module that we just imported.
So, here we'll say return JWT.sign,
this helps us to create the JSON Web Token and so inside that it'll
allow me to supply the payload and
the payload here comes in as the parameter here called user,
and then the second parameter is
the secret or private key which I get from config.secret key,
which I have just configured a little bit earlier.
We can supply additional options here.
One option that I'm going to supply to this is-.
Okay, let me go to the next line here,
the option that I will supply is expiresIn.
The expiresIn will tell you how long the jsonwebtoken will be
valid so in this case I say 3,600 meaning 3,600 seconds or about an hour.
An hour later you will have to renew the jsonwebtoken.
An hour is long enough for us to be able to test our application.
You can set this to be much longer if you choose to.
In a real application you might set this to be a much longer value maybe
a few days and expect the user to re-authenticate themselves every few days.
Now, we will also next configure
the jsonwebtoken based strategy for our passport application.
So, let me declare a variable called opts,
which is nothing but the options that I'm going to specify for my JWT based strategy.
So we'll say opts JWTfromrequest.
Now this option specifies
how the jsonwebtoken should be extracted from the incoming request message.
This is where we will use the extract JWT.
This extract JWT supports various methods
for extracting information from the header.
It'll say from authHeader from authHeader as bearer token,
from header which scheme and so on.
If you read the documentation it will tell
you various ways of extracting the jsonwebtoken.
You can pass the token in the body of
the incoming request message and then you can extract it from there,
you can also use custom extractors and so on.
In this course, I'm going to use the simplest method called
from authentication header as a bearer token.
We are already familiar with
the Authentication Header because as we have been using that with
the basic authentication and the cookie-based authentication earlier.
So, I'm just going to use
that same header field in the request message to carry the jsonwebtoken.
So I'll say opts as bearer token jsonwebtoken.
The next one we'll say opts.secretOrKey,
this is the second parameter which helps me to
supply the secret key which I'm going to be using within my strategy for the sign-in.
So that is the other option that I'm going to specify here.
Once I'm specifying these two,
let me export the Passport strategy
which I'm going to configure here so we'll say exportJWTpassport,
then we'll say passport.use.
Recall the way you specified the local strategy earlier.
Here we are specifying the JWT based strategy,
then we will create a new JWT strategy,
recall that we just imported the JWT strategy
here that is what we will use to create a new strategy.
This JWT strategy takes
the options object that I just created as the first parameter.
The strategy options and the second one is the verify function that I need to supply,
and so the verify function I'm going to supply it in the next line here,
we'll say functionJWT_payload.
Done. So, when this function is called,
the done is the callback that is provided by passport.
So, whenever you have passport which you're configuring with a new strategy,
you need to supply the second parameter done.
Through this done parameter,
you will be passing back information to passport which it will then
use for loading things onto the request message.
So, when passport parses the request message,
it will use the strategy and then extract information,
and then load it onto our request message.
So, since this happens to be a function,
I'm just going to use a arrow function here,
I've gotten fond of arrow functions.
So, let me create that as an arrow function here,
and inside of this function,
we will be defining the function.
So, what do we do inside the function?
Let me do a console.log of
JWT payload and then
let me just log out the option coming in here,
the JWT payload Option coming here,
so that you can see what is inside the JWT payload.
Then, we will search for a user by saying User.findOne,
and then I know that in the jwt.payload,
there is a ID field that comes in.
So, that is what I am going to be assigning as the ID field here.
So, I will say, User.findOne and the second one
is a callback function.
As you realize, this user Mongoose method and you try to find.
So, we'll say if err then, return done.
What does this done? This done is the callback that
passport will pass into your strategy here.
So, we are going to be calling this done function.
This done in passport takes three parameters.
So, you can see the three pieces of information this done expects it says, error: any.
So, if you have an error you will pass it in as the first parameter.
The second parameter, user?,
If a user exists,
then the user value will be passed in and then if any info?:, any.
So, these two are optional parameters and so,
if you pass in any information,
then that will be used within the application.
If I pass in false as the second parameter,
then that means that the user doesn't exist or that.
So, it'll interpret that the user doesn't exist.
So, I could say, err,
false, because this is an error.
So, I'm not going to be passing in a user value there,
I'm just going to pass in false.
There, the next,
we can say, else if (user).
So, if the user is not null,
we'll say return done(null).
There is no error so, the first parameter will be
null and the second parameter is the user,
but we just got from the MongoDB.
Otherwise, we will return
done with null, false.
So, in the last case,
we could not find the user,
so we're going to be passing in false.
So, we're going to be handling it like this.
If you want, you can create a new user account at this point
but I'm going to keep this simple just so that it's easy for us to understand.
So, we'll just say, null, false. That's six.
So, this is the JsonWebToken passport strategy that I've just configured here.
Also, let me export
one more function from here called verifyUser.
Now, this verifyUser function,
I'm going to be using it to verify an incoming user.
So, this is where I will use passport.authenticate.
So, the passport.authenticate, the strategy is jwt strategy which I've just configured,
the JsonWebToken strategy that I've just configured.
Then, the second part,
I would say, session: false.
So, that means that,
we are not going to be creating sessions in this case.
As you recall, a inverse application,
we are using token-based authentication.
So we're not going to be creating sessions.
So, that's why I set this option session to false here,
and of course, the first one specified the strategy that I'm going to be using.
So, for verifying a user,
I will use the JWT strategy.
How does the JWT strategy work?
In the incoming request,
the token will be included in the authentication header as we saw here.
We said authentication header as bearer token.
If that is included, then that'll be extracted and that will be
used to authenticate the user based upon the token.
Minor correction here, this should be User.findOne_id is equal to jwt_payload._id,
because that is the id value that is inside the payload of my JsonWebToken.
So, we are searching for the user with that given ID.
So, once we have completed this, then now,
the second part that we need to do is that we need to create the token somewhere.
Now, where do we create the token?
So, this is where something that we are doing in the users.js file is very useful for us.
In the users.js file,
recall that you already have this endpoint called login.
In the login endpoint,
you were using the username and password to authenticate the user.
So, even with the JsonWebToken to issue the JsonWebToken,
you first need to authenticate the user using one of the other strategies,
and if you're going to be using the local strategy first,
we will authenticate the user using the username and password.
Once the user is authenticated with the username and password,
then we will issue the token to the user saying,
"Okay, you are a valid user,
I'm going to give you the token".
All of the subsequent requests will simply carry the token
in the header of the incoming request message.
So, earlier, we used to create sessions.
When the user is authenticated,
we're not going to be using sessions anymore.
Instead, when the user is authenticated using the local strategy,
we will issue a token to the user.
So, inside this router.POST method that we have done on that /login endpoint,
I'm going to create a token and pass this token back to the user.
So, in here, we will say a router.POST.
Let me create a token.
To create a token,
we have this function in the authenticate module called authenticate.getToken.
So, recall, that we have already,
to make use of that of course,
even before we start there,
I need to import the authenticate.
Module here. So, we'll say authenticate require./authenticate.
So, then, when in your code here,
we can now say authenticate.getToken,
and the getToken takes parameter here.
Now, recall, going back authenticate.js file.
The authenticate.js file takes one parameter here
which will be used as the payload when you're creating the JsonWebToken.
So, in the users.js file,
I'm going to create a token by giving a payload,
which only contains the ID of the user.
So, we'll say id: req.user._id.
That is sufficient enough for creating the JsonWebToken.
We don't want to include any other of the user's information.
If you choose to, you can include other parts of the user information,
but I would suggest that keep the JsonWebToken small.
The user ID is sufficient enough because if you need to search for the user,
the user ID is enough to search in the MongoDB for the user.
So, I'm just going to encode only the ID of the user here.
Now, you know that the req.user would be already present,
because when the passport.authenticate('local') successfully authenticates the user,
this is going to load up the user property onto the request message.
So, that's why I'm able to do this here.
So, this is what I will use to create the token.
Now, once the token is created,
I want to pass this token back to the user.
So, in the rest.json object that I am supplying here,
I am already carrying the success true flag and also,
a status message here.
Let me add in the token
as one of the properties in the reply message here.
So, the token that I've just created,
I will pass this back as the second property inside of this Json string here.
So now, when my client receives this Json string in the body of the reply message,
it can go in and extract the token from there.
That's it. So, we have now updated the users.js file,
and you can now see how the token will be
created and sent back to the user when the user is successfully authenticated.
Now, this scheme can also be used
when you use third-party authentication like based on OAuth 2.0,
which we will examine in the next module.
Now, the procedure will be similar.
You will be creating a token when the user is authenticated by
the third-party or OAuth authentication provider,
and then you will pass the token back to the user,
in a similar approach as you see here.
Now, once we have done this,
then we go to app.js file.
In app.js file, because we have included a config file here.
So, let me require the config file here,
and then the URL that I'm using here instead of hardcoding this URL,
I will say config.mongoUrl.
So, now, you see how my config.js file can be used as
a centralized place where I can prepare the configuration for my application.
That's it. So, what happens now is that when the user
authenticates on the /login endpoint and the user is successfully authenticated,
then the token will be created by the server and sent back to the client or the user.
So, the client will include the token in
every subsequent incoming request in the authorization header.
Now, how does it include the authorization header?
Let's go back to authentic.js and here,
you see that we said ExtractJWT.fromAuthHeaderAsBearerToken here.
So, this will be included in the authentication header as a bearer token.
I'll show you how this is done,
then we use postman to include the bearer token into the authentication header.
Now, when this comes in,
then you recall that right down below here,
you have configured this method here called verifyUser,
which calls upon the passport authenticate with JWT.
So, this one uses the token that
comes in the authentication header and then verifies the user.
So, anytime I want to verify the user's authenticity,
I can simply call verify user,
and that will initiate the call to the passport.authenticate and verify the sser.
If this is successful,
it will allow me to proceed.
This procedure is very similar to what you have done in the
users.js file where you call upon the same passport.authenticate('local').
So, if this is successful,
then you go forward.
If it fails, the authentication function will return the error message
back to the client saying that the user is not authorized.
So, that is already taken care of.
So now, that we have included this into my authenticate.js file,
any place that I want to verify the user,
I can simply call upon
this verifyUser function that
I have specified here or the export that I've specified here,
which we'll call upon the passport.authenticate using
the JWT strategy to authenticate the user.
Now, how do we make use of this?
Now, what we're going to do is we're going to go into each and every one of our routers,
and control the options on all the routes that we want to control.
So, going back to app.js file, now,
that we are not using sessions I'm going to remove this session from here,
because we are no longer using sessions.
Similarly, I'm going to remove this passport.session from here also.
That is also not need.
Also, this authentication, see earlier,
when I configured this authentication,
this authentication was applied to every single incoming request.
Now, I'm going to change my application,
whereby I will require authentication only on certain routes and not on all the routes.
So, let me remove this authentication completely from app.js.
So, now, when the request comes in,
if it is on / endpoint,
the index will be served.
If it is on /users endpoint it'll allow you to navigate to
the various routes that are mounted onto the /users in the users.js,
and then subsequently, the rest of the ones.
What I'm going to do now is I'm going to leave
the public folder open for anybody to access.
Now, in many applications,
this may be just fine.
So, I'm going to leave that open.
Now, on the dishes,
promotions, and the leaders' endpoint,
All the get requests.
I will let those be replied to without requiring any authentication.
Now why would I want to do that?
If a user is doing a get request,
the user just wants to retrieve information.
So, for example, on the client side if I'm implementing a web application using Angular
or a client application using Ionic or native script,
then maybe I want to implement my app in
such a way that the main page will display information already,
the genetic information that you want to make available to anybody that
visits your website or that opens your app.
So, basic information can be displayed there.
But if you want to change anything,
then you expect the user to be authenticated.
So, you will allow POST operations, put operations,
and delete operations to be done only by authenticated users.
Similarly, for comments for example,
you can say that comments can be only posted or modified by authenticated users.
So, you can restrict only some routes for authenticated users,
the other route you can leave them open. How do we do that?
Now this is where the verify user that we
have exported from authenticate.js file comes in handy.
Now instead of controlling all the end points,
all the various operations on the dishes, promotions,
and leaders in points,
we will open only the get operations for anybody,
but the post, put,
and delete operations will be restricted only to authenticated users.
In the assignment, you will add in one more category of users called admin users.
Now you would restrict certain operations to be performed only by admin users.
So, for example, modifying the dishes
or deleting the dishes information from the database,
will be restricted only to admin users.
But basic users can post comments,
modify the comments that they have posted,
and perhaps even save some favorite dishes.
We'll do that part in the fourth module.
Now so how do we control specific routes?
So this is where we have to go into each of the routers
and then import controls on specific routes.
So, let's start with the dishes route.
So, for the dishes route,
you'll recall that this is controlled in dishRouter.js file.
So, going to dishRouter.js,
let's first import the authenticate here.
So we'll say, const authenticate
require../ authenticate because this
authenticator.js file is in the higher level folder.
So, remember../ authenticate here.
So, once you import the authenticate,
for the dish router route for this route,
the get operation, I'm going to allow without any problem.
So, that is open.
So I'm not going to impose any restriction.
From the post, if we want to apply multiple middleware,
we can simply add the main inside this one behind the other.
Now when the post is called,
right now you are simply executing this function here.
Now just before that,
I can go in and say, authenticate.verifyUser, there.
So, what does this do?
This says that if a post request comes in,
I would first execute this middleware,
which I have exported from the authentic.js file,
I first apply that,
which is equivalent to saying passport authenticate JWT and you are checking the user.
Then if this is successful,
then I will move on to do the rest of it.
If the authentication fails at this point,
then passport authenticate will reply
back to the client with the appropriate error message.
So that is already handled by passport authenticate,
so I don't have to worry anything beyond that point.
If I have crossed this middleware and then get to
execute the next function that means that my authentication was successful,
so I'm able to proceed forward from this point.
So, this is acting as the barrier for this post method.
Now using the same argument,
I can simply apply this particular middleware to all the other methods.
I can do that to the put.
Although in this case,
put is not going to be doing anything.
But in any case,
I will simply just for the sake of uniformity,
I will apply the verify user to also put and of course,
for delete too, I'm going to apply put.
Similarly, going down to the /dishId,
get I will allow that to work without any problem.
Post, I will apply the verify user.
Put also I will apply the verify user and for delete also same.
For the /dishId/comments, I'm going to leave the get open,
it's okay for anybody to retrieve comments about a specific dish.
Post, I'm going to close this off,
so only verified users can post comments.
Put also I will close this off and delete too.
You need to go one step further and say,
"Only the users that have posted the comment can delete their own posts."
But we will do that in the next module.
For the moment, I'm going to say,
"Okay, a verified user can delete any comment."
This is of course not true,
we can put an additional checks,
but we will do that in the next module of this course.
So, for delete also I apply the same.
Again, for the dish router comments/ comments Id,
get I'll leave it as open.
Post, I will leave it closed.
Put also close it off and then for delete also I will close this off.
Delete of course as I said,
the delete operation should be allowed only a user that
has created the comment should be asked to delete that,
but you need to set up a few additional things for that to work correctly,
which we will do in the next module.
For the moment, we're saying a verified user can delete a specific comment, that's it.
Now we will apply the same principle to the promo router and also the leader router.
So, going into the promo router,
let me import authenticate
and then get is open,
so this is leader router.
So, get is open.
Post, I'm going to control that,
put, control, delete, control,
leader router, leader ID,
get is okay, post,
controlled, put is controlled and delete is controlled.
Same thing with promo router.
Let me, require the authenticate
and then for the route get is open,
POST is controlled, put is controlled,
delete is controlled, same thing for the promoRouter/promoID.
Get is open, POST is controlled,
put and delete are also controlled, that's it.
Let's save all the changes.
So, once we complete all the changes,
let's save the changes.
Now, our application is ready to be tested.
So, let's go and restart our server,
or if the server is not running,
we'll just start the server.
Going to the terminal,
my server is not running.
So, let me start the server by typing npm start.
Interestingly, it just thrown an error here,
I just wanted to illustrate to you that even I can make mistakes,
so you'll see that there is an error here which says,
"Cannot find module.authenticate," and then if I look through the code,
I find that this problem occurred in the,
where did it occur?
So, I just search here,
and then somewhere down here,
I noticed that this problem occurred inside my users.js file.
So, right there, it says,
this is in the users.js file.
So, going to the users.js file, let's fix that.
Going to users.js file,
right at the top when I required authenticate, I said,
''.authenticate,'' and as I was telling you,
that should be a dot dot because it's in the upper folder.
This users.js file in the Routes folder,
and authenticate is in the Projects Route folder so this should be dot dot authenticate.
So, if you make mistakes, there you go,
that's how you're going to be reminded of the error that you've introduced.
So, let's save the changes and then restart our server.
Again, going back to that terminal,
let me start my server and my server is now up and running,
let's go to Postman and then test our application.
Now, in Postman, let me first try to do
a GET on localhost:3000/dishes and then when I do a GET,
it is successful because the GET end point is not controlled.
So I can do a GET on dishes,
I can do a GET on promotions,
and it all works just fine.
But if I try to do a POST on dishes,
so let me find where I have done a POST on dishes.
Here I have done a POST on dishes.
So, when I tried to do a POST on dishes,
it immediately says unauthorized.
So my Passport module has realized that I'm not authorized so I'm not allowed to do this,
so that's what it is reminding me about.
Let me clean out the cookies from,
in case you find a cookie in your Postman just remove the cookie
corresponding to the localhost because
that cookie is no longer needed and will not be required.
So, even if I remove the cookie and then POST,
it will still say unauthorized.
So, I'm not authorized for doing these operations.
So, I have to login.
Now, if you have done the previous exercise,
you would have left the user that you signed up earlier.
So, for example, you'd have signed up this particular user in the previous exercise,
let me try to sign up the same user again and
my server should complain saying UserExistsError so that means that the user exists,
so I don't need to sign up again.
If you have deleted the user from your MongoDB,
just sign up that user one more time and then let's login.
So, we will send a login request to that end point.
So, when I send the login request by doing a POST to the end-user,
notice what is returned by my server.
You immediately notice that in the reply message
along with the success flag and the status message,
you also get a token here.
Now, this particular token,
I need to copy this token,
because in my subsequent requests,
I will need to include this token.
So, let me go to the raw version of this and that I'm going to copy this token.
This token is nothing but a long string there,
so let me copy this token.
Then now if you are curious about what is contained in this token,
the JSON Web Token can be examined.
There is a particular site where you can go in and type
in your JSON Web Token and then actually check what is inside the JSON Web Token.
Let's do that as the next step.
In a browser window,
just type jwt.io and this will take you to this site called JSON Web Token jwt.io.
If you recall, in the lecture,
I had shown you the structure of the JSON Web Token
and had shown you that the JSON Web Token contains three parts: the header,
the payload, and the information there.
Now, in this case,
the information is up here so I am going
to paste my JSON Web Token into this left-hand side,
so let me select that and then paste my JSON Web Token into
the left-hand side in the encoded part and then on the right-hand side,
it is showing you exactly what is inside the JSON Web Token that I just created.
So, it says the header contains these two pieces of information.
The payload notice that it contains the ID of
the user and then the signature at the bottom here.
So, this is what is contained inside my JSON Web Token.
Going back to Postman,
let me now try to GET the dishes.
So, when I say GET localhost:3000/dishes,
it will still return an empty string,
empty array here because my database doesn't contain it.
So, let me POST a dish to my database.
So, I'm going to choose the POST operation and in the body,
you see that I have my dish here.
In the header, I'm going to add in the new header called
authorization and you recall that earlier for basic authorization,
you said basic and then you had the Base 64 encoded string there.
If you want to include your JSON Web Token in the authorization header,
because in our code,
we said authorization as bearer token.
So, if you want to include the token in the authorization header,
in the authorization we'll say bearer and then we will paste
that token string that we have just copied from our incoming request.
So, we'll paste the token string in there.
So, this is what will be contained in
the authorization header of my outgoing request here,
outgoing POST request here.
So notice, the left side says bearer and the right side is the string,
the token that I have just copied from
the point where I logged in and then let me send the POST message
now and my POST is now successful and this is posted to my database.
Now, if you want to be sure that it has been posted,
let's do a GET and when you do a GET,
you can see that indeed that dish has been inserted into my database as shown here.
So, this is how you will be making use of JSON Web Tokens in your application.
So, whenever you need to do a POST, PUT,
or a DELETE operation on any of the endpoints,
you would be enclosing.
So, let me go back to this POST request here.
In the header, you will put
this authorization field and then this would be included in the authorization,
you will start it with bearer and then the token string,
after that bearer space,
one single space and then the token string following that.
So, that is how your request messages would
carry the JSON Web Token in the header of the outgoing request message.
You can also include the token in the body of the outgoing request message.
Now, for that, you will have to configure
your express server especially the extra JWT in the
authenticate.js file to accept it from
the body so you recall that there we have seen that
the extra JWT supports many different ways of extracting
the JSON Web Token from the incoming request.
So, there you need to specify,
if you're going to be including it in the body,
you need to specify that information there.
Now, the details of how to do that you can consult
the Passport JWT module documentation to understand how this is done.
In this course, I'm going to simply use that in the authorization header
as I have shown here and this works just fine for most cases.
Now, if you are developing a Web app,
an Angular app, or an Ionic or NativeScript app,
you need to be able to configure
that app whereby the JSON Web Token will be included in the authorization header.
In the last lesson of this course,
I will show you how to integrate the client and the server,
the client that you have developed in the previous courses with
the server that we developed in this course.
With this, we complete this exercise.
In this exercise, we have seen how we
can configure our application to use JSON Web Tokens,
and we have seen how we handle the authentication of the user using the JSON Web Token,
using the support from the Passport module and the Passport JWT module,
and the JSON Web Token Node modules.
With this, we complete this exercise.
This is a good time for you to do a Git Commit with the message Passport JWT.