Now, how can we clean up
the navigation bar items here?
Theoretically, it's quite straightforward.
We have our header EJS file.
And in there we can now render these items conditionally
with help of EJS.
The problem just is that the header EJS file
is included in a bunch of templates.
And for all those templates,
we in the end have to forward information about the user,
wherever he or she is logged in,
and whether he or she should be able to visit
the admin page.
And that can become pretty annoying
to do this in every route
and forward this to every template so that in every
template we can forward this information to the header
that would be super annoying and a lot of copy and pasting,
and it would be pretty error prone.
Thankfully express JS has an easier approach,
which we can use
in App.js, where we have all these general middle wares
that we can use everywhere.
We can also write our own middleware that sets up some data
that will be exposed to all templates automatically
because it's such a common use case
that you have data that should be available
to all templates.
All we have to do here after initializing our session here,
we add app use and add our own function here.
So we write our own middleware and the middleware
just looks like this basic function we saw before
in this course a couple of times
where we get a request, a response,
and then also this special, next thing,
which we will actually use now.
So we have to accept this as a parameter value here
as well.
Now in here,
we can find out if a user is authenticated
by reaching out to request session is authenticated
that's this extra flag,
which we are setting when we log a user in.
And I also wanna find out if a user is an admin or not
for this, I'll use my DB helper,
which is already being required here.
And I will turn this into an async function
so that we can use a single weight
and I can call, get DB to get access to my users collection
and find one user.
Now I need to know which user and therefore,
I will also extract my user from my session
just as we did it before in other parts
of this website here.
So I'll get req session user,
and this might or might not be set.
And then here a look for the user where ID
is equal to user.id.
That's another piece of data we're storing in a session
and we used all of that before already.
Now we can await this to get the user from the database
since I have a name clash, now I'll name this user doc,
because I have user here already.
And I now therefore can extract the isAdmin feature
from a user doc isAdmin.
And this also is either on the find or false,
or it might be true.
Now of course, looking for a user only makes sense
if we have one.
So if we are not able to extract the user from the session,
I don't wanna execute this code down there.
So here I'll first of all, check, if not user,
if this is faulty, or if we somehow have a user, but is off,
is false,
then we also know that the users certainly
won't be authenticated.
And in this case,
I wanna return here so that this code doesn't execute
and we now have to execute this next thing here,
this next parameter as a function.
And what this does is it simply tells express
that this request for which this middleware
is being executed here
should be forwarded to the next middleware or route in line
because this here, this middleware function,
which we are writing will run for every incoming request,
but it should only do some things with that request
and then move on to the next middleware or route in line.
And by calling next, we signal that we're done
and that the request is now allowed to move on
to the next middleware,
which is this next middleware registered below in this case,
my demo routes.
So now this request we'll move on to the demo routes
and it will move on if we're not authenticated.
Of course, I also wanna move on here at the end
if we are authenticated.
So I should also to call next here,
but I wanted to do it here as well to make sure
that this code doesn't even execute,
if we don't have a user.
Now that alone, isn't too useful though.
Now we're just repeating things we did before already,
but now we can use a special express feature to store
the information that we gathered here like isAuth
and isAdmin in a certain place,
which we can access from all our templates
without explicitly passing the data to them
and in all other parts in all our middle wares
that come after this middleware here as well.
And that's the res dot locals field.
So on the response express has this locals field
and locals allows you to set some global values,
any values of your choice
that will it be available throughout
this entire request response cycle,
but only for this request response cycle.
A new request will not have any of that data,
and we don't want to have that data anywhere else,
but we wanna store it for this request,
which we are currently handling,
and we're storing it on this response here.
Now, here you can add any key value pairs of your choice
for example, isAuth and set this equal to
isAuth in this constant
and if we make it down there, I'll also set res locals
isAdmin and set this equal to isAdmin here.
And actually this can also be set to down there
because if we don't make it past this if check,
we know the user isn't authenticated,
and then we don't even need to set isAuth here to anything.
Now we have these two global variables
and you could store more data.
And these variables are by default available
in all your templates, that's the big thing
that's the big thing of this locals feature.
We set it in our own middleware so that this code runs
for every incoming request
and it runs before we reached a demo routes
so that this global data is set before we make it
into our main routes.
And now here,
we can go to the header and use that information,
for example, for this logout list item,
and here we cannot check if locals.isAuth is truthy.
So this locals key here with all the data we stored in it
will be available in all templates without you passing it
into those templates explicitly.
And that's great for such general template, white data,
like this isAuth state
and here, of course, we also need to close to curly brace
and now this list item here will only be shown
if isAuth in this resurface locals, key and object,
which express has built in is set to true.
So if you save this and I reload this page here,
I don't see it because I'm not logged in
as you can tell if I click on admin or a profile.
So showing log out makes no sense if I'm not logged in,
so that works.
Hence of course, we can also move admin and profile
into the same if check
because those items should also only be visible
if we are authenticated
and actually for admin, we can move it out of there again,
because there we have even stricter check.
We can check if locals.isAdmin is true
if that's not true,
we don't wanna show the profile link no matter
if we are authenticated or not,
because we need authorization for that.
So now with that, if I save this and reload,
these links are also gone, if I'm not logged in,
if I do log in here,
let's say with the non admin user, whoops,
wrong user, this one, then I see profile,
but not admin because this user isn't an admin.
I also see log out and if I click it, I don't see it again.
Now I only wanna see sign up and log in
if we are not authenticated
and therefore here we can run the opposite check
and check if not isAuth
and only in that case, I wanna show these two items.
So also close that here like that.
And of course it should be locals.isAuth whoops, like this.
So local dot isAuth is what I'm checking here
and if I'm not authenticated, I showed these items.
So with that, if we save this,
I see them here because I am not logged in
and if I do log in this time with my admin,
then indeed I see admin and profile
and I can go to both pages,
but I don't see sign up and log in anymore
except for when I log out, then I do see these items again.
So res locals is really useful for setting up
such global data so that you don't have to pass it manually
into all these templates.
And of course,
we cannot also use that in other places as well.
Like in our routes, in our routes we are manually checking
if a user is logged in or authorized in our get admin
and get profile routes.
Now, since we do that in our middleware already
in that new custom middleware, we wrote this middleware.
We don't need to do it again here in these routes
res locals is also accessible in these routes
because these routes come after our custom middleware,
the order does matter here because the requests travels
through all these middle wares in App.js from top to bottom.
But here our custom middleware extract some data already
and stores it in res locals.
And they offer here in our routes
res locals is set to these values.
So therefore here in slash admin in this get route
instead of again, looking into the session
and even retrieving a user here,
we can just check if not res locals is off,
it's true here.
So if that's not true, and we know we're not authenticated
and we don't need to fetch a user here,
instead here we just check if not res locals is admin
in which case is sent back the 403 page
and I need to return here.
That's important so that this code doesn't execute.
We should have done this before as well
that was a tiny bug.
You definitely have to return here
so that this code doesn't run
the same can be done in the profile page.
We don't need to look into the session again.
We could, but we don't have to we can look
into res locals is off.
And if this is false, hence the exclamation mark,
I return the 401 page.
So now again, if I save and I go back
and manually try to access slash admin,
I still get not authenticated because I'm not logged in
but if I do log in with my user again, here,
then I can visit those pages just fine.
So that protection works as before,
but now we're using res locals
since we already set this in our custom middleware here,
and that's another very useful feature
you should be aware of when working with authentication,
it's not authentication exclusive.
You can use res locals for anything you want
and you can write custom middle wares for anything you want.
But here, especially for this use case,
both features combined are very useful
because now we can check the authentication
and authorization status once,
and then we stored it so that we can use the stored data
in all our routes and all our templates
without having to pass the data
into the templates explicitly.