WEBVTT

00:00.920 --> 00:09.140
Okay, So next up, we're going to add authentication to our gateway microservice so that when requests

00:09.140 --> 00:16.250
come in to our GraphQL server on the Gateway, they will be authenticated using our normal JWT process.

00:16.790 --> 00:23.450
So to do this, we're going to have the gateway microservice send a request to the auth microservice

00:23.450 --> 00:30.040
using our TCP transport layer and the Nestjs microservices package.

00:30.050 --> 00:32.000
So let's go ahead and set this up.

00:32.000 --> 00:39.830
We will go into the Gateway module and add a new section to the imports array where we'll go ahead and

00:39.830 --> 00:47.660
import clients module dot register async, and then we'll provide an array of clients that we're going

00:47.660 --> 00:48.590
to configure.

00:48.590 --> 00:52.420
In our case, it will just be the auth service that we're going to configure.

00:52.430 --> 00:58.400
So let's import the auth service from app slash common as our injection token and then we will use the

00:58.400 --> 01:04.530
use factory to set up the configuration of this client.

01:04.530 --> 01:11.430
So go ahead and make sure we inject the config service and now we can expose the configuration object.

01:11.580 --> 01:19.860
Also, don't forget to provide the inject property where we actually inject the config service so that

01:19.860 --> 01:21.870
it's available in the use factory.

01:22.410 --> 01:26.220
So we'll start off with specifying the transport layer.

01:26.220 --> 01:32.400
In our case we're going to use transport dot TCP and then we'll specify the options.

01:32.400 --> 01:39.060
So we'll use our familiar options where we have the host and we'll use the config service dot get or

01:39.060 --> 01:49.770
throw to extract the auth host and then we'll get the port using config service dot get or throw auth

01:50.310 --> 01:51.120
port.

01:52.320 --> 01:59.940
So with this, we'll have to go into our M file and make sure we provide the auth host and auth port.

02:00.060 --> 02:01.560
So let's go ahead and do that.

02:01.560 --> 02:08.250
We'll specify the auth host is equal to auth, which is the service name we define in our Docker compose.

02:08.250 --> 02:14.610
And then we have the auth port which we know will be listening on Port 3002, which we can confirm by

02:14.640 --> 02:20.950
going into the auth dot env and we want the TCP port at 3002.

02:20.970 --> 02:27.720
So now that we've correctly set up the auth client, let's go ahead and in the Gateway source folder

02:27.720 --> 02:36.210
we're going to create an auth dot contexts file and this is going to be a function that our gateway

02:36.570 --> 02:42.300
microservice will call each and every time a request is sent to our GraphQL server.

02:42.300 --> 02:49.290
And in our case, we want to reach out to the auth service to authenticate a JWT token and then attach

02:49.290 --> 02:55.510
the user that gets sent back on the current request so that our downstream GraphQL services will have

02:55.510 --> 02:58.510
access to it using our current user decorator.

02:59.130 --> 03:07.080
So let's go ahead and export a const called auth context and set it equal to async.

03:07.290 --> 03:12.270
And in here we'll get access to an object that has the request on it.

03:12.720 --> 03:15.330
So we'll go ahead and open up a function.

03:15.870 --> 03:18.630
And now let's open up a try catch block.

03:20.100 --> 03:22.320
Where we catch any errors that occur.

03:22.320 --> 03:25.470
And firstly, I want to check to see if we have any errors.

03:25.470 --> 03:33.360
Well, if we have any errors, let's re throw this as a new unauthorized exception from Nestjs Common

03:33.360 --> 03:37.280
and we'll pass the error back into this unauthorized exception.

03:37.290 --> 03:42.990
Now we need to get access to the auth client in this function.

03:42.990 --> 03:50.250
And since this function is being run outside of the nestjs context, we need a way to actually pull

03:50.250 --> 03:54.870
or register the auth service right in this function.

03:54.870 --> 04:02.760
So in order to do this we can create a new file in the source directory that I'll call app.ts.

04:02.790 --> 04:10.800
And in here I want to create a new let called app that will be of type I Nest application.

04:10.980 --> 04:18.720
And then I want to expose a function called set app that our main.ts will call when we bootstrap the

04:18.720 --> 04:26.380
application so that we set this application and make it available to our auth context when we need it.

04:26.500 --> 04:33.670
So we're going to have this function take in an underscore app parameter that will be of Type I nest

04:33.670 --> 04:40.750
application and all we're going to do is simply set this variable equal to the underscore app.

