Hey guys, in this lesson we're going to be upgrading our authentication one more level. 1
And this is something that you can either have alongside your previous local strategy where you allow 2
people to authenticate using username and password, or email and password, 3
and you have this alongside, or as some modern websites do these days, they don't actually even allow 4
people to register using username and password or make it really, really small so that people mostly 5
go for the social logins. 6
Now what exactly is OAuth? 7
You might have heard of this term, but all it is, it's simply an Open Standard for Token-based Authorization. 8
Now what did all of that mean? 9
Well let me explain. 10
So you've heard of Facebook, right? 11
But let's say that we're building a different web app. 12
Let's say we're building a social network that's going to be the new Facebook, and it's going to be 13
called Bracebook. 14
And Bracebook is an awesome social network for people who have braces. 15
Now, when you're a new user who's signing up for bracebook, you probably won't have any friends in 16
the beginning, right? 17
And nobody likes a social network where you have no social connections and nobody wants to feel like 18
they have no friends, 19
so what can we do as the developers of bracebook to make this process of joining our social network 20
a little bit easier? 21
Well, we can ask the user for permission to access their Facebook account and see which friends they 22
have on Facebook are already users of our service, Bracebook. 23
So now once the user signs up, they'll already see that three of their friends are already on Bracebook 24
and they're ready to get started in a new life of socializing with their B braced friends. 25
Now, how would this work exactly? 26
Well, on our login page, we can ask the user to sign in, either manually where they don't get the benefit 27
of instantly connecting with their friends who have braces, or we can get them to log in with Facebook. 28
And in this case, what would happen is we would make a GET request to Facebook asking them for this 29
user's friends on Facebook and Facebook would return with a POST request with that list of users and 30
emails. 31
So this might be a really simplified version of that. 32
It's just a straight up table of this particular user's friends on Facebook and all of their emails. 33
So then this gets passed over to our server, where we can compare it against our database of users 34
and see whether, if we have any matching users who have the same email addresses. 35
So now, what we know that for this user, we have three friends of theirs who are already on Bracebook, and 36
we'll be able to automatically add them as a friend onto Bracebook. 37
And it's also a similar scenario if you are signing up to LinkedIn, 38
for example, if you couldn't be bothered to type in all of your contacts email addresses to find on 39
LinkedIn and add them individually, then what LinkedIn might do is it asks you to log in via Google, 40
and it will look through all of your contacts on Gmail in order to add them automatically to LinkedIn. 41
So by using OAuth, we're able to access pieces of information on these third party websites, such 42
as their friends emails or their contacts on Gmail, 43
but in our case, because we're talking about authentication and leveling up the security of our authentication, 44
another really big benefit involves delegating the task of managing passwords securely to these companies 45
like Facebook and Google. 46
As we mentioned before, every day or so yet another company gets hacked, and these companies tend 47
to be kind of low tech companies, companies that are not Facebook, Google, Amazon who are really 48
known for having great engineers, great teams who are able to implement all of these levels of security 49
for the authentication on their website. 50
And they probably have thought about hashing and salting passwords or other data that they're storing. 51
But there's more, 52
there are things such as not only salting the passwords but also peppering the passwords. 53
Some companies also encrypt the entire database containing the hashed passwords, and have a wide array 54
of complex mathematical solutions to keep their user passwords under lock and key. 55
Now, as a web developer, we could implement all of those things that I just spoke about and address 56
all of the edge cases, but it would take us a lot of time and a lot of development hours. 57
So, 58
why not simply delegate this task to a large company like Facebook and Google? 59
So then every time we log in our user, we simply ask them to log in on Facebook. 60
And Facebook will then authenticate them using their own secure methods. 61
And once that's done, Facebook sends us back a message saying, "This user is authenticated. 62
They are a real Facebook user, and they have got their correct username and passwords, 63
so go ahead and authenticate them as well." 64
And that will make our lives a lot easier. 65
We'll have a lot less liability. 66
And this is the way that you're seeing a lot of websites going where they only have third-party login, 67
like login with Twitter, Facebook, Google, GitHub. 68
But in order for us to be able to do all of this, we need to learn about OAuth. 69
That is the glue that binds all of this together and makes it actually tick. 70
Now, what exactly is special about OAuth? 71
Because there's a lot of other open standards that does something similar to this, but OAuth is quite 72
special in three ways. 73
The first way is that it allows you to grant a Granular Level of Access. 74
What does that mean? 75
That means that when your user logs in with Facebook, you can request specific things from their Facebook 76
account. 77
So you could say that for my app, I only really need their profile and email address. 78
But say if you were Tinder and you wanted to know who all their friends were so you don't accidentally 79
match them up, then you might want to know also their list of friends. 80
So this is what I mean by granularity. 81
The app developer can determine what kind of data do they need from the user's Facebook account and 82
request those accordingly. 83
Now, the second thing about OAuth is it allows for either read only or read and write access. 84
So in the case of Facebook, this means that you can either ask them to just retrieve pieces of information 85
about their Facebook account. 86
So what's their name? 87
What's their email? 88
Who are their friends? 89
Or you can ask for write access as well. 90
Say for example, in this case, if WordPress wanted to be able to post to Facebook on this user's account, 91
then they would need to ask for, read and write access. 92
And the third thing is that the third party that you're using to authenticate your users should be able 93
to revoke access at any point on their website. 94
So that means if you're authenticating with Facebook, the user should be able to go into their Facebook 95
account and de-authorize the access that they granted to your website, say, Bracebook, and they don't 96
actually have to go on to your website where maybe you're less keen to give up this access. 97
So now that we've looked at what's special about OAuth, the next thing to talk about is, well, how 98
does it actually work in reality? 99
So the first step is to actually tell this third party, be it Facebook, Twitter, or Google, about 100
our web application because they don't know about us. 101
Right? 102
So we have to set up our app in their developer console, and in return we get what's called an app 103
ID or a client ID, and we or our website is then the client, which will make the request to Facebook 104
to authenticate our user. 105
Now, once you've set up your app, the next step happens when the user tries to log on to your website. 106
So when the user hits up Bracebook.com and they want to authenticate, we give them an option to log 107
in with Facebook. 108
So once they click on that option, then we'll take them to the actual Facebook website so that they 109
are seeing a familiar interface, a trustworthy interface, and they will log in to Facebook using their 110
Facebook credentials. 111
And without OAuth, what we would have to do is to ask the user, "Hey, what's your login credentials 112
for Facebook? 113
Can you give me your Facebook password?" 114
Nobody wants to do that. 115
That seems super sketchy and really insecure. 116
So OAuth makes that step a lot easier because it gets them to log in on the website that they actually 117
trust, which is Facebook, right? 118
Something that they've been using for a long time now. 119
Once the user logs in on this third party, then they have to review the permissions that our website 120
is asking for. 121
So for example, you might want your profile and email address and they review that and they're like 122
okay I'll grant that permission. 123
So now that they've granted the permission and they've successfully logged in on Facebook, then our 124
website will receive an authorization code from Facebook. 125
And this allows us to check to make sure that the user actually successfully signed on to Facebook. 126
They had the right username and password. 127
So we're now 128
able to authenticate them and log them on to our website. 129
But if we wanted to go a step further, we can also exchange our authentication code for an Access Token. 130
And when we receive that Access Token from Facebook, we would save it into our database because this 131
is the token that we can use if we want to request for pieces of information, 132
subsequently. This access token is valid for a lot longer than the authentication token. 133
So the way you should see it is that the authentication code or the Auth Code is kind of like a ticket. 134
Right? A ticket that you're going to use once to enter the cinema, but the access token is kind of more 135
like a year pass. 136
And it comes with benefits like backstage access, where you get to request pieces of data from Facebook, 137
including their friend list or their username or their password, whatever it may be that they granted 138
you permission to access. 139
So the Auth Code is what we need to authenticate the user that they successfully managed the login through 140
Facebook and the access token is what we'll use to access pieces of information that are stored on that 141
user's account on this third party's website, for example, their email or their friends list. 142
All right, so enough theory, let's get started implementing it and add in Login with Google to our 143
website. 144
Now, hopefully you haven't skipped over the previous text lesson where we showed you how to set up 145
the Google OAuth credentials. 146
You'll need those 147
and it's very, very important that you follow it step by step and get it to work. 148
Hopefully, if you have completed that though, you will have filled out your .env file and you would have 149
replaced the placeholders in the Google Client ID and Google Client Secret sections. 150
And once you've done that, then we're ready to go into our index.js and start writing up some code. 151
Now, hopefully, you've already run "npm install" on this current project, and all of the modules are 152
installed. 153
Now, the only new module that we've added into this project in this upgrade is the "passport-google-oauth2" 154
. 155
And this is going to allow us to create the Google login strategy. 156
So we can import the GoogleStrategy from the "passport-google-oauth2" module. 157
And we can go ahead and configure it just below our previous passport.use(). 158
So this is our local strategy, 159
and just below it we're going to say passport.use(). 160
And this is going to set up our passport authentication middleware. 161
And this one is going to be a Google-based strategy. 162
We're going to first give the strategy a name which we'll call, "google", 163
and then we can go ahead and create the actual asynchronous function. 164
And then we can go ahead and set up our "new GoogleStrategy". 165
The first thing in this strategy is a set of options, 166
and that includes the Client ID, which is going to be set from the process.env.GOOGLE_CLIENT_ID 167
. 168
Let me just double check that I've spelled that correct. 169
Yep I think that's pretty much the same. 170
I'm going to copy the next line so I don't have to type it out. 171
And the next one is called the "ClientSecret". 172
Now the spelling of all these things matter a huge deal, 173
So try to make sure that you haven't got anything that you've made a typo here on, 174
and if in doubt, you can always check the solution file for exactly how it's meant to be spelled. 175
And then we have our "callbackURL", which in our case, if you remember previously we had set to 176
http://localhost:3000... 177
And then it's .../auth/google/secrets. 178
And then finally we need to add something called the "userProfileURL". 179
And this is actually found right at the bottom of the file. 180
And this is the one that we're going to use to be able to get hold of the user's profile information. 181
And there you have it. 182
We've now set up all of the settings, 183
so let's add a comma. 184
And now let's add the callback function which gets triggered once the process succeeds, 185
so once they actually manage to log in with their Google account. There's something called an "accessToken" 186
which we don't actually need to use. 187
So also something called a "refreshToken" which we don't need to use. 188
It's something that can help us keep that user signed in or track their session as we go along. 189
And then we have something called the "profile". 190
And finally, that thing that we use a lot in Passport, which is the "callback". 191
Now we are in the same section as what we've got here. 192
And we can now get hold of parts of the Google profile. 193
And for now, what I'm going to do is I'm just going to console.log() that profile to show you what 194
it contains. 195
And we're going to try and get this login page to trigger. 196
So for that to happen, we of course need a GET route to do that. 197
So just below the end of the other GET routes, I'm going to write a new app.get(). 198
It's going to be hit up when the user clicks on the new "Sign up with Google" and "Sign in with Google" 199
buttons in our login and register.ejs so it hits up /auth/google. 200
So it's the same for register and login. 201
We want them to both hit up the same route. 202
And then we're going to use the Passport middleware. 203
So passport.authenticate. 204
And then we're going to specify the strategy we want to authenticate with. 205
And remember we set up the authentication down here to be called the GoogleStrategy. 206
So we're going to use that right here. 207
So passport.authenticate... 208
The strategy we want to use is the "google" strategy. 209
And then once that's done then we can go ahead and add in some options. 210
We're going to say what are the things that we need to get from this login. 211
And it's going to be the "scope". 212
So this is an array. 213
We want their profile and we want their email. 214
So adding both of these things in will mean that when the user logs in we'll tell them, "We're going to 215
try and grab hold of your public profile and your email. 216
Hope that's okay?" 217
If they consent and they log in, then we should be able to receive it down here. 218
And it should go into this strategy because it's called "google" strategy 219
and we specified it here. 220
This is a good point to note that you probably noticed previously, that when we authenticated on the login, 221
we use the passport local authentication method. 222
And this is an optional thing, 223
if you only have one strategy, you don't actually have to name it because they're all the same. 224
But in our case, because we now have two strategies, it's a good idea to add the word "local" for our 225
local strategy just before the new strategy gets created, and "google" for our Google strategy, so that 226
when you run the middleware and you say passport.authenticate, you can specify which strategy the 227
code should go into, should it go into the "google" strategy, which is here, which is going to console.log() 228
the profile, or should it go into the "local" strategy which is going to console.log() the username 229
from the form submission and you'll be able to see this is how these different blocks of code actually 230
gets triggered. 231
And this is kind of behind the scenes how Passport works. 232
Let's give this a go. 233
And hopefully it works. 234
Let's go ahead and start up our server. 235
And go to our Register screen and click on this new button, 236
"Sign up with Google". 237
And now I've got the classic Google sign in screen, which I thankfully didn't have to code up. 238
This all comes from Google, and then I'm going to click to the account that I have a Google sign in 239
with, and I'm going to go ahead and sign in. 240
Now notice it says that Google hasn't verified this app. 241
Now, app verification can take anywhere between one month and three months. 242
So we're just doing tutorial, 243
we're keeping it in localhost, 244
it doesn't actually matter for us in this case. 245
If you're developing a production app, then you'll need to wait for that process and submit it on that 246
console platform that I showed you in the previous text lesson. 247
So in our case, what we're going to do is we're going to click on continue. 248
And if you accidentally clicked on the right button you can always go back and just click on it again. 249
So now it's going to say that signing in to this application called Secrets. 250
And, "By continuing Google will share your name, email address..." and some other profile things with this application. 251
We built the application, 252
we are receiving the information, 253
so in our case it doesn't really matter. 254
We're just doing some testing. 255
Let's click Continue. 256
And now it's trying to make a GET request to this location which we haven't written in our server yet. 257
So let's go ahead and do that. 258
Let's go back to our code and below the previous app.get(), 259
let's add "/auth/google/secrets". 260
And in this case, we're going to run passport.authenticate using the "google" strategy. 261
And we're going to add some options, 262
just like what we've got down here, 263
The successRedirect and the failureRedirect. 264
So successRedirect should similarly, as login, go to secrets, 265
and the failureRedirect should go also in the same vein as the code below, go to login. 266
Let's save that code and let's try it again. 267
Let's go back to our Register page. 268
Sign up with Google. 269
Click on the account. 270
Continue. 271
Continue. 272
And now, if we take a look inside our terminal, you'll see that that person's profile has now been 273
printed into our console. 274
So includes things like their picture, their name, their email, and a whole bunch of things that 275
they allowed you to have access to, which we can then save in our application. 276
That's the next part of the code that we're going to do. 277
What happens when we end up inside this callback? 278
How can we save their data into our database and be able to identify them in our system? 279
First we're going to add a try catch block as we have previously done. 280
And then we're going to use an await to make a db.query, 281
and we're going to use the command, "SELECT * FROM users WHERE email = $1". 282
And the email is going to come from that profile that we printed out just now, 283
So it's "profile.email". 284
And just to verify you can see that we printed out the profile, 285
and indeed inside the profile, there is a property called "email" which is what we need to save into our 286
server. 287
So the first thing we want to check is to create a result that checks to see if that email already exists 288
in our database. 289
Now we can use an if statement to check if (result.rows.length === 0)... in which case 290
that means we don't have any users by that email in our system, 291
then we're going to create a newUser. 292
So we're going to again, use await db.query, 293
and the query in this case is going to be an Insert query. 294
So we're going to, "INSERT INTO users..." table, 295
and we're going to add to the two columns email and password. 296
And the values that we're going to add, are going to be $1 and $2. 297
And let's add that at the end. 298
So dollar one is the email which is easy. 299
It's profile.email. 300
Now what you want to set as their password depends on you. 301
So some people, because there is no password we don't get the user's password from Google, 302
and that's the whole point of it, 303
we don't have to store it. 304
So some people store their user ID to identify them. 305
Some people store a word such as "google", just so that when you look through the database, you know 306
why it is they don't have a suitable password is because they are a Google sign in user. 307
So it really depends. 308
In my case, I'm going to set it as google to be able to differentiate them from the local login users 309
and to make sure that they don't 310
then try to log in with the same email that they logged in using the Google login. 311
But it really is something that is up to you individually. 312
Now that we've created the new user, we can now finally use that callback and we're going to pass null 313
for no errors, 314
and then we're going to pass the new user into that callback, so that when we serialize and deserialize 315
that user, we can then access it in the same location when we go to the secrets. 316
So that req.user() gets populated with the details that we add in from this newUser that we 317
created. 318
Remember, with the local login strategy, we get hold of the user that's stored in our database and 319
add it to the session, 320
and in our Google strategy, we get hold of the new user that we've just added. 321
And because this actually is the result from this query, you'll remember that it's not just the result, 322
it's actually .rows[0] (at position zero). 323
Next we're going to add an else statement, 324
and this else statement is in the case where we already have the existing user. 325
And that's because this is if result === 0, then that means that user is new and we add them in, 326
but otherwise that means the user already exists. 327
And we can tap into the result in order to add to the callback. 328
So we don't force them to log in again. 329
We also say there's no error and we simply tap into result.rows[0], and we grab the zeroth one as 330
the user that we register. 331
So now finally, we can use our catch to catch any errors. 332
And we can use a callback to pass that error through. 333
And that is all we need to do in order to set up our Google strategy. 334
Now, there's quite a few moving parts in this, so I would not be surprised if you made a typo or a 335
mistake or order or something is wrong. 336
So what 337
I recommend you to do is to use the VS Code compare, just to make sure that you haven't made any typos 338
or any issues. 339
So you can do this by right clicking on the current file that you're writing. 340
So your index.js and select this line, Select for Compare. 341
This is your file. 342
And then I want you to right-click on solution.js and select the other one, 343
Compare with Selected. 344
I know it's confusing, but just listen to me a few more times and you'll probably understand what's 345
going on. 346
But this way we've got our index.js on the left side, 347
the solution.js on the right side, and you'll be able to see any of the differences between your 348
file and the solution file. 349
So here are some things that might be different. 350
If in your file there's something extra, then you'll see it show up as red, meaning this is deleted 351
in the solution file. 352
Or if there is something extra on the solution file, you'll see it as green, which means this is something 353
that's extra compared to your file. 354
Now, some of these things might just be an order kind of thing. 355
For example, if I change the order here to here, you'll see it show up as red. 356
Here line ten is deleted, but line four is added. 357
And you can read through this just to see what the differences are and be able to understand what's 358
actually going on. 359
And the most important part is of course this app.use() GoogleStrategy. 360
So check to see if you have... there shouldn't really be anything that's different compared to the solution 361
code in your case, because we're using process.env, 362
so we're not taking into account any of these unique aspects 363
that's in your environment variable file. 364
So hopefully you can match up and find all the issues. 365
This is a really neat way that I hope will help you, but otherwise we are ready to test things out. 366
Let's hit Save and hopefully Nodemon is still listening 367
changes and updating things. 368
Yep. 369
So let's go to our server and let's go to Register, Sign up with Google. 370
And if we check our database and update it you'll see the new user has been added in. 371
So this is the email and this is the password that we set for them. 372
And now if I go into secrets and I refresh it or I close down the browser and reopen the browser, you'll 373
see it's still cached my session even though I'm logging in with a Google login. 374
Now, the final thing we want to do before we finish is we want to make this Log Out button work, because 375
otherwise we can't test our other login methods, 376
but also it's pretty useful for our regular users. In Passport 377
the log out method is really simple. 378
You'll see if you go into the secrets page, which is where the log out button exists. 379
It goes to "/logout". 380
So this will be a GET request that we can create in our server. 381
Going to the end of all of the other get requests, 382
let's go ahead and create another app.get(). 383
And we're going to say, "/logout". 384
And here we're going to set up our request response (req, res) callback. 385
And we're going to tap into the request object, 386
and there's something in there that's actually called "logout". 387
Now it's important that you choose this one which is all lowercase rather than this one. 388
And then we can go ahead and add our callback. 389
So error (err), 390
and inside here we're going to check if there is an error, 391
In which case we will simply just console.log() the error. 392
And otherwise, we're going to res.redirect() "/" 393
So the home page is where we're going to go after we log out. 394
If you've never seen this syntax before this is simply just a one liner. 395
So we've got an if, we've got the condition, and then we've got what should happen 396
if that's true, all in one line. 397
And let's go back to our secrets page. 398
Let's make sure that we can actually sign in. 399
And now if we click Log Out, it takes us to the home page, 400
and if we try to go to secrets again it's not going to let us. 401
So there you have it. 402
We've got Login with email and password, 403
we've got login with Google, 404
and we now have Log Out. 405
All that's left to do, is for you to be able to show a secret for each user, and for each user to be 406
able to add their own secrets, and that will complete this project. 407
So for all of that and more, I'll see you on the next lesson.