3.2 Authenticate Users with Middleware and Guards
We need to authenticate our app’s admin users, and we’ll do so by writing some middleware. We’ll also define an admin guard. Along the way, you’ll learn a very important fact about guards: authenticated users only authenticate against the same guard.
1.Introduction2 lessons, 09:21
2.Custom Laravel Authentication3 lessons, 24:42
3.Real-World Authentication3 lessons, 28:43
4.Conclusion1 lesson, 00:59
3.2 Authenticate Users with Middleware and Guards
In the previous lesson, we started customizing our applications so that we can authenticate users based upon whether or not they are an administrator. We wrote to a new user provider and set everything up except for the authentication middleware. Now, why do we need authentication middleware to begin with? Well, that is how we protect things within our application. For example, this is the home controller inside of app http, Controllers and Home Controller, we can see that inside of the constructor, there's this call to the middleware method and it is specifying off, this is the authentication middleware that protects this home controller. So everything that is on this controller, which right now there's just index, but you would need to be authenticated in order to access anything on this controller. Now, you can specify some exceptions. If we look inside of the Auth folder and go to the Login Controller, it's constructor is using the guest middleware. So this is ensuring that in order to access the login controller, you can't be logged in except for the log out method and that makes sense because you have to be authenticated in order to log out. But in order to do anything other than log out, you need to be a guest. So, this is one way that we can protect things within our application, we can define the middleware inside of our constructor, we can also do so whenever we define our routes. If we go to web.php inside of our routes folder, we can see the route for home. And if we wanted to specify the authentication middleware, we could do that here. Whenever you look at other applications, you're going to see a mix of where that authentication middleware is used. You'll see it used within the routes or you will see it used within the controller. It just really depends upon where it makes sense to do so. In this lesson, we are going to write some middleware that we will use to authenticate a user and ensure that they are an admin, like for example, let's take our home controller. Let's copy it and let's rename it to AdminController and we, of course, need to change the class name as well. So let's do that right fast, but let's say that's we don't want to authenticate just anybody. We want to ensure that only admins have access to this controller, because it is an admin controller. So we don't have this authentication middleware yet, but we will say that, okay, we are going to use this auth.admin middleware to ensure that the user is an admin, so that they can access the resources on this controller. And instead of returning a view, let's just return some content, this is the admin controller. So let's write this middleware. And the first thing we need to do is write a class for that. So let's go to our extensions folder and, inside of here, we are going to create a class called Authenticate admin. And this is not going to inherit from anything else. It's just going to be a standalone class but there are some resources that we will need. The namespace will be app extensions just like everything else we have done. We need to use Closure. And we also want to use Illuminate\Support\Facades\Auth because we need the auth facade in order to get the guard. And we're going to use the guard to determine if the user is a guest or we're going to use the guard to get the currently authenticated user. So our class is AuthenticateAdmin and this class is going to have a method called handle. This is the method that's going to handle the request and that is the first thing that has passed to this method is the request. The second is going to be the next piece of middleware and we are typing in that as a closure and then we also have the guard but we're going to initialize guard as null. Now, this is the name of the guard and we will use that in order to get the actual guard to being used here. So that's the first thing we will do. We'll use our Auth facade, we will call the guard method and then we will pass in that guard and that will give us the guard for this request. And if all goes according to plan, that's going to be the guard that we created in the previous lesson. I've already forgotten what is called but that's easy enough to look up. It's the admin guard. So once we have the guard, we can do two things. We can determine if the user is a guest because we know from looking at the guard interface that there is a guest method that returns a boolean value based upon if the user is a guest. So if they are a guest or if they aren't an admin. So in order to determine if they are an admin, we need to get the user which we also know we can get from the guard and then from there, we will check is_admin property. Now, in either one of these cases, the user is not authorized for whatever is being protected by this middleware. So we are going to say for a one, you are not authorized. However, if the user is an admin then we want to pass the request on to the next piece of middleware. So we will call next, and we will pass in our request. So this is our middleware. It's very simple, very straightforward, we just need to add it to our application. So let's go to the Kernel.php, and here, at the bottom, we see this route middleware but we can read the comments. It says that this middleware may be assigned to groups or used individually. That's great. So we can already see the middleware that's already defined here. We're going to add hours in between auth and auth.basic, and we're going to use the same scheme, as far as the name is concerned, auth.basic. So we're going to say auth.admin and then we just need to specify our class that is App\Extensions\AuthenticateAdmin: :class. So, we have added our middleware to all the other middlewares so that we can use it within our admin controller. The only other thing that we need to do here is specify our guard because by default, it's going to use that web guard. So we're going to say :admin. So we are now hopefully protecting this admin controller using our admin guard and our auth.admin middleware. So cross your fingers, let's go back to the browser, refresh, no errors. That's a very good thing. We need to add a route, don't we? Let's go to web.php. Let's copy this one route that we have, and let's say that /admin is going to go to AdminController@Index. Now, let's go back to the browser, let's refresh. Let's cross our fingers again. No errors, that's good. So let's log in. We want firstname.lastname@example.org or whatever we have used before. We log in, we still have access to home. That's great but if we try to go to admin. Uh-oh, something went wrong but if we look, we see that it is a 401. Now, this looks like a server error but it's not and we can prove it by going to the developer tools, make sure the network is selected and refresh. And of course, you also want to to be recording the network traffic but you can see that the response for this request is a 401 which is what we expect because our user is not an admin. So let's change that. But let's create a new user that is specifically an admin, that way, we don't get confused as to what should or should not have access to something. So we want to log out, let's go back to home, and let's log out and we will register a new user. Let's make sure that we know that this user is an admin. So the email address is going to be email@example.com. Let's see if it accepts the password of admin, and it does not so let's do password and then we are logged in. Now, we do need to set that is admin flag. So let's go to the database and we want to go to the user's table and then we want to modify the admin user so that it is an admin. So that is now changed. Whenever we refresh, everything should be fine. So now let's go to admin. Now, remember we are logged in with an admin user. That admin user is an admin because we just change that value, but we still get a 401. Now, this might not be what we expected, but this is what is supposed to happen. And let's go through the process of what we did. So, first of all we logged in with our user. But if you remember, we have several different guards defined. So if we look at those there is the web and the API, those are built in, and then we added our own admin guard. Now, the default guard is web. So if there is not a guard specified then the web guard is going to be used. So whenever we logged in with our admin credentials, we did so using the web guard and so as long as we authenticate using the web guard, we will be fine. However, when it comes to the admin controller, well, we are using our middleware, but we are also specifying that we want to use the admin guard. Well, we didn't authenticate with the admin guard, so therefore, we are not logged in as far as the admin guard is concerned. So when you're going to be working with multiple guards within the same application, you have to be aware of this. Now, we can make this work. We can come in here and say that's okay, we are not going to use the admin guard for this auth.admin middleware. So if we go back to the browser and refresh for the admin path, then this is the admin controller, we are authenticated because now, we are authenticating against that web guard. But, let's switch it back to admin and of course that is going to fail. So the solution to this particular problem is rather simple. It is a little involved that we have to write extra code because we essentially need another log in form, so that whenever we log in, we will log in with the admin guard. So our application is going to end up with two log in forms. There's going to be one for admins and then there's going to be one for your everyday user and we will look at that in the next lesson.