04:40.900 --> 04:46.960
Finally, let's go ahead and export the app and the set app function from this file.

04:46.960 --> 04:55.540
And now in our main.ts, after we bootstrap and initialize our application, I want to call, set,

04:55.540 --> 05:02.890
app and pass in the app that we just created so that it will be set and available later on in our auth

05:02.890 --> 05:03.760
context.

05:03.760 --> 05:10.900
And now we can actually use this app in the auth context because it'll be set at this point and we can

05:10.900 --> 05:20.020
get access to the auth client now by declaring a new const called auth client, set it equal to app,

05:20.020 --> 05:22.480
which we import from dot slash app.

05:22.480 --> 05:30.250
And on the app we have the get function which will let us get access to any provider in our application.

05:30.250 --> 05:36.790
In our case, we know this is going to be of type client proxy so we can specify the type and then pass

05:36.790 --> 05:44.680
in the auth service injection token that we registered in our client module in the Gateway module here.

05:44.710 --> 05:49.690
This injection token is matching the one we're pulling on the app dot get call.

05:49.690 --> 05:54.190
So also make sure we import this from app slash common.

05:54.400 --> 06:00.910
So now that we have the auth client, we want to actually reach out to the auth service and verify our

06:00.910 --> 06:01.510
JWT.

06:01.900 --> 06:09.910
So to do this we'll declare a new const called user and set it equal to well, we want to convert this

06:09.940 --> 06:17.950
auth client call to a promise because by default this auth context will only work with promises and

06:17.950 --> 06:19.060
not observables.

06:19.060 --> 06:21.580
So to do this we'll call await.

06:21.580 --> 06:28.530
And then in order to convert an observable into a promise, we can use this function called last value

06:28.540 --> 06:30.790
from rxjs.

06:30.790 --> 06:36.280
And now inside of this function we will pass in our source observable, which in our case will be auth

06:36.280 --> 06:38.410
client dot send.

06:38.830 --> 06:44.200
And then we're going to send to the authenticate route.

06:44.230 --> 06:50.290
And if you recall, you can go back to the auth controller where we have our authenticate message pattern

06:50.290 --> 06:57.280
that expects the JWT to be set and runs through the JWT auth guard and the JWT strategy.

06:57.460 --> 07:00.520
And now we will provide the data.

07:00.520 --> 07:05.140
In our case it will be the authentication property.

07:05.170 --> 07:12.970
And now we're going to pass the request dot headers, dot authentication.

07:12.970 --> 07:19.720
So we're pulling the authentication off of the GraphQL request and sending it directly to our auth service

07:19.720 --> 07:20.770
to verify.

07:20.800 --> 07:28.810
Now finally, we will return the user inside of an object outside of this auth context.

07:28.840 --> 07:35.710
So to now actually utilize the auth context, we'll go back to the Gateway module and inside of the

07:35.710 --> 07:45.240
GraphQL module, we can actually add a another property above the gateway property called Server.

07:45.260 --> 07:53.360
And inside of server we can open an object and provide a context parameter which takes in the context

07:53.360 --> 07:55.050
function that we just created.

07:55.070 --> 08:01.790
So we'll pass in that auth context function which will get called every time a GraphQL request is sent

08:01.790 --> 08:03.380
in to our gateway.

08:03.650 --> 08:10.910
So finally we need a way to actually attach the user that gets returned from the auth context onto the

08:10.910 --> 08:16.970
outgoing request that gets sent to our downstream microservices like our reservations service.

08:17.060 --> 08:24.470
So in order to do this, we can add an additional property to the gateway object that will be called

08:24.500 --> 08:26.170
build service.

08:26.180 --> 08:32.600
And this is going to be a function that takes in an options object and we'll go ahead and destructure

08:32.600 --> 08:42.770
the URL from it and then open up a function that will return a new remote GraphQL data source.

08:42.920 --> 08:48.770
And in here we'll open up another options object where we'll pass the URL back in because we want to

08:48.770 --> 08:52.970
maintain the same URL to the downstream GraphQL microservice.

08:52.970 --> 09:00.740
And then importantly, we can attach a middleware function called will send request, which is a function

09:00.740 --> 09:08.390
that gets called every time We are about to send a request to a remote GraphQL data source or downstream

09:08.390 --> 09:10.010
GraphQL service.

09:10.040 --> 09:19.400
So this is another function that has an options object that we can pull the request off of as well as

