Coding

NodeJS / Express / MongoDB – Build a Shopping Cart – #7 Sign Up with Passport

  • 00:00:00 welcome back so in the last lecture we
  • 00:00:02 set up the CSRF protection which is a
  • 00:00:04 very important piece of our application
  • 00:00:07 but now I really want to create a new
  • 00:00:09 user so let's get started with that I'm
  • 00:00:13 in the project where I left the last
  • 00:00:15 time and I will handle all the user of
  • 00:00:18 indication so sign up and log in with
  • 00:00:21 another package called passport passport
  • 00:00:25 so I will install this here also if the
  • 00:00:28 save flag passport and I will need two
  • 00:00:32 other packages I also need a package too
  • 00:00:36 and crypt my password so I will use all
  • 00:00:40 with the safe flag bcrypt no chase for
  • 00:00:48 that and I also want to add a package to
  • 00:00:55 have flash messages and abled in my
  • 00:00:59 application which are basically messages
  • 00:01:02 stored in the session which are then or
  • 00:01:06 can be shown in the view and their after
  • 00:01:09 they are deleted from the session so
  • 00:01:10 like if you were watching my lateral
  • 00:01:12 videos you know it from the validation
  • 00:01:15 errors which are displayed in the view
  • 00:01:17 if we got any and well otherwise they
  • 00:01:19 are and so this possibility to attach
  • 00:01:23 messages to the request to render them
  • 00:01:26 in the view the package I'll use for
  • 00:01:28 this is the connect flash package and
  • 00:01:33 with that I get quite a lot of packages
  • 00:01:35 installed I'll go to my app J's file and
  • 00:01:39 work with these packages for example
  • 00:01:42 Passport I'll require this here this
  • 00:01:46 again is the package used for really
  • 00:01:49 user management user authentication
  • 00:01:51 login SIA and sign up and so on and also
  • 00:01:54 the flash package from connect flash and
  • 00:01:59 then in the middle where initialization
  • 00:02:03 process here after initializing the
  • 00:02:06 session here and the ordering does
  • 00:02:09 matter
  • 00:02:10 I'll initialize flash which
  • 00:02:13 needs the session to be initialized
  • 00:02:15 forest since user sessions so that's
  • 00:02:17 important and thereafter I will
  • 00:02:20 initialize passport with the initialize
  • 00:02:25 method and I will also set it to use
  • 00:02:31 sessions to store the user so there for
  • 00:02:35 this reason this also has to be after
  • 00:02:37 this session initialization and with
  • 00:02:40 that I'm good to go and use passport
  • 00:02:43 with these flash messages however this
  • 00:02:47 really is only the first step
  • 00:02:49 because passport doesn't ship with all
  • 00:02:53 functionality we need here out of the
  • 00:02:55 box box because it doesn't know which
  • 00:02:57 fields or user has email and password or
  • 00:03:00 username and password or whatever so we
  • 00:03:03 have to implement a strategy for
  • 00:03:06 passport to use now there are packages
  • 00:03:09 with predefined package predefined
  • 00:03:12 strategies where we then only have to
  • 00:03:15 adjust certain parameters or define how
  • 00:03:18 the user should be created but the
  • 00:03:20 general strategy has been laid out so I
  • 00:03:23 will use the local strategy in this
  • 00:03:26 application which basically means
  • 00:03:27 locally on this server that's where the
  • 00:03:30 name comes from stored the user so
  • 00:03:33 created with email password whatever you
  • 00:03:35 like but store it on this server other
  • 00:03:37 strategies could be Facebook so use
  • 00:03:40 facebook login or anything like that
  • 00:03:43 there are tons of strategies available
  • 00:03:45 and if you want to learn more about them
  • 00:03:48 simply google for passport strategies
  • 00:03:52 strategies and here in the documentation
  • 00:03:59 of passport you may go to strategies
  • 00:04:02 over here and there you see a bunch of
  • 00:04:04 strategies you may choose so as you can
  • 00:04:07 see there are a lot however I'm going
  • 00:04:11 with the local strategy as explained
  • 00:04:14 earlier so back in my project I will
  • 00:04:19 install it or on the NPM install save
  • 00:04:22 flag pass
  • 00:04:23 word local and this allows me to
  • 00:04:27 configure this strategy to my needs to
  • 00:04:29 configure this I will create a new
  • 00:04:31 folder in my project which I will name
  • 00:04:33 config and in this config folder I will
  • 00:04:36 create a passport JS file and this is
  • 00:04:39 the file where I want to set up my
  • 00:04:40 passport and the strategy I use for this
  • 00:04:44 I will first import passport here too
  • 00:04:47 and now here's an important thing to
  • 00:04:49 understand if I require passport here in
  • 00:04:53 this file and I pry relie set it up in
  • 00:04:56 this app chase file here of course
  • 00:04:59 currently I'm not requiring this config
  • 00:05:01 file anywhere but this will follow but
  • 00:05:03 if I you import passport in two
  • 00:05:06 different files the configuration I
  • 00:05:09 apply in one file for example here in
  • 00:05:11 this app.js file where I initialize it
  • 00:05:13 and so on will be available in the
  • 00:05:15 availab file so I'm not setting up two
  • 00:05:17 different instances or anything like
  • 00:05:19 this I'm working with the same one that
  • 00:05:21 is important to understand in passports
  • 00:05:23 case here so back in the config file I'm
  • 00:05:26 importing passport I also want to import
  • 00:05:29 my user model so I will require this
  • 00:05:35 models user and then I want to import
  • 00:05:42 this strategy just local strategy so I
  • 00:05:44 will require this passport local and
  • 00:05:47 then here the strategy object okay
  • 00:05:53 so this is the setup are the imports I
  • 00:05:56 need and with this I can configure
  • 00:05:59 passport now the first configuration I
  • 00:06:02 want to use here is the Syria lies user
  • 00:06:08 function which will basically tell
  • 00:06:13 passport how to store the user in the
  • 00:06:15 session so this takes a function
  • 00:06:21 with the user as an input and then the
  • 00:06:25 done callback which password will
  • 00:06:28 execute once it well is done and in here
  • 00:06:32 I will use I will simply return done
  • 00:06:36 null and then user ID which basically
  • 00:06:40 means whenever you want to store the
  • 00:06:42 user in your session serialize it by ID
  • 00:06:46 so use the ID of the user which of
  • 00:06:48 course can be retrieved I also need to
  • 00:06:51 define the opposite that I want to
  • 00:06:54 deserialize the user I'm missing an eye
  • 00:07:02 here here I'll also pass a function as
  • 00:07:05 an argument with the ID of the user
  • 00:07:07 which I know is the argument or the arm
  • 00:07:13 user property by which I'm serializing
  • 00:07:17 the user and then I can say user using
  • 00:07:20 the user model find by ID so in the
  • 00:07:25 Mongo database here using Mongoose then
  • 00:07:27 pass the ID which is stored in the
  • 00:07:29 session and then I have my normal
  • 00:07:31 callback where I either get an error or
  • 00:07:34 I find the user and I want to return
  • 00:07:38 both things here in the done function so
  • 00:07:42 the first argument when returning done
  • 00:07:45 is of course always the error case
  • 00:07:46 therefore I'm setting this to null here
  • 00:07:48 when I'm serializing the user and here
  • 00:07:51 I'm even returning the error in case I
  • 00:07:53 got one or well the user if this was
  • 00:07:56 done successfully so this allows
  • 00:07:58 passport to store my user in the session
  • 00:08:01 or store the ID in the session and
  • 00:08:04 retrieve the user whenever I needed
  • 00:08:06 through this stored ID so that's a key
  • 00:08:09 part but we're not really creating users
  • 00:08:12 yet right so let's do that for this I
  • 00:08:15 will create a new strategy and I will or
  • 00:08:18 a new middleware for this I will do this
  • 00:08:20 using the use method on passport and
  • 00:08:23 then I will name this local sign up so
  • 00:08:26 this will be my sign up strategy which I
  • 00:08:28 use when I want to create a new user I
  • 00:08:30 will create a new local strategy for
  • 00:08:33 that
  • 00:08:35 this local strategy here this
  • 00:08:37 constructor takes two arguments the
  • 00:08:39 first one is some configuration in form
  • 00:08:42 of a JavaScript object and the second
  • 00:08:45 one will be a callback so the
  • 00:08:49 configuration first I want you tell this
  • 00:08:53 local passport package that my user name
  • 00:08:58 field and these are of course keys this
  • 00:09:01 package expect it expects to get so by
  • 00:09:04 which I configure this package so that
  • 00:09:06 my username field is email in my case
  • 00:09:09 this could of course be username if you
  • 00:09:12 were using that but I'm using email and
  • 00:09:14 my password field is password I also
  • 00:09:21 want to pass the request to recall back
  • 00:09:25 so pass request to callback true which
  • 00:09:31 means that in my callback here I first
  • 00:09:34 get the request so that I can use it
  • 00:09:36 there too I also get the email and
  • 00:09:39 password entered and then this done
  • 00:09:43 function again which I can use in the
  • 00:09:45 end to tell password hey I'm done
  • 00:09:47 everything's working hopefully so in
  • 00:09:50 here I can then use my user model to
  • 00:09:55 find the user or basically with that I'm
  • 00:09:58 checking if the user already exists and
  • 00:10:00 I try to find the user by email by the
  • 00:10:06 email field which should be equal to
  • 00:10:09 this email provided and then here I'll
  • 00:10:12 have a callback with either an error
  • 00:10:15 error or the found user now if I got an
  • 00:10:20 error then I want to return done with
  • 00:10:24 that error so I'm not successful if I
  • 00:10:28 don't get an error but I find a user
  • 00:10:30 well then I'm successful with this query
  • 00:10:34 with this database query but it's not a
  • 00:10:36 good case because then I'm trying to
  • 00:10:38 create a user which already exists or at
  • 00:10:39 least with an email address which
  • 00:10:41 already exists so in this case I will
  • 00:10:43 also return done with no error since
  • 00:10:46 nothing went
  • 00:10:48 on here but of course also not with any
  • 00:10:53 retrieved object but instead with as
  • 00:10:56 aford argument a message where I say
  • 00:11:01 email is already in use this later be
  • 00:11:05 this flash message stored in the session
  • 00:11:08 which I can output in the view so here
  • 00:11:11 I'm by setting this to false here I'm
  • 00:11:14 not telling passport that this was
  • 00:11:18 successful I'm telling a dodn't that no
  • 00:11:20 error appeared but it's also not
  • 00:11:22 successful instead here is the reason
  • 00:11:25 why it was not successful this message
  • 00:11:27 so if we pass both checks so we didn't
  • 00:11:31 get an error and the user does not exist
  • 00:11:34 yet then I can create a new user by
  • 00:11:37 using my Mongoose model and I will set
  • 00:11:40 the email address of this user to email
  • 00:11:43 and the password – now here's the
  • 00:11:46 interesting question – password I could
  • 00:11:49 do this but that would not be encrypted
  • 00:11:51 certainly not what I want so this is
  • 00:11:55 where I come back to the part I was
  • 00:11:56 talking in an earlier video about in my
  • 00:11:59 user model I want to create some helper
  • 00:12:01 methods to allow me to easily encrypt
  • 00:12:04 the password for this I will first
  • 00:12:06 import bcrypt which is the package I
  • 00:12:09 installed a couple of minutes ago which
  • 00:12:13 allows me to easily hash my password and
  • 00:12:15 then I add some methods to my user
  • 00:12:18 schema using the methods object here and
  • 00:12:21 then I simply come up with some names so
  • 00:12:25 I will name my first one encrypt
  • 00:12:27 password and this will be a function
  • 00:12:32 where I expect to get a password and
  • 00:12:35 inside of this function I will simply
  • 00:12:38 return the hashed password using bcrypt
  • 00:12:42 than the synchronous hashing here and
  • 00:12:47 I'll pass the password into this
  • 00:12:49 generate the salt for this um hash so
  • 00:12:54 generate this synchronous two and I will
  • 00:12:57 use five rounds of salt creation here
  • 00:13:02 and null as the last argument and with
  • 00:13:04 that I'm creating a encrypted password
  • 00:13:07 and I'm returning this so I can use this
  • 00:13:09 encrypt password method to get and
  • 00:13:11 encrypt the password and I will also set
  • 00:13:13 up another method here so also at this
  • 00:13:16 two methods named valid password where I
  • 00:13:20 will also pass in a password and this of
  • 00:13:23 course the method with which I want to
  • 00:13:24 be able to check if a password matches
  • 00:13:26 the hashed password so for this I will
  • 00:13:30 also use bcrypt since I can't just
  • 00:13:33 rehash it and then compare both hashed
  • 00:13:35 password because they will differ that's
  • 00:13:38 the idea behind this hashing here but
  • 00:13:41 bcrypt can use the knowledge that knows
  • 00:13:45 T algorithm used and so on to check if
  • 00:13:48 the two technically different but still
  • 00:13:52 the same passwords are equal so then I
  • 00:13:56 can use to compare method here compares
  • 00:13:59 synchronously if this password matches
  • 00:14:03 this password and this year of course
  • 00:14:06 refers to the password of the current
  • 00:14:09 user on which we're running this so this
  • 00:14:12 refers to the user on which this valid
  • 00:14:14 password method is executed and you will
  • 00:14:17 see this later so with these two helper
  • 00:14:19 methods in place I'll go back to my new
  • 00:14:21 user creation and I will replace
  • 00:14:26 password here with new user and crypt
  • 00:14:30 password password
  • 00:14:36 I can then save this new user and I will
  • 00:14:40 provide my callback here and if I get an
  • 00:14:47 error then I want to return done with
  • 00:14:52 the error but otherwise I will return
  • 00:14:56 done now so no error and with the new
  • 00:15:00 user so that has been quite a lot of
  • 00:15:03 work but with this the user is finally
  • 00:15:07 created with the strategy I can create
  • 00:15:09 the user now the question that remains
  • 00:15:11 of course is how to apply the strategy
  • 00:15:13 so in my index.js file in the route here
  • 00:15:16 and the post route where I create this
  • 00:15:19 user I will no longer redirect to my
  • 00:15:23 route route to my slash route instead I
  • 00:15:28 will replace this whole function here
  • 00:15:30 with passport and then authenticate and
  • 00:15:35 this middleware which will take a word
  • 00:15:38 or request takes the strategy at chest
  • 00:15:41 you find local sign up of course this
  • 00:15:44 one year and then some configuration or
  • 00:15:47 JavaScript object where I can tell
  • 00:15:50 password password that I want to have a
  • 00:15:53 redirect in the success case so if we
  • 00:15:55 are signing up successfully then I want
  • 00:15:58 to redirect to let's say slash profile
  • 00:16:01 otherwise I want if the failure redirect
  • 00:16:05 I want to be redirected back to sign up
  • 00:16:08 and I also want to flash a message here
  • 00:16:14 so fade your flash true and this will
  • 00:16:16 flash the message I'm setting up here
  • 00:16:19 using this connect flash package I just
  • 00:16:22 installed a couple of minutes ago
  • 00:16:25 so if that that is all set up but how
  • 00:16:28 does password
  • 00:16:29 know this strategy because I'm setting
  • 00:16:31 it up in this password chess file in the
  • 00:16:33 config folder and I'm nowhere importing
  • 00:16:36 this folder the simple answer is
  • 00:16:40 password doesn't know it so currently
  • 00:16:44 this would not work
  • 00:16:46 back in the app chairs file I will write
  • 00:16:50 after initializing Mongoose therefore
  • 00:16:53 required this package so config password
  • 00:16:58 and I don't need to bind it to a
  • 00:17:00 variable I simply want to load it which
  • 00:17:02 will automatically run through this file
  • 00:17:05 and give me this configuration set it up
  • 00:17:07 so execute all these methods here and
  • 00:17:09 there for setup password of course I
  • 00:17:12 could copy all that code here and just
  • 00:17:14 dump it into my app chest well but that
  • 00:17:16 would quickly blue or bloat this this
  • 00:17:20 file and I don't want to use that there
  • 00:17:21 from outsourcing this in this file so if
  • 00:17:24 all that in place I will restart my
  • 00:17:27 server oh and I want to want do one
  • 00:17:30 thing before I do this I want you add
  • 00:17:34 the profile route here so that I can
  • 00:17:37 actually get redirected to it if I'm
  • 00:17:39 successful
  • 00:17:40 so router get profile and here I simply
  • 00:17:54 want to render this profile page and
  • 00:17:58 just to see that this works output user
  • 00:18:02 profile on that page so now for restart
  • 00:18:06 this I'm getting an error as I can see
  • 00:18:08 let's see what is this passport is not
  • 00:18:11 defined well that's certainly sad to
  • 00:18:15 hear maybe I should do this
  • 00:18:19 so passport require passport and also
  • 00:18:26 I'll reorganize my imports to have my
  • 00:18:28 own files or my own yeah imports you're
  • 00:18:32 below the packages so with that if I now
  • 00:18:36 restart my server this should hopefully
  • 00:18:38 work and let me reload go again to the
  • 00:18:45 signup page and I'll try this the
  • 00:18:48 problem of course is that I should
  • 00:18:50 redirect to users sign up and user
  • 00:18:54 profile here so restart this again
  • 00:18:59 so so user signup let's try this again
  • 00:19:05 and I get redirected redirect to user
  • 00:19:09 profile but I can got a note found error
  • 00:19:12 because this route here is false so if I
  • 00:19:16 do this and now I reload this would work
  • 00:19:18 so as you can see the sign of signup
  • 00:19:21 would have worked just a little routing
  • 00:19:23 error here and you can also verify this
  • 00:19:25 by going to your um
  • 00:19:28 Mongo pin folder here and run the Mongo
  • 00:19:38 shell client then I'm using my shopping
  • 00:19:41 database here and if I go to my users
  • 00:19:45 collection show all users you can see
  • 00:19:47 this newly created user I just created a
  • 00:19:49 couple of seconds ago so the user signup
  • 00:19:52 is working what's not working is if I go
  • 00:19:57 to my signup page again outputting
  • 00:20:00 validation errors so let me quickly add
  • 00:20:03 this here for this I'll go in my next s
  • 00:20:07 file to the get user signup or out here
  • 00:20:12 and I want to extract possible messages
  • 00:20:16 I would could have by using a request
  • 00:20:19 flash so possible reflash messages
  • 00:20:23 stored in my request through this flash
  • 00:20:25 package and then they would be stored
  • 00:20:29 under error if they come from Passport
  • 00:20:32 so the message I'm flashing here is not
  • 00:20:37 stored under message but in status is
  • 00:20:40 just telling passport that this is a
  • 00:20:41 message I want to pass but it is stored
  • 00:20:44 under error so I can retrieve this like
  • 00:20:47 this from this flash package and then I
  • 00:20:50 can also pass this to my view so
  • 00:20:53 messages should be messages here in my
  • 00:20:58 view I can then simply go here where I
  • 00:21:02 have my validation errors placeholder
  • 00:21:04 and enter handlebars here so little if
  • 00:21:09 statement check if we have
  • 00:21:14 masa Ches but how can I do that well
  • 00:21:17 back in the index.js file I also need to
  • 00:21:20 pass another field here I will name it
  • 00:21:24 has errors since in handlebars template
  • 00:21:26 that can only check on single properties
  • 00:21:29 I can't specify equality conditions for
  • 00:21:33 example so here I will pass has AZ
  • 00:21:36 errors if messages length is greater
  • 00:21:41 than zero otherwise we don't have any
  • 00:21:43 messages so we have no errors so if we
  • 00:21:46 do have errors I will go back to my
  • 00:21:48 setup page I check this by running if it
  • 00:21:51 has errors this newly created variable
  • 00:21:55 I'm passing to the View and then inside
  • 00:21:58 of this if condition I'll create using
  • 00:22:02 bootstrap a little dev here and inside
  • 00:22:06 of this div I want to loop through all
  • 00:22:09 my error messages I might have of course
  • 00:22:11 I will only have one here but I think
  • 00:22:13 you get my point
  • 00:22:14 and I simply output it here so with this
  • 00:22:22 if I restart my server reload the page
  • 00:22:26 here test which is a take an email
  • 00:22:29 address you see a mail is already in use
  • 00:22:32 so that's not happening that's taken
  • 00:22:34 care of
  • 00:22:35 however we still would be able to create
  • 00:22:37 a new user with an invalid email address
  • 00:22:39 like this one and I'll take care of this
  • 00:22:41 in the next lecture see you there
  • 00:22:44 bye