WEBVTT

00:00.020 --> 00:06.650
And now we need to implement another strategy that actually validates that JWT so that we can use this

00:06.650 --> 00:10.730
on all of our other routes where we want to apply authentication to.

00:10.970 --> 00:16.820
So let's go ahead and open up our auth directory again and let's open up and create a new strategy in

00:16.820 --> 00:22.440
our strategy folder called JWT dot Strategy Dots.

00:22.460 --> 00:26.690
So this is going to be a new injectable class here.

00:27.650 --> 00:33.860
We're going to export a class called JWT Strategy, which will follow the same pattern as our local

00:33.860 --> 00:34.400
strategy.

00:34.400 --> 00:40.520
We're going to extend passport strategy here from Nestjs passport, and this time we're going to pass

00:40.520 --> 00:43.370
in the strategy from Passport JWT.

00:43.400 --> 00:48.710
So recall that this strategy that we installed earlier will handle most of the work here.

00:48.710 --> 00:53.360
We just need to supply some configuration inside of the constructor here.

00:53.360 --> 00:59.510
So in the constructor itself, we're going to need to get access again to the config service here.

00:59.510 --> 01:04.830
So let's go ahead and inject the config service as well as the user service.

01:04.830 --> 01:07.680
So let's go ahead and inject the user service here.

01:07.680 --> 01:11.880
And now inside of the constructor we're going to make a call to super here.

01:11.880 --> 01:15.060
And now there's some options that we need to configure.

01:15.090 --> 01:20.010
First property we're going to configure here is JWT from request.

01:20.010 --> 01:26.130
And what this is going to do is it's going to actually specify where on the request object that the

01:26.130 --> 01:27.660
JWT lives.

01:27.660 --> 01:33.360
So by default, this might be a header value, but in our case, we know that the gut is actually a

01:33.390 --> 01:34.530
cookie itself.

01:34.530 --> 01:41.490
So let's go ahead and specify that here by calling extract JWT from Passport, JWT, and then we're

01:41.490 --> 01:51.120
going to call from extractors on here and supply an array where we will provide a function that gets

01:51.120 --> 01:52.650
the current request.

01:52.650 --> 01:58.140
So the request object here, you know, will actually be from Express.

01:58.140 --> 02:01.110
So go ahead and import request.

02:01.510 --> 02:08.730
From express top and now we specify where on the request object where the GDB lives.

02:08.740 --> 02:16.750
So in this case we're going to look under request dot cookies, dot authentication, which is where

02:16.750 --> 02:22.030
our JWT should be if it was correctly set on our auth service.

02:22.040 --> 02:29.200
Now that we've actually told the strategy where the JWT lives, we need to supply the secret or key,

02:29.200 --> 02:34.480
which is going to be the JWT secret value that it will use to decode it.

02:34.480 --> 02:37.210
So of course this will be the same key that we use to sign.

02:37.210 --> 02:43.000
The cookie is going to be the same key we're going to use to verify this cookie so we can use the config

02:43.000 --> 02:48.270
service here and call config service dot get JWT secret.

02:48.280 --> 02:53.950
So now we've configured our constructor, we're going to have that async validate method again, where

02:53.950 --> 02:56.260
in this case we're going to get the user ID.

02:56.650 --> 03:03.380
And the reason we're getting the user ID here is because after the JWT is decoded, the token payload

03:03.380 --> 03:08.090
used when creating the token is going to be supplied to the validate method here.

03:08.090 --> 03:13.460
So if you remember, if we go to auth service when we actually created the token, we had this token

03:13.460 --> 03:16.640
payload here where we included the user ID.

03:16.820 --> 03:20.390
So let's actually go ahead and define this as an interface here.

03:20.390 --> 03:28.430
So I'll create a new folder here called Interfaces and we will add a token payload dot interface, dot

03:28.430 --> 03:35.240
ts, and we'll export this interface called token payload where we know we have the user ID of type

03:35.240 --> 03:35.980
string.

03:35.990 --> 03:42.470
So now in the auth service we can actually specify the dedicated interface and we'll do the same thing

03:42.470 --> 03:43.790
here in the strategy.

03:43.790 --> 03:50.420
So after this JWT is decoded, we get that token payload in the valid method which this strategy will

03:50.420 --> 03:52.310
take care of all for us.

