Protect Routes with Auth0:26
user. But now, let's make that an actual thing. So if we have a look here, let's get rid of the comments. We have all of this here, users, create a User, host a User, settings, logout. Really, all of that should assume a logged in User. So let's say route, middleware. We want the auth middleware. And we're going to group that for all of these routes within it. So I'll paste that there and reformat. Okay.
Create Login Route0:52
So I'll paste that there and reformat. Okay. So now every single route here will inherit the auth middleware, which means if I come back and I refresh the settings page, we get an issue, but it is actually working. So it's trying to redirect me to a login route, but we haven't yet created that. Okay, let's do that now. We can do it right at the top. We'll say, listen to a get request to login, and that's going to load some new controller called LoginController. And it's trying to create action there, but it's not quite enough.
called LoginController. And it's trying to create action there, but it's not quite enough. You'll see right here, it's trying to find a route with a name of login. So not a URI, but a name. So let's make sure we give this a name of login. So basically what's happening here is in Laravel's auth middleware, on the condition that you are not authenticated, it tries to link you to this route with a name of login. And up until this point, that route didn't exist, which is why we saw that error or exception. Okay, so let's switch back and go ahead and create this controller, php artisan make:controller LoginController.
Okay, so let's switch back and go ahead and create this controller, php artisan make:controller LoginController. All right, but you know what's come to think of it, why don't we instead place this within an auth directory? So we'll add that here and move it over. And then of course, I just need to update the namespace, like so, and then reimport that. Okay, so we said we need a method called create, and we'll say, please log in just to make sure it's working. Okay, only remaining step is to go back to our routes file and officially import it at
sure it's working. Okay, only remaining step is to go back to our routes file and officially import it at the top. All right, cross your fingers, come back, give it a refresh. And sure enough, it redirects us to the login page because we're not authenticated. All right, let's get started. Now you will see that I'm using a controller here. But up until this point, we've been using route closures, which is perfectly fine, especially for small projects. But yeah, for most of the things I build, I usually will reach for controllers across
Render Login Page2:51
for small projects. But yeah, for most of the things I build, I usually will reach for controllers across the board. So maybe at some point near the end of this series, I will quickly migrate all of these. But yeah, from now on, I want to start moving toward the way I would personally construct these things. Okay, so let's go into our LoginController. So let's return, and we'll ask Inertia to render a view called AuthLogin, and give that a reformat. All right, let's create that now.
a reformat. All right, let's create that now. So in my resources directory, I'm going to create a new view component in the auth directory called login. Okay, once again, just to make sure it's working. Okay, so come back, we give this a refresh. And there we go. But now notice one thing, we see the navigation bar here that really assumes you're already signed in. So for example, welcome back JohnDoe, and then users and settings.
Disable Layout on Login4:15
But yeah, it sounds like I want to turn that off. So we may have to tweak this just a little bit. Let's go back to our login view. And here, I'm going to set layout to null. Okay, so now if I come back to Firefox, we have an issue here. Even though I tried to effectively disable it, it's still being applied. And again, that's because of the way I set this up here. What we have here basically means if layout is null, then just set it to this layout component. So in our case, layout was null, so it set it to the default component. Okay, why don't we tweak this just a little bit?
So in our case, layout was null, so it set it to the default component. Okay, why don't we tweak this just a little bit? This is no longer quite right. Let's say if page.layout is undefined, only on that condition do we automatically set it. But if it's something else, or if it's null, then we should leave it alone. Okay, now if we come back and give it a refresh, I think that's going to work. Yeah, now we've disabled it entirely, but yeah, if we were to remove this, it should still be applied. Okay, that's what we want.
And then finally down here, and if we're using the options API, we would do props like this, or just again, to keep it a little consistent, we've been using the composition API. So I'm going to do script setup here, and then let's create our form. And you'll remember, we need to use Inertia's form helper, and that gets imported automatically. Okay, so what do we have here? An email and a password, that's what you have to give us. Now a little note here, you'll see that I have created two script tags. At the time of this recording, I don't know of any way to disable the layout from within script setup. That might change when you're watching this, but yeah, again, at the moment, the only way
Let's see what that looks like. Switch back, yeah, getting a little closer, but I would like this perfectly centered on the page. So on the main section, I can use standard Flexbox for that. Flex, or actually, you know what? Why don't we do a grid, and we can say place-items to the center, and that should achieve the same thing. But for that to work, we do have to make sure that this main tag takes up the full height. So let's say min-height is screen, which basically means at minimum, it needs to be 100% of that window's height.
Submit Login Form8:50
How should we submit this form? Well, we already have the event handler set up from the previous page, so I'll scroll down and we'll declare this method. And what should happen when you submit? Well, we're already using Inertia's form helper, so can I just say form.post to login? All right, let's see what happens there. Come back, give it a refresh. I'll try to sign in John. I don't think he exists. Login, and yeah, now we do get a method not allowed exception.
Implement Login Authentication9:16
I don't think he exists. Login, and yeah, now we do get a MethodNotAllowedHttpException. We're trying to post to /login, but we haven't yet created that. Okay, that's our next step. So right up here, let's listen for post to login, and that will hit a store action. All right, let's do that now. store, and this is where we log in the User. All right, so let's do this to save a little bit of time. I'm going to go to the Laravel docs. And if I search for authentication in the security section, of course, you may know
I'm going to go to the Laravel docs. And if I search for authentication in the security section, of course, you may know that Laravel offers a bunch of starter kits or quick starts, and by the way, many of those will have Inertia options or flavors or adapters that you can use. But in our case, we're just doing it manually to illustrate the basic process. Anyways, when we manually authenticate a User, here's a full example of basically what needs to happen. You validate the request, you try to sign in the User. If that was successful, you regenerate their session, and then you redirect them where they intended to go.
If that was successful, you regenerate their session, and then you redirect them where they intended to go. Otherwise, you redirect back with validation errors. So let's save ourselves some time and just grab all of that and paste it in here. And then I will just update store and import the request. And also the Auth facade. All right, saves a little bit of time. Now this should be fairly easy to understand. The only thing that might be confusing is redirect()->intended(). So imagine a situation where you try to visit your, say, settings page, but you're not signed in.
Share Auth User Data11:36
If I log in, we do see a validation error. Okay, there we go. So now we are officially signed in, and do note that it redirected us to where we intended to go. Okay, so now we can make a lot of this more dynamic. Before we were hard coding a User, but we don't have to do that anymore. So let's go into our layout. And let's see. Welcome back, username. And let's see.
Welcome back, username. And let's see. Ah, so we were already fetching that dynamically. So it sounds like I just need to go to HandleInertiaRequests. And let's see. Ah, yeah. We must have hard coded this a number of episodes ago. Now we can change this to Auth::user()->name. But one little thing. This is going to work.
But one little thing. This is going to work. So if I come back and refresh, sure enough, we see. Welcome back, Susan. But yeah, if Susan is not signed in, then Auth::user() will return null. And then when we try to access name on null, it'll throw an error. So what you might do is something like this. Check to see if we have an authenticated user. And if so, grab only the information you care about. Otherwise, make $auth equal to null.
And if so, grab only the information you care about. Otherwise, make $auth equal to null. And that should do it. Okay, now before I finish up here, a common question people will often ask is something like, well, why are you doing it like this instead of Auth::user() and then the only method to grab only the fields that you care about? So the username or their name or their email or things like that. And you can do that. It's totally fine. Just the one thing to be aware of is that now you have a direct one-to-one relationship.
It's totally fine. Just the one thing to be aware of is that now you have a direct one-to-one relationship between your table column names and the properties that you pass to the client. And you may not always want those to be the same. And actually, often you won't want those to be the same. So that's why if we switch back to this example here, we have a little more control. Actually, in this case, notice I happen to be calling it username, even though it's pointing to an actual name. And that's just a mistake on my part. It doesn't really matter.
Add Logout via POST13:38
And that's just a mistake on my part. It doesn't really matter. But again, it's an example of how there might be situations where what you want to use on the client isn't necessarily the name of the column in the database table. So with this approach, we are separating the two. Okay, but anyways, if I come back to Firefox and give it a refresh, this is looking pretty good. So now that we are logged in, we need a way to log out. So let's add a logout link to the navbar. All right, let's go to layout.
So let's add a logout link to the navbar. All right, let's go to layout. And let's see. Looks like we created a nav component. Settings, yeah, I'm just going to add another one here. And this will log out. Now this will go to a logout endpoint. And it doesn't make sense for this ever to be active. So let's get rid of that and have a look. Refresh.
So let's get rid of that and have a look. Refresh. And yeah, now we have a logout link. But when I click on it, of course, it's going to make a GET request to logout. And generally, it's a better practice to instead submit a POST request to log a user out. And luckily, Inertia makes this really easy. So real quick, notice that on our nav link component, that's actually just deferring to Inertia's link component. So anything I pass to nav link will be sent to the link component. Okay, so Inertia's link component includes a method prop we can use to declare the request.
So anything I pass to nav link will be sent to the link component. Okay, so Inertia's link component includes a method prop we can use to declare the request verb or method. So in our case, if I wanted to make a POST request, it's as simple as that, which is really cool. Usually, for anything that's not a GET request, you'd have to create a form or do something weird like that. But this should do the trick. So now, we haven't created that logout endpoint yet, but do notice when I click on it, we Oh, actually, you know what, I think a long time ago, we did create a logout.
So now, we haven't created that logout endpoint yet, but do notice when I click on it, we Oh, actually, you know what, I think a long time ago, we did create a logout. Yeah. Okay, I'm sorry about that. Okay, just to show you what this would look like. So if I open up the network tab, we should now see that we're making a POST request to /logout. Okay, so let's get rid of this entirely. And I'm going to move it all the way up here. If you make a POST request to /logout, that will hit the LoginController and maybe
And I'm going to move it all the way up here. If you make a POST request to /logout, that will hit the LoginController and maybe a destroy action. But actually, this is the only case where you do need to be signed in in order to access this route. So I could put it within here, but I just want it to be near all of the other login routes. So I will manually add this on like so. Okay, so now, we'll set up our destroy action. And this is very simple.
Okay, so now, we'll set up our destroy action. And this is very simple. We can just say auth logout. And then let's redirect you to the login page. So I could hardcode it, or I could say redirect to the route named login. Or in real life, maybe there's a splash page for your app, in which case you'd redirect there. Okay, let's see if it works. logout. And it works.
Logout. And it works. So you see what I mean how authentication in an Inertia app is really no different from how you would authenticate in a traditional Laravel server side app. It's the same basic process, which is one of the big wins when you use Inertia. All of that overhead of tokens and OAuth and figuring out how all of that should communicate, it goes out the window. You don't even need to think about it at all.
