Okay, so now that we've got
this protection code here in place,
in the next step, we know that this request
is coming from an authenticated user
and we know that it's a request with the right method.
So now we need to extract data
from the incoming request, the old and the new password
and we will also need the email address of that user
because of course, in the database,
we store users with emails and passwords
and if we wanna change the password of a user,
we need that email address of the user as well
to identify that user.
Thankfully, getting that email address is not too difficult
even though it's not part of this form here
because we do encode the email address in our token.
We return that object here in our nextauth file
and that data ends up in the token
and therefore, it's part of that session, which we get here.
So we can get the userEmail
by accessing session.user.email here
and we can then also get the other data,
like the oldPassword by reaching out
to the request
and there the body field as we learned it.
So that's what we also do in the signup.js file
when we create a user.
There we also get the email and password
by diving into the request.body in the end.
And here in change-password,
we could, for example, expect that on the body,
we got a oldPassword field
and then we got the newPassword,
which we get from request.body.newPassword.
Of course, we'll have to make sure
that when we send the request from the client
to the server later,
that we have old and newPassword,
that we have these two fields in the request data,
which we attach to the outgoing request.
And with that, we've got all the data we need.
Now we wanna connect to the database
and find a user by that email
and then once we got that user,
we wanna check if the oldPassword is correct,
so if it matches the password currently stored
in the database
and if that's then the case,
we wanna hash the newPassword
and replace the oldPassword with the new one.
And for this, of course, we can call connectToDatabase again
and import that from our lib/db file like this.
And then here call connectToDatabase
and await this, of course
and here we then get the client.
And then once we got the client,
we can reach out to the database
and then there to the collection
that stores our users, which is the users collection here.
So here we then reach out to the users collection.
usersCollection.
I can store access to that collection in a separate constant
and then we can use that usersCollection to findOne user
by email as we did it before as well
in the authentication code.
There we also find a user by email
and now we're going to do the same thing here.
We look for the email field in our documents
and check if we find one document
where the email address stored in the document
is equal to the userEmail we get from the session.
Now, this will return a promise,
so we await that and then we'll get a user document
or not but if we somehow don't find a user,
something is wrong anyways
because that userEmail is coming from the session,
so it would be strange if we have a logged in user
and yet he or she is missing from the database.
But still, we can also handle that case.
Close our connection here
and then possibly also return some error.
This could be a 404 error
where we say user not found or something like this.
Now, if we make it past this check,
which we typically should,
we can now verify the password.
So we get the currentPassword
by accessing user.password.
So this user document, which we get from the database
and there, the password field,
which is this hashed password.
That's the currentPassword now
and now we wanna verify this
and for that, in the lib folder,
we have this auth.js file with the verifyPassword function.
And I wanna call this function now
and hence, we need to import verifyPassword from lib/auth.
We need to add this import
and with it imported, we can call verifyPassword
and pass in the incoming password,
so the oldPassword in our case,
as a first argument
and then the stored hashed password
to which we wanna compare it.
So this currentPassword here as a second argument.
And we then await this
because that returns a promise
and we get back the answer to the question
whether the passwords are equal or not.
And if they are not equal,
if not passwordsAreEqual,
if that is false, then we know
that the old password entered here simply does not match
the user's password.
And in this case, we don't wanna allow the user
to change the password, of course.
So here we wanna return.
We wanna also close our client connection.
And we wanna send back a response
where the status code could be 403,
not 401 as it was before but 403
where we say you are authenticated,
that's why it's not 401,
but you're not authorized for this operation.
You simply do not have the permissions
to perform this operation
even though you're authenticated.
And in this case, the user doesn't have the permissions
for this specific operation
because the passwords are not equal.
Of course, alternatively, we could also use 422,
which kind of indicates that the user input is incorrect.
And that would be a fair assumption here as well,
that the user simply mistyped the old password.
Ultimately, it's up to you which status code you wanna use
because after all, it will also be you
who has to handle these status codes in the front end code
when it comes to showing an error message to the users.
So here I'll go for 403
and then also attach some data with a message key
where I say invalid password.
Or something like this.
Now, if we do make it past this if check though,
then we know that the passwords are equal
and then we can replace the oldPassword in the database
with the new one.
Now, for this we can use our usersCollection again
and call updateOne to update one document
and for that, we now need to pass in our first argument
where we identify the document that should be updated
and in this case, that's the document
where the email matches the userEmail.
So the same filter as we set it up here for finding a user.
But then here we also need to pass in a second argument
where we describe the update.
Now, for describing the update,
we also pass in a JavaScript object
as a second argument
and in there, we add the special $set key.
This is a special key which will be identified
and understood by MongoDB
and $set then wants a nested object as a value
where we just set the properties of the document
that should change.
So in this case, for example,
I don't wanna change the email,
I don't wanna change the ID,
I wanna change the password.
So as a value for $set, I have an object
where I set password
and now this key here has to match one
of the keys of your document
or it's added as a new key if it doesn't,
which I don't want here.
So here I'll set password to the newPassword
but of course, as you learned, not like this.
Instead, we wanna hash that newPassword first
because if the password has changed,
we, of course, still don't wanna store plain text passwords
in the database.
And therefore here, I'll now create my hashedPassword
by calling hashPassword,
which is a function we have to import
from the lib/auth file as well.
And when we call hashPassword,
we pass the newPassword, the plain text password
as a argument and then await this operation,
since it returns a promise,
and we then get this hashedPassword
and that's then the password we wanna set
for the updateOne operation.
And this will now update the user in the database.
Now, we can also await this operation
and we can get the result and do something with it
if we want to.
Here I don't actually need it but still, I'll get it.
And we can also add error handling as we did it before
in the course but for the moment,
I'll ignore all of that.
I'll then just close the client connection
and then send back a response
with a status code of 200
where we, for example, say Password updated.
Like this.
And now with all that in place,
with all that code added,
we should have the code that does change the password
of an authenticated user
and therefore, now with all that in place,
we now need to go to the front end
and work on the front end code
to send a HTTP request to this newly added API route.