03:52.310 --> 03:58.190
And while we have that token payload, it's now a responsibility to check to see if the user on that

03:58.190 --> 04:00.020
token actually exists.

04:00.020 --> 04:06.710
So we can easily do that by calling this dot user service, dot get user.

04:06.710 --> 04:13.610
And I'm going to pass in an object here where we supply the underscore ID and pass in the user ID.

04:13.610 --> 04:15.560
So of course we're going to have to implement this.

04:15.560 --> 04:24.500
So let's go in to the user service and implement a new method here called async get user.

04:24.500 --> 04:29.540
And inside of this route here, we're actually going to specify a new DTO file.

04:29.540 --> 04:32.780
So let's specify a DTO to get a user.

04:32.780 --> 04:41.270
So in the folder I'll add to get user DTO and we'll export a class here called Get User DTO here we

04:41.270 --> 04:45.980
will specify that the user will pass a string to get the user.

04:45.980 --> 04:50.870
We'll use class validator to make sure that this is a valid string here.

04:50.870 --> 04:53.390
And also this is not empty.

04:53.720 --> 05:01.190
So now in the user service we'll specify that we have get user, DTO type, get user DTO, and then

05:01.190 --> 05:09.350
we can call this DOT users repository dot, find one and pass in the get user DTO.

05:09.350 --> 05:15.920
So now if we go back to our JWT strategy, you can see that we are correctly getting the user here after

05:15.920 --> 05:17.810
we decode the token.

05:17.810 --> 05:21.590
And if the user doesn't exist, of course we know we will throw a 404.

05:21.740 --> 05:26.330
So now that we have the JWT strategy, let's go ahead in our guards folder.

05:26.330 --> 05:29.180
And this is going to be very similar to our local off guard.

05:29.180 --> 05:33.140
We're going to export a class here called JWT Auth Guard.

05:33.230 --> 05:39.890
That's going to extend auth guard from Nestjs passport and we will just pass in JWT here.

05:39.890 --> 05:48.290
So now we can test out our JWT auth guard and JWT strategy by going into our users DOT controller.

05:48.290 --> 05:55.160
And so I want to create a new route in here, a get route that will actually just return the authenticated

05:55.160 --> 05:55.940
user.

05:55.940 --> 05:58.310
And this is going to be very easy for us to do.

05:58.310 --> 06:05.180
So this is going to be an async get user route and we actually don't need to supply any args here.

06:05.180 --> 06:12.470
Instead, we can actually pull the current user off of this route, which we know will be of type user

06:12.470 --> 06:18.260
document here and all we're going to do is actually just return this user back to the caller.

06:18.260 --> 06:25.880
And now all we need to do is actually run the use guards here and supply the JWT auth guard.

06:25.880 --> 06:33.020
So just like the local strategy in the JWT strategy, whatever we return from this validate method still

06:33.020 --> 06:35.690
gets populated on the request object.

06:35.690 --> 06:41.990
So as soon as this guard is run, we have the user on the request object and we can simply return it

06:41.990 --> 06:45.590
back to the caller without having to do anything else here.

06:45.590 --> 06:52.640
And lastly, make sure in our auth module here, just like we did for the local strategy, we add the

06:52.670 --> 06:56.810
JWT strategy in our providers array here.

06:56.810 --> 07:01.070
So now in Postman, I'm going to launch a get request at localhost 3000.

07:01.330 --> 07:04.930
One slash users to get my user here.

07:04.930 --> 07:08.920
And if I launch this request off right now, you can see we get a 401.

07:08.950 --> 07:11.140
Unauthorized, which is great to see.

07:11.170 --> 07:15.040
We haven't actually included any valid JWT with this call.

07:15.040 --> 07:21.190
So we expect that it is unauthorized and this is going to be the same guard and process we apply to

07:21.190 --> 07:22.630
any protected route.

07:22.630 --> 07:25.810
If we don't have a JWT, we will get that 401 back.

07:26.080 --> 07:33.520
So to get a JWT, I'm going to launch a post request at localhost 3001 slash auth login.

07:33.520 --> 07:36.670
And this is going to be for a user I've already created here.

07:36.670 --> 07:38.620
I have the credentials in the body.

07:38.950 --> 07:40.420
I'll go ahead and send this.

