Intermediate Gatsby, v2 Protected Pages
Transcript from the "Protected Pages" Lesson
>> The next thing that I wanna dig into is, how can we use this information, these serverless function, and actually build out some protected routes? How do we create a section of our site where you need to be logged in to access it? And the way that we're gonna do that is we are going to create some new files.
[00:00:21] So let's jump in to our pages and create one in a folder called account, and we'll call this one login.js. So what we're doing with login.js is we are going to create a form, well, really just a button because what we're gonna do is let people call that serverless function.
[00:00:46] In a more complete service, you might have a username and password that people needed to use, but our goal here is to demonstrate, not to build something that we can ship to production. Then we're gonna use this navigate from Gatsby again, and that'll be if something goes wrong.
[00:01:04] If we are already logged in, we don't wanna stay on this page and so on and so forth. So, what I wanna do is I'm going to start, let's build out our component, so let's export default function LoginPage. And in this, we're gonna do a loginStatus and a setloginStatus, do React.useState, and we'll leave this one undefined.
[00:01:35] And I'm explicitly leaving it undefined because what I want to happen here is, we haven't gotten any information yet. So, what we can do then is let's return a button and that button is gonna say login. And what I wanna do is if we're logged in, so if you are logged in and that loginStatus is true.
[00:02:04] What I want to happen instead of actually seeing anything, is we should just immediately navigate. And I wanna navigate over to account, Oops, account/dashboard, which we'll have to create here in a second. And I'm gonna use a feature of the Gatsby API which is replace, and I'll show you why here in a second, and then we'll return null.
[00:02:29] So that if for whatever reason, we still have to return a component, it just doesn't null up. So this is not the most full-featured page, and it actually won't work the way that we want it to right now. But let's get this running in develop so that we can look at the navigation behavior, okay.
[00:02:53] So let's make sure we're logged out so that when we do get to this point, it doesn't immediately bounce us. And then let's go to account/login. So here is our account/login, and right now this button doesn't do anything, so when I click it, nothing happens. But if I wanna set my login to true, What I want to happen is it'll auto, yeah, that's what we want, is it should automatically redirect us.
[00:03:24] So I go to account/login and we immediately go to the dashboard, because we're already logged in, we don't need to see the login page. But if I go back, you see how it took me to the homepage. The reason that we want that replace is that if we don't do that, what happens is we basically cut off our users in the past.
[00:03:48] And what ends up happening is if I go to the homepage, and then from here I tried to go to the account/login page. And I go, I didn't wanna do that, let me go back home. Wait, why did that work? Maybe I need to refresh. Let me go to account/login, and then I tried to go back and it's apparently outsmarting me for some reason.
[00:04:14] What does happen when it's not replacing, I'm not sure why it's ignoring me on this, is that it will cause you to, you hit Login and then you go to dashboard. And then when you try to hit Back, you go to Login again, which redirects you to dashboard.
[00:04:28] So you put people in a dead end where they can't use the Back button anymore, and it's not the end of the world but it's an inconvenience. It's something that you don't want to force people to deal with if they don't have to. So what we are gonna do is use this replace attribute which causes the site to give us the behavior that we want, which is to bounce back and forth like that.
[00:04:53] So next, let's actually wire this thing up, so I want a function called async function login. And login is going to pull the status out of await, we're gonna fetch, and we wanna fetch api/login. So just write to that API that we wrote, and then we'll get it out as json.
[00:05:27] And we don't actually need this, I don't know why VS Code keeps trying to help. So if the status is not ok, then we know that the function failed because that function will always return ok. So if the status is not ok, we're gonna throw a new error, and we're just gonna put in the status because something definitely went weird.
[00:05:52] Otherwise, we will navigate to account/dashboard. All right, so what this will do is that'll let us do that navigation, and then this, I don't know why I put this up here, this needs to be, It needs to be in here. No, wait. I'm over thinking things. Okay, here we are, so we do want that to be out here.
[00:06:29] And then I also want the ability to check the loginSatus, and that's where this loginStatus is gonna be. So what we're actually gonna do is write when you login, we're going to check the login. But first, why don't we just go ahead and have this thing login? So, I'm gonna onClick and we will login, then I need to set this back to not do that, right.
[00:06:58] Yes, that's what we wanted. So we're pulling this out, honestly, we could put this wherever we wanted. We could put it in here. So I'm just rearranging deck chairs at that point. So let's go to our account/login page, it takes us here. And if I look at our cookies again, when I login, there's our cookie and we're now on the dashboard that we still need to build.
[00:07:22] So this is pretty dang cool, but if I go back to my login, you see how it shows us the login even though the cookie is set. So we need one more function that's gonna check for the presence of that cookie that we can run right at the get-go.
[00:07:38] So we're going to do an async function called checkLogin, and this one is going to take the setter from React. So when we have a login status, we wanna set loginStatus, we're gonna pass that right through here. And in it, we're gonna get the loggedIn, we'll default it to false in case something goes wrong with the call, and we will await another fetch call, To api/check-auth.
[00:08:15] And after that, we will get the response as json. And then, we can just set that loginStatus. So we'll set the loginStatus to whatever loggedIn is, so that's either gonna be true or false. And if it's true, we'll get redirected. So by saving this and refreshing the page, Nothing happens because we didn't actually call this yet.
[00:08:48] So [LAUGH] let's actually call this, let's add a helper function down here. We're gonna use React use effect. And as soon as the page loads which we signify with an empty array, we want to call checkLogin, and we'll pass in that setLoginStatus. So this should set up our loop, and there it goes.
[00:09:17] So now if I go to login, when I'm logged in, it'll work. And if I go to api/logout and then I go back to account login, oops, It takes me to the login. So that's a working login page, right? And so there's a decent amount going on here, but again, we are largely just writing React, we're not doing a lot of Gatsby here.
[00:09:43] The biggest thing that we're doing with Gatsby is these navigation calls. So because the serverless functions made it so straightforward to update that cookie and check that cookie value, we don't really have to do a whole lot to manage this auth. And again, that's sort of the power of this approach is we're able to very quickly stand up these serverless functions and get things built instead of having to think really hard about how to get them built.
[00:10:12] Also get clever about implementing it in the browser in a way that's secure. So next, let's go ahead and build the dashboard page. And this dashboard page is going to be fairly straightforward, I don't know that it's gonna have anything super special in it. It's actually gonna be an almost exact duplicate in terms of a lot of the functionality to the point that I might just copy paste it.
[00:10:43] Let's throw this over and just say, cuz we still wanna check the login, so if we were gonna have a bunch of dashboard pages, I would probably say let's try this out and put this into a custom hook or something. But given that we're only using it twice, I'm kinda okay with leaving it so that it's really clear what's going on.
[00:11:03] The difference is that we are going to instead of tracking the login, we're gonna track log out and call this function log out. And after we've logged out, we want to go to the login page, not the dashboard page. So let's call this one Dashboardpage. We will still set our loginStatus and setLoginStatus.
[00:11:35] We're still gonna check the login at instantiation. But with this one, instead, what we'll do is we'll check if it's false and we will head over to the login page instead. And if you get here, we wanna actually show you a little bit of a dashboard. So we're not gonna put too much on this dashboard, but what we can do is say h1, wow, look at all this secret stuff.
[00:12:06] And underneath it, we can show a button. Let's get that out of there, that in there, and this one we will logout. Okay, so this will be our dashboard, and so what should happen now is if I try to go to login, it should bounce me back to the dashboard.
[00:12:35] But now if I logout, cookie is gone and I go to the login page. If I try to go to the dashboard, bounce back to the login, right? So, we've got a pretty solid experience here of going around to these different pages, and we could wrap this in kinda require auth sort of thing.
[00:12:55] And then use multiple pages with a require auth component around them that just did this navigation form. This is the basic pattern that we're looking at when we get into authenticated routes. And the main difference is that when we check this auth, the logic that we run in this function, this is the logic that would need to be based on your service.
[00:13:21] So whatever service you're using, if you're using a JSON web token, you would send that as a header and then you would need to validate that against whatever service you're using to manage auth. Or if you're using cookies, you would need to make sure that whatever details you needed to set are set so that somebody can't spoof it.
[00:13:41] There are things that definitely need to be considered about authentication, but the mechanics of it are pretty similar everywhere that you'll go. You need some way to validate the information, usually that means calling out to an auth service. And once you get that confirmation or a signal that it's not a valid login, you can either bounce somebody to the login page, or you bounce them to where they're supposed to go.
[00:14:07] And just send back this like, yeah, I am logged in or you're not logged in, and then the app can kinda handle the rest of that. And honestly, we could make this even more, you could send a header that would be a redirect. So after somebody hits this function, they get redirected to where they should be.
[00:14:23] There's a lot of stuff that you can do, and there's no hard and fast rule here. But ultimately, it comes down to a pretty straight forward flow of saying, are they logged in, yes or no? And then on the client side, checking that and showing people or not showing people things based on that yes or no flag from your serverless function.
[00:14:49] And then the last thing that I wanna do is, what if somebody goes to just account directly? I don't wanna have to build an account page, so why don't we just add a client-only route in here? And this is actually something pretty cool about these client-only routes, is that this one's a catch all, they're fallback.
[00:15:13] So if dashboard exists, dashboard will still hit dashboard.js, but something that doesn't exist like the route or if I put in some nonsense, will still hit this page. So, I can in here, I'll import everything as React, from React and then we're gonna get navigate again. And then down here, we're gonna do a very lightweight component that effectively is just a redirect.
[00:15:44] So this is gonna redirect to the account dashboard. And I could add some auth detection and stuff and redirect to the right place, but because that's already there with the dashboard, I'm just gonna be okay with an additional redirect. So we want this to run as soon as somebody hits the page, and we will navigate to account/dashboard.
[00:16:10] And again, we wanna replace true so that they don't end up with a broken back button. And we'll just return nothing. So we want that navigation to happen immediately and it will return null, otherwise. So let's, Where did I mess up? Need to call it a function because it's function.
[00:16:38] There we go. Okay, so now I can hit, Any route will bounce me back to where I need to be. And, If I go to just the plain account route, it takes me to the dashboard. And so then from there we can go into the gatsby-config and say, all right, let's add the account to, Our nav here.
[00:17:05] And I don't have to go straight to login or dashboard, I can just go to account, and then when I hit that, it takes me where I need to go. So I can logout, go to Books, go to Login, right? And we have access to this whole kinda nice experience with a protected route.
[00:17:22] We've got our list of books, our list of authors. These are all pretty powerful, pretty nice setups, and good flows.