Coding

#10 Adding User Authentication | Build a Complete App with GraphQL, Node.js, MongoDB and React.js

  • 00:00:01 hi welcome back to the serious in the
  • 00:00:04 last part of the series it was refactor
  • 00:00:07 time and this part will add something
  • 00:00:09 new will add user authentication will
  • 00:00:12 make sure that the users which can
  • 00:00:14 already be created in our current setup
  • 00:00:16 that the users can login that we create
  • 00:00:19 a token for the users and that this
  • 00:00:21 token can then be used to authenticate
  • 00:00:23 users for some protected resources in
  • 00:00:26 our API let's get started
  • 00:00:31 so here is the API as I left it this is
  • 00:00:34 our current graph QL API and I first of
  • 00:00:38 all want to work on my schema and I want
  • 00:00:41 to add an endpoint for users to log in
  • 00:00:45 because creating users is already
  • 00:00:47 possible so that is basically our sign
  • 00:00:49 up logic but now we need to make sure
  • 00:00:52 that users can not only sign up but also
  • 00:00:54 sign in now this is not really a
  • 00:00:58 mutation which I want to add therefore
  • 00:00:59 because signing a user in logging in
  • 00:01:02 doesn't change anything here on our
  • 00:01:05 server or in the database of course you
  • 00:01:08 could have a different logic where you
  • 00:01:09 want to add an entry in a database or do
  • 00:01:11 whatever you want but for me here
  • 00:01:14 logging in is jnana query I'm querying
  • 00:01:17 for a valid token for this user and the
  • 00:01:21 only interesting thing about that query
  • 00:01:22 is that it will have some arguments
  • 00:01:24 namely the email address and the
  • 00:01:27 password which I verify on the server so
  • 00:01:30 therefore here I will add login as a
  • 00:01:32 root query and the login query will have
  • 00:01:36 some data that is passed along and that
  • 00:01:39 data will be my email which is a string
  • 00:01:41 that is required and a password which is
  • 00:01:44 all just string that is required so this
  • 00:01:47 is the type of data
  • 00:01:49 I expect on that query when a user wants
  • 00:01:52 to sign in the response should be data
  • 00:01:56 which contains a token maybe some
  • 00:01:59 information when the token is about to
  • 00:02:01 expire and also some information about
  • 00:02:04 the user ID we could say so I will add
  • 00:02:07 this new type up there maybe we'll load
  • 00:02:10 a user and I'll name it off data you can
  • 00:02:13 name it however you want and in there I
  • 00:02:15 want to return the user ID so dad will
  • 00:02:19 just be armed a unique ID not the ID
  • 00:02:23 which is created because we're not
  • 00:02:25 creating anything here we're just
  • 00:02:27 checking the credentials and then we
  • 00:02:29 create a token which we don't store on
  • 00:02:31 the server though but I will return the
  • 00:02:34 idea of the user who was offended cated
  • 00:02:35 I'll return the token JSON web token is
  • 00:02:39 what I will use here and if you're not
  • 00:02:42 sure what this is
  • 00:02:43 I do explain it in greater
  • 00:02:44 tell in my note rest series on his
  • 00:02:47 channel in my note complete guide and of
  • 00:02:49 course Google always is your friend at
  • 00:02:51 the end this is a mechanism an
  • 00:02:54 authentication mechanism which we can
  • 00:02:55 use in applications where we have a
  • 00:02:58 decoupled front and back end where they
  • 00:03:00 offer we're not using a session because
  • 00:03:02 the server doesn't care about the client
  • 00:03:04 and where we instead pass a token to the
  • 00:03:06 client which the client can store and
  • 00:03:09 attach to subsequent requests and on the
  • 00:03:11 server we can then validate this token
  • 00:03:13 because we can validate it in a way that
  • 00:03:15 ensures that the client can't fake it
  • 00:03:17 and therefore we can prove ok
  • 00:03:20 this client did login in the past this
  • 00:03:22 token is valid you have access this is
  • 00:03:24 the general flow we'll have here and
  • 00:03:26 therefore I will generate the token upon
  • 00:03:29 login and I will return it here and the
  • 00:03:31 token will just be a string and I also
  • 00:03:34 want you have maybe the token expiration
  • 00:03:38 field let's say and that should describe
  • 00:03:40 when this token does expire and this
  • 00:03:43 could be the expiry time let's say in
  • 00:03:45 milliseconds or in seconds or in hours
  • 00:03:49 and I will go for hours here I will
  • 00:03:52 return an integer and this will be
  • 00:03:54 something like 1 for 1 hour 2 for 2
  • 00:03:56 hours whatever we set so this is my off
  • 00:03:58 data which I will return for logging in
  • 00:04:01 so off data is the expected response so
  • 00:04:05 with that I added this query to my
  • 00:04:06 schema but as you know this doesn't do
  • 00:04:08 anything on its own we need to add a
  • 00:04:10 fitting resolver so let's go to that off
  • 00:04:13 resolver file where I also create users
  • 00:04:16 and Dera will add login and you have to
  • 00:04:19 use the name login here because I used
  • 00:04:22 login as a name here to remember these
  • 00:04:24 names have to match so here I have login
  • 00:04:27 I'll use async/await hence I add the
  • 00:04:30 async keyword in front of this function
  • 00:04:32 there I will get some arguments by the
  • 00:04:35 way you can use object D structure in
  • 00:04:37 here too if you get some arguments and
  • 00:04:39 you know that will be email and password
  • 00:04:42 as I know it will be the case here you
  • 00:04:44 can of course use object D structuring
  • 00:04:46 here and you can use that anywhere in
  • 00:04:48 all the other resolvers too so here just
  • 00:04:50 to mix it up this new syntax with object
  • 00:04:55 D structuring and now in there I want to
  • 00:04:57 lock the user in now what
  • 00:04:58 does logging the user in mean what do we
  • 00:05:01 have to do there well we have to
  • 00:05:03 validate that the email and password
  • 00:05:05 combination is correct obviously and
  • 00:05:07 then we have to generate this token and
  • 00:05:09 return it to the user these are
  • 00:05:11 basically a two steps we have to do here
  • 00:05:13 so let's first of all find out whether
  • 00:05:15 that user exists in our database so I
  • 00:05:19 will create a new user a constant here
  • 00:05:21 which I can because I'm now not
  • 00:05:23 importing this from my merged file
  • 00:05:24 anymore I have no other user constant in
  • 00:05:27 this file so I can create it here and I
  • 00:05:29 will now use my user model which I am
  • 00:05:33 importing at the top here I will use
  • 00:05:36 that user model to find one user for
  • 00:05:39 that email we're getting as an argument
  • 00:05:42 now if I don't find a user here we
  • 00:05:46 already have the first problem so if not
  • 00:05:48 user then we know logging in won't be
  • 00:05:51 successful because that email doesn't
  • 00:05:53 exist so here since we don't have a user
  • 00:05:58 I will throw an error so old fro a new
  • 00:06:01 error user does not exist if we make it
  • 00:06:09 past as if check then we know okay there
  • 00:06:12 is a user so we can now validate the
  • 00:06:15 password as a next step for this we can
  • 00:06:17 use be tripped which we already import
  • 00:06:20 and in create user we use that to create
  • 00:06:24 a new hashed password with the hash
  • 00:06:26 function and now this password is stored
  • 00:06:29 as a hash in a database so comparing
  • 00:06:32 can't be done with a normal equality
  • 00:06:34 check because the incoming password will
  • 00:06:36 be plaintext the password stored in the
  • 00:06:38 database will be that hash so they won't
  • 00:06:40 equal but bcrypt has a function that
  • 00:06:43 will basically you could say generate a
  • 00:06:46 hash based on the incoming password and
  • 00:06:48 then compare the two hashes roughly a
  • 00:06:51 little bit different thing it does but
  • 00:06:53 that is how you can't think about this
  • 00:06:54 it has this compare function and there
  • 00:06:57 we pass our plain string as the first
  • 00:07:00 argument so I passed a password as the
  • 00:07:03 first argument the second argument is to
  • 00:07:05 hash to which you want to compare it and
  • 00:07:07 here this hash can be retrieved on that
  • 00:07:09 user object we found so there I know
  • 00:07:12 that user in the database of course will
  • 00:07:14 have a password field so I can now
  • 00:07:16 compare the stored password and the
  • 00:07:19 incoming password this is an async task
  • 00:07:22 so we have to await it and the result
  • 00:07:25 will be true or false depending on
  • 00:07:28 whoever they are equal or not so I will
  • 00:07:30 store this in a constant named is equal
  • 00:07:32 if this is not true whoops if is equal
  • 00:07:36 is not true then I know the user exists
  • 00:07:40 but the entered password is incorrect so
  • 00:07:42 here I will also throw an error where I
  • 00:07:45 will return password is incorrect and by
  • 00:07:49 the way of course you could always throw
  • 00:07:51 the same error message like invalid
  • 00:07:53 credentials to make sure that you don't
  • 00:07:57 give away any hints about why the
  • 00:07:59 validation failed so if it failed
  • 00:08:01 because the password is not set it's not
  • 00:08:03 right or if it failed because the user
  • 00:08:05 is not right here I do give this hint
  • 00:08:07 also for us to debug it but if you want
  • 00:08:10 to give no hints away simply use the
  • 00:08:12 same error message so if we make it
  • 00:08:15 after disick though then we know we have
  • 00:08:18 a user and the password is correct so
  • 00:08:21 now we want to create that token and for
  • 00:08:23 this I'll quit my development server and
  • 00:08:25 install a new package with NPM install –
  • 00:08:29 – safe and then the package name is
  • 00:08:31 jason web token one word no dashes no
  • 00:08:35 whitespace it's one word JSON web token
  • 00:08:38 this is a package which will help us
  • 00:08:40 generate JSON web token which is this
  • 00:08:43 token I want to return to the front-end
  • 00:08:44 so now here I can import this and I'll
  • 00:08:47 store it in a constant which I named JWT
  • 00:08:50 I will require JSON web token there and
  • 00:08:54 now I can use that in the log and
  • 00:08:56 function there JWT package has a sign
  • 00:09:00 method and this sign method now takes
  • 00:09:04 some configuration so we pass in an
  • 00:09:06 object we can put some data into that
  • 00:09:10 token which we can later retrieve from
  • 00:09:12 the token you don't have to but you can
  • 00:09:14 use the token to also store some data in
  • 00:09:16 it there will be some default data added
  • 00:09:18 by this package to secure the token and
  • 00:09:20 so on but you can also add your own data
  • 00:09:23 and I want to store let's say the user
  • 00:09:25 ID I
  • 00:09:26 get that from that user which I
  • 00:09:28 retrieved here and I can use the virtual
  • 00:09:31 ID gathered to get it as a string which
  • 00:09:33 is what I want to enclose the token I
  • 00:09:35 don't want to put my object ID in there
  • 00:09:37 and I also want to store the email let's
  • 00:09:40 say user email now this generates a
  • 00:09:44 token this is a synchronous task and we
  • 00:09:46 can store the result in a variable named
  • 00:09:48 token or in a constant however I also
  • 00:09:51 want to configure this a bit more the
  • 00:09:53 first argument is the data we want to
  • 00:09:55 put in a token the second argument is
  • 00:09:57 required this is a string which is used
  • 00:10:00 to to hash the token basically and this
  • 00:10:03 will later be required for validating it
  • 00:10:05 because this basically is your private
  • 00:10:07 key only someone who knows that key can
  • 00:10:11 validate this token and therefore it
  • 00:10:13 should be on your server and not be
  • 00:10:15 exposed to your clients and here I'll
  • 00:10:17 enter some super secret key you want to
  • 00:10:21 use a longer string there in production
  • 00:10:23 but this is now what I will use for
  • 00:10:24 hashing it and later for validating
  • 00:10:27 incoming tokens and any token that was
  • 00:10:29 not hashed with such a key is treated as
  • 00:10:31 invalid the third argument is optional
  • 00:10:35 but there we couldn't configure the
  • 00:10:37 token and here I'll set the expires in
  • 00:10:40 key to one hour we can set it like this
  • 00:10:42 and you could set it to two hours but I
  • 00:10:45 want to go with one hour it is a good
  • 00:10:47 practice to keep these tokens
  • 00:10:48 short-lived in case they get stolen it's
  • 00:10:52 not that easy to steal them they are
  • 00:10:53 stored in the clients browser but if
  • 00:10:55 someone gets access to that browser
  • 00:10:56 somehow then people could steal that
  • 00:10:59 token and since that token is the only
  • 00:11:01 thing you need to authenticate yourself
  • 00:11:04 we want to make it short lift so now we
  • 00:11:07 have that token and therefore at the end
  • 00:11:10 of this resolver here I will return an
  • 00:11:12 object which has to meet the
  • 00:11:14 requirements of my off data type here so
  • 00:11:17 off data has a user ID has the token and
  • 00:11:21 the token expiration which is an integer
  • 00:11:23 so let's return exactly that now so we
  • 00:11:26 returned a user ID to meet that first
  • 00:11:30 requirement here and that user ID can be
  • 00:11:33 retrieved from the user we got like this
  • 00:11:35 with that gather function then I
  • 00:11:38 returned the token of course
  • 00:11:40 and also the token expiration which I
  • 00:11:44 have in my schema so this field here I
  • 00:11:47 want to return that to and since I set
  • 00:11:49 it to one hour here
  • 00:11:50 I'll return one as an integer here and
  • 00:11:53 this is it this is my login resolver
  • 00:11:56 with that I got to login resolver and
  • 00:11:59 the schema setup let's now start this
  • 00:12:02 again and let's simply try it out so
  • 00:12:05 I'll go back to graphical and I'll write
  • 00:12:08 a new career or first of all we need to
  • 00:12:11 reload to get Auto completion login and
  • 00:12:15 then I will use some invalid credentials
  • 00:12:18 let's maybe start with tests at tests
  • 00:12:21 calm but I'll enter it invalid password
  • 00:12:23 this one is not the right password and I
  • 00:12:26 want to get all three fields token token
  • 00:12:29 expiration and user ID and if I hit
  • 00:12:32 enter I get this illegal arguments
  • 00:12:37 string on the find and that problem
  • 00:12:40 stems from my login resolver function
  • 00:12:44 obviously when I find one user we should
  • 00:12:47 use a wait here to write it's an
  • 00:12:49 asynchronous task and if I use async
  • 00:12:51 await I have to put a wait here where we
  • 00:12:54 used normal syntax with then and so on
  • 00:12:56 but using it without a wait well then we
  • 00:12:58 immediately continue to the next line
  • 00:13:00 and we try to access user password when
  • 00:13:03 the user hasn't been retrieved yet so
  • 00:13:05 with that fixed let's run this same
  • 00:13:08 query again and I get password is
  • 00:13:10 incorrect now let's say user email
  • 00:13:13 address which doesn't exist and I get
  • 00:13:14 user does not exist now let's use both
  • 00:13:18 correctly so an existing email address
  • 00:13:20 with the correct password and I get the
  • 00:13:22 user ID I get this token string and I
  • 00:13:26 get D token expiration and will later
  • 00:13:28 store all of that in the front-end so
  • 00:13:31 that we can send it to requests to
  • 00:13:33 authenticate ourselves now we got this
  • 00:13:36 token but this alone does not protect
  • 00:13:38 our other resources we can still send
  • 00:13:41 queries for all events and so on now if
  • 00:13:43 you want to lock down access to certain
  • 00:13:45 operations let's say to creating an
  • 00:13:47 event now it's time to also add an off
  • 00:13:50 middleware to make sure that we can
  • 00:13:52 validate incoming
  • 00:13:54 and see whether they have a valid token
  • 00:13:56 attached to them so that we can grant or
  • 00:13:59 deny access to our different end points
  • 00:14:01 based on that let's do that so I already
  • 00:14:05 mentioned it I want to use a middleware
  • 00:14:07 for this I want to use a function which
  • 00:14:08 basically can be added to any incoming
  • 00:14:13 request or which runs on any incoming
  • 00:14:15 request and which then gives me the
  • 00:14:18 information whether there is a valid
  • 00:14:19 token or not so that I can then block or
  • 00:14:22 allow access now let's first of all work
  • 00:14:25 on that middleware function I'll add a
  • 00:14:27 new folder here on the top level I'll
  • 00:14:29 name it middleware you can name it
  • 00:14:31 differently of course and in there we'll
  • 00:14:34 store all the middleware we will create
  • 00:14:35 manually I only need one for now and
  • 00:14:38 that ste is off mill where I'll name the
  • 00:14:41 file is off j/s you can also name this
  • 00:14:43 however you want and there I'll export a
  • 00:14:47 typical expressjs middleware function
  • 00:14:49 which gets the incoming requests gets
  • 00:14:51 the response object and gets this next
  • 00:14:54 function we can call to let the requests
  • 00:14:56 continue and then in this function I
  • 00:14:59 wanna have a look at the incoming
  • 00:15:01 requests at the header stare whether
  • 00:15:03 there is a token part of these headers
  • 00:15:05 and then validate you have a token and
  • 00:15:08 so on so let's first of all look into
  • 00:15:11 the incoming request headers for this
  • 00:15:13 I'll create a new constant and I'll name
  • 00:15:15 it off header and for my incoming
  • 00:15:18 requests I'll call the get method and
  • 00:15:20 look for the authorization field this
  • 00:15:24 sees if there is an authorization field
  • 00:15:26 in the incoming request now if this is
  • 00:15:29 not said if there is no off field then I
  • 00:15:33 know we are not at indicated we have no
  • 00:15:36 valid token but this does not
  • 00:15:38 automatically mean that this user should
  • 00:15:41 not have access to anything maybe we
  • 00:15:43 just want to block access to some of our
  • 00:15:45 resolver functions but not to all of
  • 00:15:48 them so for now I want to let the
  • 00:15:50 request to continue its journey through
  • 00:15:52 our API basically but I want to attach
  • 00:15:55 some information to the request so that
  • 00:15:57 everyone interested in the API every
  • 00:16:00 resole or function for example can check
  • 00:16:02 is this user authenticated who sent this
  • 00:16:04 request or not and I can do this by
  • 00:16:07 simple
  • 00:16:07 adding is off-field to the request and
  • 00:16:10 you can name this field whatever you
  • 00:16:12 want you should just make sure you don't
  • 00:16:14 overwrite an existing field
  • 00:16:15 it's off does not exist on the request
  • 00:16:18 though and I will set this to false and
  • 00:16:20 then I will call next and to avoid that
  • 00:16:24 any error code after this in this
  • 00:16:26 function gets executed I will return
  • 00:16:28 this next call
  • 00:16:30 so we then leave this function and the
  • 00:16:32 request continues with this extra
  • 00:16:34 metadata added if we make it past this
  • 00:16:36 check then we at least have an
  • 00:16:38 authorization header but this does not
  • 00:16:40 mean that there is a valid token in
  • 00:16:42 there so let's extract the token from
  • 00:16:45 that header for this and we'll access
  • 00:16:47 the header and I'll split it now I will
  • 00:16:50 expect that I get my token in the header
  • 00:16:55 which basically looks like this
  • 00:16:56 authorization and then bearer and then
  • 00:17:01 the token value and that bearer thing is
  • 00:17:04 really just there as a convention and
  • 00:17:06 this is how you do it on a lot of public
  • 00:17:08 API s and therefore I also want to
  • 00:17:09 implement this here this basically just
  • 00:17:12 signals here which type of
  • 00:17:14 authentication were using so I will have
  • 00:17:18 that format and therefore I will split
  • 00:17:20 my off header on the whitespace because
  • 00:17:23 the value in off header will not be
  • 00:17:27 authorization : Bearer and the token but
  • 00:17:32 instead it will just be the left side
  • 00:17:34 because we're getting your authorization
  • 00:17:36 header value with this line here so the
  • 00:17:39 value we have now is something like
  • 00:17:40 bearer and then a token so I now split
  • 00:17:43 here on this empty white space on this
  • 00:17:46 blank and since I split on that I get
  • 00:17:50 two values first value of the array
  • 00:17:53 should be bearer second value should be
  • 00:17:55 our token so now that I split I can
  • 00:17:57 access this second value with index 1 as
  • 00:18:01 you know in JavaScript and now this
  • 00:18:04 would be the token now obviously maybe
  • 00:18:06 this is not set maybe we don't have that
  • 00:18:09 so if not token if we have no token here
  • 00:18:13 or if token is equal to an empty string
  • 00:18:17 then I also will set request
  • 00:18:21 all falls and returned next if we make
  • 00:18:24 it past this check here though then we
  • 00:18:27 have something which could be a token
  • 00:18:29 which we should now verify to verify
  • 00:18:31 that we need that JSON web token package
  • 00:18:33 again so I'll store this in JWT and I'll
  • 00:18:36 require JSON web token like this and
  • 00:18:39 then here I can call JWT verify and pass
  • 00:18:46 in that token which we want to check and
  • 00:18:48 pass in that key you used before so that
  • 00:18:53 key you used for creating that token in
  • 00:18:56 my case that was some super secret key
  • 00:18:59 here in the off resolver let's use that
  • 00:19:01 here that is required for validating
  • 00:19:04 this because only tokens with that same
  • 00:19:07 key will be valid tokens and then what
  • 00:19:11 we get here is our decoded token now
  • 00:19:14 this actually could fail so I will wrap
  • 00:19:17 this in a try-catch block and I'll catch
  • 00:19:21 any errors I might get here and if I do
  • 00:19:24 get an error here I will actually set
  • 00:19:26 request is off to falls again and also
  • 00:19:29 return next so here I'll add decoded
  • 00:19:33 token as a variable outside of try-catch
  • 00:19:35 and then I'll assign a value in there
  • 00:19:38 and now if you make it past this
  • 00:19:41 try-catch block we know we have a
  • 00:19:43 decoded token which is then
  • 00:19:44 automatically a valid token because this
  • 00:19:46 only returns a decoded token if it is
  • 00:19:49 valid so now if you make it past this
  • 00:19:51 try-catch block we know we didn't get an
  • 00:19:54 error now let's also check if decoded
  • 00:19:56 token is really set because if it is not
  • 00:19:59 set that again I'll treat this as not
  • 00:20:02 authenticated but if we make it after
  • 00:20:05 this final check then we know we have a
  • 00:20:07 valid token and now we can something set
  • 00:20:11 is off on our request to true and
  • 00:20:14 additionally I want to add a user ID
  • 00:20:17 field and I want to get that from my
  • 00:20:20 decoded token because it really is a
  • 00:20:22 decoded token we can now look into the
  • 00:20:25 token and use the data we put in there
  • 00:20:26 and if you remember when we created the
  • 00:20:29 token I did store the user ID in there
  • 00:20:31 as my own custom value so now I can
  • 00:20:33 retrieve that here in the
  • 00:20:35 so I can store the user ID field or the
  • 00:20:38 value in that field in my request user
  • 00:20:41 ID field and then I call next so now we
  • 00:20:44 have this middleware which does a couple
  • 00:20:47 of checks to make sure that we have a
  • 00:20:49 valid token or not but it does never
  • 00:20:51 throw an error it just set some extra
  • 00:20:54 data on the request there is off
  • 00:20:56 property and if we are authenticated to
  • 00:20:59 user ID which will later help us fetch
  • 00:21:01 the user from the database and so on now
  • 00:21:04 with all that in place the question is
  • 00:21:07 how do we now use this middleware to
  • 00:21:08 protect our individual resolvers with
  • 00:21:11 the REST API this was simple there we
  • 00:21:14 had a bunch of routes and on every route
  • 00:21:16 we could add or not add this malware for
  • 00:21:20 a graph QL of course we only have one
  • 00:21:22 route we have this one endpoint we can
  • 00:21:24 add the middleware to that but then the
  • 00:21:26 entire endpoint is locked down or not
  • 00:21:28 that is the reason why I am NOT locking
  • 00:21:31 down on anything in this middle of where
  • 00:21:32 I'm never throwing an error I only set
  • 00:21:35 some metadata so with that since it
  • 00:21:37 always lets every request through but
  • 00:21:39 just adds the information whether that
  • 00:21:42 is an authenticated or an authenticated
  • 00:21:44 request since we have that information
  • 00:21:46 only I can run this malware
  • 00:21:49 on every incoming request so time for a
  • 00:21:52 general app use statement before we
  • 00:21:54 reach our graph QL API and here I'll
  • 00:21:57 import is off my own middleware from
  • 00:22:01 that middleware folder and there D is
  • 00:22:04 off file and I add it as a mail aware
  • 00:22:08 here just like this don't execute it
  • 00:22:10 just pass it as a function reference and
  • 00:22:12 Express will use this as a middleware
  • 00:22:15 since we have a valid expressed chance
  • 00:22:17 middleware function profile here so now
  • 00:22:20 this will run on every incoming request
  • 00:22:23 and since it will run on every incoming
  • 00:22:25 request we will have that request is off
  • 00:22:28 field on every request in every resolver
  • 00:22:32 so how can we now use that well let's
  • 00:22:35 say for creating an event we want to be
  • 00:22:38 authenticated for getting a list of all
  • 00:22:41 events we don't have to so for my events
  • 00:22:44 resolver I'll not change anything but
  • 00:22:46 for a create event I'll
  • 00:22:48 add a check right at the beginning I
  • 00:22:50 want to check whether we are
  • 00:22:51 authenticated or not now the cool thing
  • 00:22:54 here is this function does resolver
  • 00:22:57 function does not just receive arguments
  • 00:23:00 automatically but we also get a second
  • 00:23:02 argument there by default which is our
  • 00:23:04 request so here we get access to our
  • 00:23:07 request with that we can of course add a
  • 00:23:09 if check and see if request is off is
  • 00:23:12 true or if it is not true then I know
  • 00:23:17 for this rizal or function not for this
  • 00:23:20 one but for this one I don't want to
  • 00:23:22 grant access and therefore here it's now
  • 00:23:24 easy to throw a new error on
  • 00:23:28 authenticated something like this and
  • 00:23:31 since we throw an error the rest of the
  • 00:23:33 code will not execute so let's give this
  • 00:23:36 a try let's go back to graphical and
  • 00:23:39 reload and it doesn't matter if we now
  • 00:23:42 log in first or not because this token
  • 00:23:44 is not getting stored anywhere
  • 00:23:45 automatically so right now even if we
  • 00:23:47 have to token even if we copy it it'll
  • 00:23:51 not get used for the requests we send
  • 00:23:53 here I'll show you how to use it in a
  • 00:23:55 second for now let's simply send a
  • 00:23:57 mutation event here a request here
  • 00:24:00 create event with the event input and
  • 00:24:02 there let's add a quick title should not
  • 00:24:06 work description we are not
  • 00:24:10 authenticated let's add the price and
  • 00:24:17 let's add the date and this date here
  • 00:24:20 can be anything because will not make it
  • 00:24:24 into our resolver anyway so we'll not
  • 00:24:26 use that date let's fetch the idea let's
  • 00:24:29 hit enter and correctly we get
  • 00:24:31 unauthenticated now just to show that
  • 00:24:34 this is not the middleware but the
  • 00:24:36 metadata extracted by that middleware
  • 00:24:38 which is then used in the resolver let's
  • 00:24:40 also run a quick query for all events
  • 00:24:42 and that should still work because there
  • 00:24:45 I'm not checking whether we are
  • 00:24:47 authenticated or not and it does so now
  • 00:24:50 this is how we can protect our resolvers
  • 00:24:52 with this little check here and this
  • 00:24:55 request we get as an extra argument now
  • 00:24:58 of course we can copy that into all
  • 00:25:00 resolvers which we want to protect
  • 00:25:02 for example here in bookings um you
  • 00:25:05 should not be able to get a list of
  • 00:25:06 bookings if you're not authenticated
  • 00:25:08 events okay but bookings that should be
  • 00:25:10 restricted to authenticated users so
  • 00:25:13 let's add our ex I don't need that but I
  • 00:25:15 need the second argument the request
  • 00:25:17 here and then we can paste in our check
  • 00:25:19 the same of course for booking an event
  • 00:25:21 you should only be able to book an event
  • 00:25:23 if you are locked in so we get the RX we
  • 00:25:26 get the request and we add our logic
  • 00:25:29 here that's the second argument now
  • 00:25:31 cancel booking of course you should be
  • 00:25:33 logged in for that too so here we get
  • 00:25:35 the request and we check this
  • 00:25:38 additionally when we cancel a booking we
  • 00:25:41 probably also want to check whether the
  • 00:25:43 user who does try to cancel is the user
  • 00:25:45 who created that booking but that is
  • 00:25:48 some extra check we can add later for
  • 00:25:50 now adjustable at this a general
  • 00:25:51 authentication logic here so here I have
  • 00:25:55 this in place I have my bookings now all
  • 00:25:58 lockdown for authentication obviously
  • 00:26:01 creating a user and logging in should be
  • 00:26:03 possible without being authenticated
  • 00:26:05 events is what we already worked on and
  • 00:26:07 with that our API is secured the
  • 00:26:11 question now justice how can we now test
  • 00:26:14 whether this works for the protected
  • 00:26:17 hooks or for the protected resolvers
  • 00:26:19 because well we can query events here
  • 00:26:21 but that is it basically the problem is
  • 00:26:24 we can't attach headers in this
  • 00:26:26 graphical interface well first of all
  • 00:26:29 let's run a quick query here and let's
  • 00:26:32 um get D token again so enter some valid
  • 00:26:36 credentials which are stored in your
  • 00:26:38 database here whoops
  • 00:26:41 should of course retrieve the token and
  • 00:26:44 then copy that
  • 00:26:45 token now let's use a different way of
  • 00:26:49 sending requests let's use postman you
  • 00:26:53 can simply google for postman
  • 00:26:54 postman is a great tool for sending
  • 00:26:56 requests to REST API but we can always
  • 00:26:59 use it for a graph QL API you can simply
  • 00:27:02 download it run through the installer
  • 00:27:03 and once you have it installed and
  • 00:27:05 opened it it should look something like
  • 00:27:07 this now here you can enter a URL to
  • 00:27:11 which you want to send a request in our
  • 00:27:13 case that would be localhost
  • 00:27:15 3000 refus'd but a post request because
  • 00:27:21 now we don't want to want to access
  • 00:27:22 graphical we want to send a request to
  • 00:27:24 our graph QL API instead and now here we
  • 00:27:28 need to configure the body set this to
  • 00:27:31 binary at draw excuse me and choose
  • 00:27:33 Jason there and now how do you send data
  • 00:27:37 in this post body
  • 00:27:39 well you enter a JSON document here and
  • 00:27:42 the key the first key you need is the
  • 00:27:44 query key then you have a string and in
  • 00:27:47 there you put your query now this is way
  • 00:27:50 more inconvenient than with graphical
  • 00:27:52 because for example for the log inquiry
  • 00:27:54 you would enter it like this escape the
  • 00:28:00 quotation mark test at test comm escape
  • 00:28:04 this one – at the password escape tester
  • 00:28:09 escape close that open the curly braces
  • 00:28:14 get the token close that it all has to
  • 00:28:18 be in line close that double quotation
  • 00:28:20 mark and then hit Send and actually my
  • 00:28:24 bad we should add query in front of this
  • 00:28:27 here – yes this can look confusing here
  • 00:28:30 basically we describe what is the query
  • 00:28:32 you want to run and then we have queries
  • 00:28:34 and mutations so I should wrap this all
  • 00:28:37 with an extra pair of curly braces and
  • 00:28:40 now I get back this token and now we can
  • 00:28:43 copy that – of course so this is how we
  • 00:28:46 can send requests they're not too
  • 00:28:48 convenient but we'll still give it a try
  • 00:28:50 will soon add a front end and of course
  • 00:28:52 there is better tooling – but this is
  • 00:28:54 relatively easy for this simple request
  • 00:28:57 we'll have a better way later now of
  • 00:29:00 course I don't want to send a query for
  • 00:29:04 logging in instead I want to send a
  • 00:29:05 mutation and the mutation here will go
  • 00:29:09 to create event – that create event
  • 00:29:11 endpoint then I want to get back the ID
  • 00:29:13 and let's say the title of that created
  • 00:29:15 event and here I need to pass my event
  • 00:29:20 input curly braces and there I'll have
  • 00:29:24 my titled escape the double quotation
  • 00:29:28 marks
  • 00:29:29 title will be should work let's say then
  • 00:29:34 I have my description escape to double
  • 00:29:37 quotation marks again this now works
  • 00:29:42 let's add a price which is let's say
  • 00:29:46 39.99 and a date now before I fetch a
  • 00:29:51 date here with my good old trick I still
  • 00:29:53 have that token in my clipboard so I
  • 00:29:56 want to use that and we have to attach
  • 00:29:57 this as a header to the request and that
  • 00:29:59 is the one part we couldn't do with
  • 00:30:01 graphical so let's go to headers here in
  • 00:30:03 postmen and there let's add a new header
  • 00:30:07 which is named authorization and the
  • 00:30:10 value should be bearer white space and
  • 00:30:13 then your token like this and now we can
  • 00:30:16 go back to the body here I prepared a
  • 00:30:20 date already which I can fetch with that
  • 00:30:22 same trick I used earlier and this
  • 00:30:23 serious add that here just escaped these
  • 00:30:26 double quotation marks that's important
  • 00:30:28 with a backslash and let's give this a
  • 00:30:33 try right now sent as I get user is not
  • 00:30:36 to find which is better than not
  • 00:30:39 authenticated so it seems to work but
  • 00:30:42 let's see what is wrong user is not the
  • 00:30:44 find and indeed something I messed up
  • 00:30:47 during my refactoring and I will look
  • 00:30:50 there somehow
  • 00:30:50 here I'm using the user model here of
  • 00:30:53 course and therefore I should import it
  • 00:30:56 so let's import user by requiring that
  • 00:30:59 from models user like that now this
  • 00:31:04 should work down there and now let's
  • 00:31:07 save this and rerun our request this now
  • 00:31:11 looks better now there is one other
  • 00:31:13 thing we can fix right away though and
  • 00:31:15 the data something which is still in
  • 00:31:17 there deliberately in the past we
  • 00:31:19 hard-coded the ID of the user who
  • 00:31:22 created an event or we made a booking
  • 00:31:24 now of course we got that user ID as
  • 00:31:27 part of our middleware here we got
  • 00:31:29 request user ID I store this when we
  • 00:31:32 have an authenticated user so in places
  • 00:31:34 where I know that is off is true which I
  • 00:31:37 do know here when I create an event
  • 00:31:38 because I check it then whenever I use
  • 00:31:41 that idea
  • 00:31:42 I can just use request user ID of course
  • 00:31:45 to use the Real ID of the user instead
  • 00:31:48 of this hard-coded one so I'll do that
  • 00:31:50 here and then booking it's the same I
  • 00:31:52 check whether the user is authenticated
  • 00:31:54 and if the user is authenticated then
  • 00:31:56 here let's use that user ID we got so
  • 00:32:00 with that we are now taking advantage of
  • 00:32:03 our login functionality we are now using
  • 00:32:08 the token in the header to send requests
  • 00:32:11 to endpoints in our API which are
  • 00:32:14 protected and with dead we got a very
  • 00:32:17 good State
  • 00:32:20 will now add or start adding a front-end
  • 00:32:23 to this will also refine and tweak the
  • 00:32:26 backend add functionalities maybe detect
  • 00:32:28 some bugs but this is the current state
  • 00:32:31 with which I'm happy we got the core
  • 00:32:33 functionality set now it's time to move
  • 00:32:36 on and start building the front-end and
  • 00:32:38 then work on both front end and back end
  • 00:32:40 simultaneously so hopefully see you back
  • 00:32:43 in the next video bye