07:40.420 --> 07:45.460
And so now we have the JWT set on our cookies here.

07:45.460 --> 07:53.890
So now if we launch a request at users to get the current user, you can see we're still getting a 401.

07:53.920 --> 07:58.570
Unauthorized even though we're actually sending the auth cookie with this request.

07:58.570 --> 07:59.650
So why is that?

07:59.650 --> 08:06.050
Well, this is because right now we're not actually parsing the incoming cookies on the request.

08:06.050 --> 08:11.900
So in order to do this, we're going to go ahead and install a new package.

08:11.900 --> 08:17.660
So let's go ahead and NPM install a package called cookie parser.

08:17.660 --> 08:22.940
And what cookie parser is going to do is it's going to parse incoming cookies and add them to the request

08:22.940 --> 08:23.720
object.

08:23.720 --> 08:29.630
I'll also go ahead install the development dependency for the cookie parser types here as well, and

08:29.630 --> 08:32.750
then we can go ahead and start our containers back up.

08:32.750 --> 08:41.090
So now in our main.ts for our auth project, what I want to do here is I want to import Star as cookie

08:41.120 --> 08:44.780
parser from cookie parser.

08:44.780 --> 08:52.850
And now what we're going to do is before we add the validation pipe here, we're going to call app dot

08:52.850 --> 08:53.660
use.

08:53.660 --> 09:00.680
So this is going to be a middleware and all we have to do is call cookie parser here as a function and

09:00.680 --> 09:07.580
automatically all requests with their cookies will be executed through this middleware here and all

09:07.580 --> 09:09.680
incoming cookies will be now parsed.

09:09.680 --> 09:16.940
So now inside of Postman, now that we've set up cookie parser, if I launch this get request when we

09:16.940 --> 09:19.340
launch it, we see we actually get a 200.

09:19.340 --> 09:26.390
Now that this request was successful and we are actually getting the user back here because of our JWT

09:26.420 --> 09:30.260
authentication working properly and our cookies are getting parsed.

09:30.260 --> 09:33.440
So we have implemented full end to end authentication.

09:33.440 --> 09:40.130
With JWT, we have an auth guard we can use anywhere in our application to lock down our routes and

09:40.130 --> 09:42.110
we are ready to move on.

09:42.110 --> 09:44.560
So let's go ahead and do that in the next lecture.

09:44.570 --> 09:50.630
Right now in our user service, when we create a new user, we're actually currently allowing multiple

09:50.630 --> 09:56.180
users with the same email to be created, which could be leading to issues when we actually want to

09:56.180 --> 09:58.250
verify the user by that email.

09:58.250 --> 10:04.910
So let's go ahead and actually implement some validation to ensure that only a unique email can be used

10:04.910 --> 10:06.440
when creating a user.

10:06.440 --> 10:13.400
So before creating this user, we'll call await this dot, validate, create user DTO, and then we'll

10:13.400 --> 10:19.700
have a private async validate, create user DTO here, which of course takes in the create user.

10:19.700 --> 10:28.040
DTO I'm going to open up a try catch block here and inside of the try we're going to call await this

10:28.040 --> 10:36.680
dot users repository dot find one and look up this user by the email in the create user DTO.

10:36.680 --> 10:43.820
So if this user does already exist in the repository, we know that we're going to throw a 404 error

10:43.820 --> 10:45.500
and end up in this catch block.

10:45.500 --> 10:51.440
So if that's the case, I actually want to return right away and exit this method because if the user

10:51.440 --> 10:55.820
does not exist, that means this crazy DTO is actually valid.

10:55.820 --> 10:59.360
So we can just leave the catch block and create the user.

10:59.360 --> 11:06.350
However, if the user did actually exist and we didn't trigger an error, then I want to throw a new

11:06.350 --> 11:12.800
unprocessable entity exception where we say the email already exists.

11:13.040 --> 11:20.270
So now if the user was found, we throw an error here because the user should not exist with that email.

11:20.270 --> 11:27.890
So now back in Postman, if I launch a post request to create a new user, I will send this off here

11:27.890 --> 11:31.640
and we can see we are throwing an error because this email already exists.

11:31.640 --> 11:33.710
So this is exactly what I expect to see.

11:33.740 --> 11:38.180
Now, if I change the email to a unique email, we can see the user gets created.
