Now we learned a lot about authentication,
sessions, cookies, and so on.
I will conclude this core section
and the new knowledge I want to pass on to you
for this section
by having a look at authentication
versus authorization.
Two terms that are kind of related,
but describe different things.
In this course section,
we thus far focused on authentication.
Now authorization is related, but different.
With authentication, we're talking about signing up
and logging in with credentials
and about storing data in a session
to grant a user access
to restricted resources and pages.
With authorization, we take this a step further.
As a user you might be logged in and authenticated,
and you still might not be allowed
to do everything.
With authorization, we restrict the actions
that can be taken by a user
or the pages that can be visited by a user,
even if he or she is authenticated.
And a typical example would be
that you have an online shop.
And if you log in,
you can only view your orders.
You can't view all the orders of all the users.
You can only see your orders.
Or if you're building a blog,
then you might be allowed
to log in as an administrator,
who is able to delete and edit
and create blog posts.
And maybe you also can log in
as a regular user
so that you can leave comments,
but you then still are not allowed
to edit or delete blog posts.
So even if you are logged in,
you typically can't do everything.
It, of course, depends on which website you're building.
And I want to show you how you could implement
authorization here as well.
To implement this, in this existing application,
you're in this existing website,
I will copy the admin.ejs file
and create a new profile.ejs file based on it.
And in this profile template,
I'll say "The Profile Page,"
and then just quickly replace
all that content here
with one paragraph there,
where I say "Your profile."
It's just a dummy page.
And I'll say "Profile" here as a title.
Now, my idea is that we have this page as well,
and every logged in user should be allowed
to reach this page,
but only administrators should be allowed
to reach the admin page.
Therefore I'll go to the routes,
and first of all add another route.
I will simply copy my admin route here,
this get route,
and add it as a /profile route,
where I render the profile template
if we are authenticated.
The check is the same as before.
I render the 401 page
if we are not authenticated.
So that hasn't changed.
And now I will also go to my header here
and add a new list item to it,
which links to this profile page,
so to /profile.
With all of that, if we reload,
we got that page here
and we can now visit both pages
if we are logged in.
And if I do log out,
we can visit neither of these two pages.
But now again, only an administrator
should be allowed to access this page.
Now, to practice that,
I will manually edit one user
to turn him or her into an administrator.
And I'll simply take the first user here.
Of course, you could also edit the signup page
so that it has a check box
that allows you to sign up as an administrator,
but that's also not realistic
because you typically don't let users
choose whether they are an administrator,
but you would instead manually assign
the administrator role.
For example, through an extra interface
or here, in this case,
simply directly in the database.
So for that, I'll take the ID of this user
and then run db.users.updateOne,
because I want to update this document.
And for my filter condition,
I'll look for the user
where the ID is equal to this ObjectId I grabbed.
And then the second parameter value here
defines how we want to update the user,
and there we use this special dollar sign set property,
and then I'll set this to a new object.
And in this new object, I'll add my new key.
Let's say the isAdmin key.
It's broken across two lines here.
I'll zoom out a bit.
So I'll set the isAdmin key here,
and set this to true.
Only this user receives this flag.
If I hit Enter, this was updated.
And if I now find all my users,
we can see that the first user has the admin flag,
the other user doesn't have it.
This is also not the only way
of implementing authorization.
There are very elaborate ways
where you have multiple roles
that might be stored in a separate collection,
and a user then might have one
or multiple roles assigned to him or her.
And so on.
There are different ways of implementing authorization.
This is a relatively simple one,
where I simply set a specific flag,
but it shows what authorization is all about.
And that is what this lecture here is all about.
So isAdmin is true here.
That's in the end the role I'm signing with this flag.
And now the admin page
should only be visitable if this is true.
Now, how do we implement authorization
in our code then?
Again, there are different ways of doing that.
When we authenticate a user,
so when we log a user in,
then we store some user data in a session.
For example, the ID.
Here we could also store the information
whether the user is an admin or not,
by simply reaching out to existingUser.isAdmin.
And this is either undefined if it wasn't set,
in which case isAdmin will have undefined as a value,
or if a user has this flag,
the flag value is used.
So that would allow us to store
the isAdmin value in a session,
and that would be absolutely fine
in a good way of implementing this.
Alternatively, of course, we don't store it.
We just store the ID and maybe the email.
And we instead fetch the user record based on that ID
in the place where we want to check
whether that user is an administrator or not.
That will require an extra database query,
but it keeps the session a bit leaner.
Another advantage is that if you ever change
the admin status of a user,
you don't have to update the session,
which you would have to do if you store it
in the session as well.
If you only store it in the user document,
the session does not need to be updated
if you change that data.
And that's, in general, true.
The more user data you store in a session,
the more data needs to be updated in a session
if that user data changes.
You should also keep that in mind
when storing the email.
If a user then gets a new email address,
you'll have to update it in the session as well,
at least if you rely on that email in the session
anywhere else in your website.
So therefore here I'll not store the role
in the session,
but instead I'll just get it
just in time when I need it.
And that's here in this get admin route.
The first step still is to check
whether a user is authenticated,
because if a user is not authenticated,
he or she also can't be authorized
to access this page.
So in this case, we don't even have to continue.
But if we make it past this first if-check,
we know the user is authenticated,
and then I want to check if it's the right kind of user,
if it's an admin.
So then here I'll get my user
by using the database getDb,
and there the users collection.
And I'll find the user by ID, let's say,
by looking for ID, _id,
being equal to request.session.user.id.
Keep in mind that in my user session object,
I have this ID key, which is the user's ID.
Now that is an asynchronous operation,
so I will convert this into a async function
so that we can use the await keyword here
and wait for this result.
And then we should find a user for this given ID.
Now, if we don't find the user
for whatever reason,
though that shouldn't happen,
or more importantly,
if the user is not an admin,
so if isAdmin is false,
which is what I check here,
then I don't want to render the admin template.
Instead then I want to render a new template,
which I'll name 403.
This is a template that doesn't exist yet,
but I'll copy the 401 template
and rename it to 403.
And in there I'll say "Not authorized.
"You are not authorized
"and therefore not allowed to access this page."
And that's different.
You are authenticated,
but you are still not allowed to access this page.
That's what I'm saying here.
And I don't just render it like this.
Instead I will also set the status code here
to 403 because that is the status code
that you should use
if a user is authenticated,
but not authorized to do something
or to see something.
And with that, if we save this,
if I log out,
if I create another new user here,
just so that I have a non-admin user as well.
And then if I log in with this user here,
then you see I got not authorized
when visiting the admin page.
I can visit profile,
that works,
but I got not authorized
when visiting the admin page.
And therefore here, in the login route,
in the login post route,
we probably shouldn't redirect to /admin,
but to /profile
because not every user who logged in
will be able to actually view this admin page.
For example, this user I just showed you
won't be able to do so.
Now if I log out with this user,
and I then log in with my first user,
who is an administrator,
then the profile page, of course, works,
but the admin page now also works.
Now I don't get the "Not authorized" error there,
simply because this user now is an administrator.
And that's how we can implement authorization as well,
simply by adding some extra logic
from our side to check for more
than just the isAuthenticated flag.
In this case, I'm also checking for the isAdmin flag.