Now, in this course section,
we re factored our code a lot.
Now I got a little bonus task or a little bonus extra work,
which we can do, and which we should actually do
if this would be a realistic site
and which we therefore will do
when we built this online shop in the next section.
And that would be proper route protection.
Now, what do I mean by that?
What is route protection?
In the auth course section
and in discourse section as well,
we saw that some routes to be precise, to getAdmin route,
first checks the authentication status,
and actually returns the 401 page,
if an unauthenticated user tries to access this page.
Therefore if I log out
and I try to manually enter /admin,
I get this not authenticated error as expected.
Now this works as expected,
but we're only using this on the getAdmin route here
and to get admin controller action.
But if we take a closer look,
we actually should use it in our routes as well.
And for example, here, when we create a post,
sure, this action is triggered
upon an incoming post request,
but such post requests could also be sent
from unauthenticated users.
Of course, not just like this on this page,
we have no forum that would send this request
that would be reachable for an unauthenticated user,
just like this.
But there also are tools outside of a browser
that could be used for sending such requests.
We could also write some basic Ajax JavaScript code
that sends such a request.
So sending post requests to this route
that triggers this action, would also be possible.
So we should all check the authentication status here.
And actually, we should check it
in all those posts related routes.
For example, getSinglePost is triggered with a get request
and is not protected at all.
So if we know the idea of a post,
we can manually enter the correct URL in the browser,
and we would be able to load this page
and therefore trigger this action.
So long story short, this logic,
which I have here for locking down access
should be used in all the routes in this file,
except for the get home route,
because that starting page should be available
without logging in as well.
Now, of course, we could just copy and paste that code
into all the actions and that would do the trick,
but copy and pasting is rarely the best idea.
Instead, what we could also do
is we could add another custom middleware,
which we execute for all these routes
that should be protected,
and which then automatically
should check the authentication status
and redirect if a user is not authenticated.
And that is what I'll do.
For this in the middlewares folder, I'll add a new file
and I'll name it auth-protection-middleware,
file name as always is up to you.
And in there, I'll add a classic middleware function.
I'll name it, protectRoute or guardRoute,
whatever you wanna call it.
And we get the request in the response objects
and this next function.
Now in this middleware,
I wanna check a res.locals.isAuth is truthy.
So in the same check, I have in a post controller here
in my getAdmin route.
To be precise here,
I'm checking if the user is not authenticated
and we can do the same thing here and check if not isAuth.
So if this request is coming from a user
for whom we set res.locals.isAuth to false.
And keep in mind that this is set in this auth middleware.
Here we are setting res.locals.isAuth.
Now, if the user is not authenticated,
then I wanna return and render a response,
to be precise, render the 401 template here.
Or we actually do that differently
so that we can also apply this for postRoutes,
and instead of rendering a template,
I simply redirect and redirect to /401, for example,
which is a new route we should add now.
And I will add it in my auth routes file
because a route that
basically just renders the 401 template,
which tells us that we're not authenticated
fits perfectly into the author route setup in my opinion.
So in here, I'll now add a get.route/401
where I wanna trigger a new action in the auth controller,
which we have to add now.
And there you can add it anywhere,
for example, at the very top, the get401 function here,
which gets a request and a response.
And here I then wanna call res.render
and render the 401 page
and also set the status code to 401 like this.
The setting the status code is of course optional,
but not a bad idea.
Now to make this newly added
get401 function usable outset of this file,
we have to go down and export it
by adding it to this exports object, like this.
And then back in the authRoutes file,
here, I'll connect this 401 route I just added
with authController.get401.
Now we added this route, so now we can redirect to it.
And therefore back in off protection middleware,
this res.redirect should work
if we got a request from a unauthenticated user.
Else, if we make a post there's a check,
we know that the user is authenticated
and then I'll call next,
so that the request which was handled by this middleware
is able to move on to the next middleware in line.
And then we can export this guardRoute function like this,
and then use it in the place
where we want to protect content.
Now, where would that be though?
Well, since this is a middleware,
we wanna register it as such.
And up to this point, we always did this, an app.js.
And here, indeed, we could consider registering it here,
maybe after the auth middleware,
but keep in mind the middleware we just wrote,
we'll check all incoming requests
and look in this res.locals field
to see if the user is authenticated
and redirect to the 401 page, if that's not the case.
So if we activate and use that middleware
before we reach the blog and authRoutes,
logging in or signing up will be impossible
because all those routes will never be reached
by unauthenticated users because our middleware redirects
before these routes got a chance of taking over.
So activating our guard middleware here is wrong.
Activating it after these routes is all the wrong
because then our route controller actions
already handled the request.
So then it's too late to look into the request
and see if we wanna block it.
So therefore the right place to do this
is in the place where we define our routes.
And then we can add the middleware to all the routes
that should be protected.
And there are two ways of doing that.
I showed you a little bit early already,
that dos get and post functions,
which you use for registering routes.
And actually all the functions express
gives you for registering middlewares
take multiple parameter values.
The first parameter value is such a filtering path here,
but then the second value is a middleware function
that should become active if a request reaches this route
and then all other arguments
that you can pass to these get and post functions
should be more middleware functions
that will be activated step-by-step from left to right.
So if I wanna protect the admin page
with my newly added middleware,
I can import my guardRoute middleware
by requiring it from middlewares auth-protection-middleware.
And then we could simply add it here, guardRoute like this.
I'm not executing it,
instead, I'm just pointing at it
because this guardRoute function which I'm importing
already is such a middleware function
and therefore as mentioned before,
it should be executed by express.
Now with this approach,
this admin page would be guarded by that middleware
because all of these middlewares here execute left to right
for any requests that match this route definition.
And therefore we would first make it into guardRoute,
and only if that grants access to getAdmin,
we do get in there.
So only if that calls next,
we will move on to the next middleware in line.
So that works and we could therefore add guardRoute
to all the routes that should be protected.
This would be fine, but if you have a lot of routes
that should be a fact that by the same middleware,
copy and pasting the middleware can become annoying as well.
Therefore, as an alternative, what you can also do,
is you can use a router use here to define a middleware
that should run for all incoming requests.
No matter if it's a get or post request
and no matter which path we have there.
Though you could always add a path filter
if you want it to narrow it down somewhere.
But here, I'll just use the guardRoute like this.
And the effect of that will be
that all the routes after this line 10 here
will be protected by this middleware.
The route in front of it get home will not be protected
and indeed it shouldn't be
because dad should be accessible by authenticated users
because the incoming request
basically travels through all these routes and middlewares
from top to bottom.
So first this route is checked
and if that's the right route it's executed
and the coat thereafter won't execute
for the specific request that's currently being handled.
If that was not the right route for the incoming request,
it will next go into this middleware,
and the that middleware might then redirect to the 401 page
or grant access,
and the request moves onto the next route in place.
And with that,
we subsequently move through all these routes there
and in the end,
one of these routes will hopefully become active.
And that's how we can protect multiple routes
with that middleware.
The order is super important
if you're doing it like this still.
And with that long story short,
if we go back to the post controller,
we can remove this check from getAdmin,
because it's now protected by our middleware.
And as a result, if I save that, and I'm not logged in,
accessing /admin should still fail.
I'm redirected to 401.
However it crashes here.
And the reason for that is that an app.js,
we now must move the authRoutes in front of the blogRoutes
because you have to keep in mind that his 401 route
is to find in my authRoutes
and all these routes should not be protected.
Now, since I added my guard route middleware
in the blogRoutes and requests travel
through all the middleware functions from top to bottom,
if we had the order from before
that the blogRoutes are evaluated first,
then every incoming request,
no matter which URL it's targeting,
will first move into blogRoutes and then into authRoutes.
However, inside of the blogRoutes,
it then also goes through all these middleware functions
from top to bottom,
and therefore it will also reach to guardRoute
and that will block the unauthenticated request
and redirect to the 401 route.
That's good, but the 401 route can never be reached
because to reach it,
we would have to make it past these blogRoutes.
So therefore the order matters here
and we have to move our unauthenticated routes.
So to routes that should be accessible
by authenticated users in front of the other routes.
So all three routes should come first.
With that, we now have a chance of reaching the 401 page
and therefore we can reach
all the pages we should be able to reach,
but if we try to reach admin, we see 'Not authenticated.'
Of course, on the other hand,
if we do now log in with a valid user,
we can still visit the admin page and so on.
So that then still all works
because now our middleware lets us through.
And that was another building block
that you should know in general
when working with authentication
that you authenticate and protect the right routes,
but also specific to this section
that you know how you can easily reuse this logic
with your own middleware,
but that then you have to make sure
that you order your routes correctly.
Again, this is all that concept,
we'll see again, in the next core section,
when we built this complete online shop.