Now that we have a conditional in for the admin button, we need to do the same for all of the admin routes because currently, they are still accessible to everyone.
We will set up a function to check if the user is an admin when accessing a route. If they are an admin, they will be able to proceed. If they are not, it will force a logout request which will redirect them.
Kent C. Dodds: [0:00] Even though we've successfully hidden the admin link from people who are not the admin, the admin page itself is still accessible by anybody and they can create new blog posts and all that. We don't want them to be able to do that.
[0:12] We're going to redirect users to the posts/admin page or to the home page if they try to access that particular page. We're going to make something similar to this requireUser function in our session server. Export a function here called requireAdminUser.
[0:31] This is going to take request, which is a request object. Here, we'll get the user from await requireUser from that request. Then we'll say, if the user.email is not equal to the ENV.ADMIN_EMAIL, then we're going to throw await logout on that request. Otherwise, we'll return the user.
[0:56] What this await logout thing does, and we're throwing it, is the logout will give us back a redirect response. When you throw a response, it stops all execution of the code. Remix will catch that and perform the redirect.
[1:11] Now, we've got a function that we can put anywhere we want to prevent non-admin users from accessing. If we look at our admin.tsx here, we can add await requireAdminUser. We'll pass our request onto that. We need to get the requireAdminUser from our session.server file.
[1:30] Then, we'll do the same thing in our new.tsx. We can require the admin user for the action. Let's bring that in from the session.server. We also want to have a loader. Let's export const loader as a LoaderFunction. This is going to be an async function that takes the request. We'll await requireAdminUser with that request. Let's bring in the LoaderFunction.
[1:57] We want to do the same thing with the index.tsx. We'll copy and paste that. Now, we have protected our admin-only routes. If I go over here and I remind myself that, yes, if you do define a loader, you do need to return something. We'll return json with an empty object. We'll want to do that here as well. We'll bring json in from remix-run/node. Great.
[2:22] With that, now we can get to our admin page, but if we're not an authenticated admin user, then it will redirect us to the log in with a redirect to where we can log in, firstname.lastname@example.org, racheliscool. If we log in, then it will send us back to the posts/admin page.
[2:41] In review, all that we did here is we went to the session.server. We created this requireAdminUser that accepts the request. It requires a user. It verifies that the user's email is the ADMIN_EMAIL and then, returns the user. Then, we went to each of the admin-only routes. We said requireAdminUser for that particular request.
[3:00] We did requireAdminUser on the new page, as well as on the index page. On the new, we also did this in our action to make sure that the admin user is the only one that can create blog posts. This is important to do this. You can't just put it on a segment of all of your routes because the loaders and actions can be run independently and in parallel.
[3:22] This does need to be applied to each one of these loaders and actions. Most of the time, you're going to require some sort of admin ID or something in here. You're going to be typically getting the admin user or getting the user for the request anyway.
[3:38] In addition, if this does become overly cumbersome, then you can use Remix with some sort of middleware tool like Express. You can add a middleware that double-checks the user's authenticated state there. There's nothing currently built into Remix for that and so, we are going to be adding this requireAdminUser on any page where we only want the admin user.
Why do we need to
await requireAdminUser(request); on all pages in the admin folder? (index and new). I assumed it would be enough to do it only in the admin.tsx file (and indeed, it seems to work). Am I missing something? Aren't the loader of the parent route called anyways?
oh well, I should have just listened until the end of the video. too eager once again.