09:19.400 --> 09:23.840
the context off of that was returned from our auth context.

09:24.020 --> 09:33.530
So now we'll open up a function and what we can do is we can call request dot http headers.

09:33.530 --> 09:38.990
And on these Http headers we will call set to attach a new header.

09:39.110 --> 09:43.160
And in here we're going to add a new header called User.

09:43.520 --> 09:50.240
And we'll check to see if we have a context dot user from our auth context.

09:50.270 --> 09:55.400
Then we want to json.stringify that user.

09:56.060 --> 09:58.970
Otherwise we will just add a null.

09:59.000 --> 10:06.200
So now whenever a request is authenticated and sent to our downstream microservices, we're going to

10:06.200 --> 10:12.530
add this user header which is going to stringify the user that we returned from our auth context and

10:12.530 --> 10:17.300
we'll have this user available later on in our GraphQL microservices.

10:17.720 --> 10:25.520
So now on the other end of this, in our reservations resolver, as we are using the current user decorator,

10:25.520 --> 10:33.080
we'll go to the current user decorator and refactor this to work with GraphQL and our newly refactored

10:33.110 --> 10:34.370
auth context.

10:34.370 --> 10:41.840
So inside of this, get current user by context function, I first want to check to see if the context

10:41.840 --> 10:50.030
dot get type, which is a function that will return the current execution context is equal to Http.

10:50.570 --> 10:58.850
Well then we will just follow the same existing behavior where we switch to Http, get the request and

10:58.850 --> 11:05.150
pass the user on the request object which is populated from our local strategy.

11:05.180 --> 11:13.520
However, if the context dot get type is GraphQL, then what we want to do is extract the user.

11:13.520 --> 11:21.320
So we'll have a new const called user and we can switch the context to GraphQL by calling context dot

11:21.320 --> 11:22.970
get args.

11:23.840 --> 11:28.730
We're going to access the second argument in the context args array.

11:28.760 --> 11:32.510
We'll have access to the incoming request from GraphQL.

11:32.630 --> 11:39.040
We can then get the headers and finally we have the user that we set on those headers.

11:39.050 --> 11:46.340
So if the user does exist on the headers like we set previously in the Gateway module, then we will

11:46.340 --> 11:47.300
return.

11:47.480 --> 11:51.440
Well, if you remember correctly, we just stringified the user.

11:51.440 --> 11:56.060
So now we need to parse it back into an object.

11:56.060 --> 12:02.990
So we'll call Json.parse, parse the user and return it to our correct resolver.

12:02.990 --> 12:08.450
So I'll go ahead and start up all of our services by running Docker compose up.

12:08.450 --> 12:15.740
So now that our services have started up successfully, I can go to my browser and navigate to localhost

12:15.740 --> 12:24.210
3004 slash GraphQL and now we're met with the GraphQL playground UI, which is essentially going to

12:24.210 --> 12:26.100
be the equivalent of using Postman.

12:26.100 --> 12:33.330
But in the GraphQL world now, right now our server cannot get a successful response back, and that's

12:33.330 --> 12:40.950
because we're not supplying any authentication headers to our server, which we know we need to be supplying

12:40.980 --> 12:44.670
in order to be authenticated to access our GraphQL server.

12:44.670 --> 12:51.060
So let's head to Postman where we can launch a request to authenticate with our system.

12:51.060 --> 12:58.110
So I'm going to go ahead and create a new user on the auth microservice by calling localhost 3001 slash

12:58.110 --> 13:07.230
users and post a new account to create this user and then we can launch a request that auth slash login

13:07.230 --> 13:08.430
to log in.

13:08.430 --> 13:11.880
And now we have the JWT sent back to us.

13:11.910 --> 13:13.680
I'll go ahead and copy this.

13:13.680 --> 13:21.210
And now I can go back to the GraphQL playground and on the bottom here I've clicked on Http headers

13:21.210 --> 13:29.190
to open up this headers window and we will create an object here and we'll supply our first header by

13:29.190 --> 13:34.680
providing an open string and setting the header authentication.

13:34.680 --> 13:42.030
And if you remember, this corresponds to the header that we're pulling off in our auth context.

13:42.060 --> 13:45.870
We're pulling it off of request dot headers, dot authentication.

13:45.870 --> 13:50.190
So that's going to be the same set of headers that we're providing here.

13:50.460 --> 13:58.410
Let's go ahead and open up colon quotes and now paste in the JWT from that postman call.

