- [Instructor] In this video,
we will now configure a passport JWT strategy
so that we can authenticate requests
with a JWT bearer token
and then we will also use this strategy
in the respective routes
so that our Todo List sample application works.
So let's kick this off
by heading into Visual Studio Code
and there, I open playground,
server, lib, passport
and there index.js because we are going
to implement this strategy, of course, there again.
And of course, passport comes with a JWT strategy
but we have to install it first.
So I'm stopping this process here in the terminal
and I now type npm install -S passport-jwt.
So this is the passport-jwt strategy.
And I run Node.js again
and I will now start by first bringing in the strategy.
So add const passportJWT
equals require passport-jwt.
And then we need two parts of this module.
So after we require UserService,
I now add const JWTStrategy
equals passportJWT.Strategy.
So that's the strategy.
We know that already from the local strategy
but we need one more thing
and this is const ExtractJWT.
So that's a helper that extracts JWT.
We will see in a second how this works.
But first, let's bring this in
and it's passportJWT.ExtractJWT.
Notice the lowercase here.
So I used uppercase JWT
but the model is called lowercase jwt
but we will leave it like that.
And next, we are going to implement now
or configure more or less this strategy.
For that, I scroll down.
And right before we do the serializeUser
and after the local strategy configuration,
I now add passport.use,
the same as with the local strategy
and now I add new JWTStrategy
and then we have this configuration object
and there we now define jwtFromRequest
and this is done now via ExtractJWT.fromAuthAsBearerToken.
So this uses this ExtractJWT helper
to extract the JWT token from this bearer token header
that we are sending from the Vue.js application.
And just real quick,
so in case you don't believe me,
let's head into this application real quick.
So in todolist, if you look into src
and there into api,
you have this index.js file
and there you see that really each request
has this Authorization: Bearer jwt header with it.
So this is coming from the Vue.js application
and here in our passport configuration,
we are configuring that we want to extract this header.
And then there is another property we have to set,
secretOrKey, because, of course,
we want to verify the signature of this token,
and for that we need the secret
and its config, and you remember that hopefully.
JWTSECRET.
So it's the same, of course,
that we used when signing this key.
Ignore the error that we are getting here.
This is just because we're not yet done
with setting this up
because we now have to define a callback
and it's async, so you see it's the next argument
in this JWTStrategy constructor
and this gets the jwtPayload
and also a done callback.
And now in there, I add a try catch block.
That's the same as we do always,
return done error if there is an error.
So what do we have to do now here?
Well, we have to load the user.
Const user equals await UserService.
So that's familiar already.
findById and where is the ID now?
Well, if you remembered,
when we created the token here in routes,
api, index.js,
we wrote it into userId.
So it is now inside the payload of the token
and with that, we can just fetch it from jwtPayload.userId.
And in case everything works fine,
we will have a user then
and we will now return null
and the user, as we did before.
So this will, of course, only work
if the token was valid, also not expired.
Then passport executes this callback
and tries to fetch the user now
with this given userId it gets from the payload
from the token.
Now, we are almost done
but we also have to, of course, apply this middleware.
For that, I now head back into the routes file.
So I'm now back in routes, api, index.js
and there we already have passport
but we now also have to use this strategy
for the respective routes.
So let's start with the whoami route.
So this route should actually give us back
the currently authenticated user
or the username of this user.
So I now add passport.authenticate
and the strategy is now not local but jwt,
so we instruct passport to look into the header
and try to extract the JWT token there
and again, session is false
because we don't need a session
or the user stored into the session here.
Then, of course,
we also don't want to return not implemented
but we will return res.json
and in this JSON object,
I now write username: request.user.username.
Again, the Vue.js application
is configured in a way
so that it expects the username in this property.
We can remove the next argument here
and with that, the whoami route works.
Again, this works because if passport manages
to get a valid JWT token from the header
and then can also load a user,
we will end up this callback
and then, of course, we will have a user in request.user.
Now we also have to authenticate all the API requests
for the todo list
and for that, we can now just do the same
as we did before but now we do it in this mount
of the todolist router.
So just copy now this line from line 33
and I paste it in here.
And with that, all requests
to the API will be authenticated
and also require authentication via JWT.
If you look into the todolist here,
you will see that we are, for instance,
when we add an item here in line nine,
we are relying that we have a request.user.id
to add this item and assign the right user to it.
So really, this API relies on the user being transmitted
via this token.
So with that, we should be now good to try this out.
So I'm opening the browser again.
So before we try to log in,
let's open the Chrome Developer Tools
because I really want to see what's going on here
under the hood and now let's try to log in.
Lcorla and my secret too.
And I'm in.
So I'm now in the Todo List application.
You see first we are doing the login
and if we scroll down,
we see that I'm sending here the username
and the password
and this gives us back this JWT token
that we created in the backend on the API.
Then we are calling whoami.
This is then used to fill out this logged in as section
and you see that it returns the username
and then we try to fetch the todolist
and this is right now empty
but what's interesting here
is now if you look at the headers,
and scroll down to the request headers,
you see that I'm sending here Authorization: Bearer.
So that's not a bearer token.
And I told you already that this is done programmatically.
This means I'm doing that in my Vue.js code
and I'll show you that real quick.
So in Visual Studio Code, I open todolist,
src, and api and there in index.js,
you see that each of those methods
that call into the API gets this JWT token
as an argument
that JWT token is stored in the application itself
and then we're sending the header
like you see, starting with line 18.
Authorization: Bearer and then this JWT token.
Now let's switch back to the browser
to use now this todo list,
so I'm adding a new to-do item, finish course.
Then work through beta tests.
Talk to producer.
And as you see there,
items are now added to the list
and I can even click on them
to cross them out, to mark them as done
or I can also edit them
by clicking on this edit icon here
and I can say work through beta tests again
and you will see this will also be updated.
Of course, I can also delete items
by just clicking on the delete button.
If we look at what's going on here under the hood,
and you see in the request URL
that it contains the ID of the item
that we want to update as the last pass argument.
And if you scroll down,
you can also see the payload
and you see that this was an action
that marked one item as done, completed: true.
And you see that this was the update
and the action that marked an item as completed.
Now, to make this a little bit more fun,
I also want to show you that this is really capable
of doing multi-tenancy.
So I'm heading to localhost:3000
and I'm creating a new user
by clicking to Registration
and the user is jdonaldson
and the email address is jdonaldson@explorecalifornia.com
and I'm setting a password.
And I click on Create Account.
All right, now let's log out this one user
and I want to show you one more thing first.
If I reload, I'm still logged in
and this works just because I'm setting a cookie
from Vue.js that contains the JWT token
and in my application,
I'm just looking if there is a cookie
and if there is that cookie,
I'm using this JWT token again.
So that's the reason why we can we even reload the page,
otherwise I would be logged out
because the Vue.js application cannot persist anything
beyond a reload of the browser.
Now let's log out
and let's try to log in this user.
So it's jdonaldson.
And I'm providing the password.
And if I click on Login,
you see that I get the 401 error.
So something is still wrong
and I know exactly what it is.
We have to verify this user.
So I click on the user list
and click on this verify button
and now the user's verified.
Now let's try it again.
I hit Login
and I'm now in the to-do list of jdonaldson
and I can again add items here.
So this works now.
We have a multi-tenant to-do list application
that you could now deploy to the web
and offer it as a service.
There is just one more thing that I want to show you.
So let's look at what happens if I update an item.
So let's click on go fishing.
And let's say it's go fishing again
and if I look at this request,
you see as before, I'm doing this put request
and I'm sending it to the URL
that contains the ID of the item
that I want to change
and in the payload, I send the new description
that should be updated.
Now, let's look into Visual Studio Code
and let's follow this path a little bit.
So if I go into server, routes,
let's close the index here,
and head into api
and todolist and index.js,
let's look for this update route.
So it has to be a put and here it is.
So it's put and then there is the item,
ID is an argument.
And then I'm calling TodolistService.updateItem
with the itemId and the body.
So if I open that,
and up here, and you see that I'm just doing a findOne
with the ID and then I'm updating item.completed
and item.description before I save this item.
And here we have a very typically security flaw hidden.
This is very often overseen
and I invite you to look at it
and try to figure out what could be the problem here.
The problem is in updateItem and also in deleteItem.
I will show you what's the problem here in the next video.