13:59.190 --> 14:07.620
So now we can click on the right tab over here schema to view our raw GraphQL schema, which Nestjs

14:07.620 --> 14:15.570
generated for us using the code that we decorated using the input type, object type and field decorators.

14:15.570 --> 14:21.450
We have the entire GraphQL schema that we can view here, which includes all of our mutations, queries

14:21.450 --> 14:22.500
and arguments.

14:22.500 --> 14:28.110
However, to view this in a more readable format, we can click on the docs window and now we can see

14:28.110 --> 14:33.990
a nice view of all of the queries and mutations we have available to us which are coming from our downstream

14:33.990 --> 14:37.590
GraphQL services we defined in the Gateway module.

14:37.590 --> 14:41.850
So right now this is just a reservation service, GraphQL Server.

14:41.970 --> 14:49.470
We have the query to query all reservations and a single reservation to get back the reservation document.

14:49.560 --> 14:51.960
We can see the type that comes back.

14:51.960 --> 14:57.930
So this is going to be the reservation document which we decorated with the field decorators earlier.

14:57.930 --> 15:04.920
So all of these properties that we decorated have been added to these docs here and we can see the mutations

15:04.920 --> 15:07.770
as well to create a new reservation.

15:07.770 --> 15:14.070
And we can even see the arguments that are required for this call and our Create Reservation DTO So

15:14.070 --> 15:18.420
this is very useful because it generates all of this great documentation for us.

15:18.420 --> 15:23.100
Well, without having to do anything besides merely decorating our code properly.

15:23.100 --> 15:27.780
So let's go ahead and actually try out these mutations and queries to make sure everything's working

15:27.780 --> 15:28.650
properly.

15:28.710 --> 15:35.880
So to do this, I'm going to execute a mutation by typing a mutation open curly brackets.

15:35.880 --> 15:41.100
And now we have access to the mutation that we called Create Reservation.

15:41.340 --> 15:48.510
And now if we open up parentheses, we can automatically have the input expects here to be supplied

15:48.510 --> 15:56.160
so we can supply a colon and then open up another object where we supply the create reservation input.

15:56.850 --> 16:05.460
So I've gone ahead and copied an existing request from Postman and pasted it into our options object

16:05.460 --> 16:12.210
here to supply all of the parameters that the Create Reservation input expects, including our start

16:12.210 --> 16:19.050
date, end date and charge object, which contains all of the necessary details to create a reservation.

16:19.200 --> 16:23.100
So now what we're going to do is we're going to open up another curly brackets at the end.

16:23.130 --> 16:23.760
End here.

16:23.760 --> 16:30.090
And this is where we actually tell GraphQL which properties we want to pull back from this call.

16:30.090 --> 16:36.420
And this is another one of the great benefits about GraphQL is that we don't pull back all of the data

16:36.420 --> 16:37.770
by default over the wire.

16:37.800 --> 16:42.960
We're able to choose which properties we want to send back and limit the amount of data that gets transferred

16:42.960 --> 16:44.670
to make things a bit more efficient.

16:44.700 --> 16:48.090
However, in our case, I want to pull back every property.

16:48.090 --> 16:54.150
So let's go ahead and specify all of these properties, including the start date, end date, invoice

16:54.150 --> 16:54.600
ID.

16:54.960 --> 17:00.960
We can pull back the user ID and finally the timestamp as well as the underscore ID.

17:01.260 --> 17:08.460
So now if we execute this request to create a reservation, we can see the request succeeded and we

17:08.460 --> 17:12.600
get this data sent back to us that we requested in the mutation.

17:12.600 --> 17:19.500
So now let's go ahead and open up another tab up here to actually query this reservation that we just

17:19.500 --> 17:20.370
created.

17:20.370 --> 17:27.310
Let's use the query keyword and now we'll call reservations to pull back all reservations.

17:27.310 --> 17:33.430
And now we can just simply paste in all of these properties we've already asked to get back from the

17:33.430 --> 17:34.450
mutation.

17:34.450 --> 17:41.380
We'll paste in to this object and we can get back all of the reservations and we can see the one we

17:41.380 --> 17:42.220
just created.

17:42.220 --> 17:47.590
Now, if we want to be more selective and query by ID, we'll change the query to just reservation.

17:47.590 --> 17:52.390
And now it's complaining because it expects that ID on the reservation.

17:52.390 --> 17:55.090
So let's go ahead and supply the ID now.

17:55.090 --> 18:00.130
And now if we execute this, we get back the single reservation that we just created.
