So let's now get started
with adding our own logic,
because as I said NextAuth won't do everything for us here.
When we roll with our own authentication system
with our own user accounts
and thereafter before we use NextAuth for anything,
we will first of all,
have to add the logic to create users
because NextAuth can then
help us with authenticating those users
and with getting the token for those users,
but to create the users themselves,
we need to bring our own logic.
Hence I'll bring up my dev server again,
and I want to add an API route,
which gets input,
which gets a request with an email and a password,
which in the end should be sent from the log in page,
if we are creating a new account
and then that data should be used to store that user,
and I will use MongoDB again as a database
for storing the user data,
but you can go with any database you want.
It doesn't have to be MongoDB,
but MongoDB
especially when using MongoDB Atlas
is just great to use,
free to get started,
easy to use with Javascript
and already hosted for us,
so that's great.
Hence we'll need to add an API route
and therefore this API folder in the pages folder,
and in that API folder, I'll then add an auth subfolder,
and in that auth subfolder,
a sign up JS file.
Now the file name is up to you,
and in there I'll then define my handler function
which will be responsible for creating a new user,
and we should of course,
as always export that function thereafter.
Now in here, we want to connect
to the database and store the incoming user data.
Hence I will actually quit the dev server one more time
and then install the MongoDB package again
as we also did before in the course already
and then restart the dev server
because we'll need that MongoDB package
to successfully connect to that database
and talk to that database.
We will actually also talk to that database
from multiple functions here
from multiple API routes later
and therefore to share some logic,
I'll add a new folder on my route level of the project,
a lib folder,
we could also name it, helpers, whatever you want,
and in there I'll add my DB JS file,
file name of course is up to you
and it's that file, which should help me
with establishing a connection and so on.
So here in this DB JS file,
I'll import from MongoDB,
and as before in the course,
I'll import Mongo client
because that's the thing
that helps us establish a connection,
and then I'll add a function,
connecTtoDatabase, could be the function name
and in there we'll do what the name implies.
We'll use Mongo client to call the connect method,
and then here pass our connection string
with which we connect to a database.
Now for MongoDB, we can get that string from
MongoDB Atlas with a created cluster,
and therefore you should make sure
that you have that cluster.
We did create that early in the course already,
so make sure that you don't skip those previous parts
where we talk to MongoDB already,
and once you've got a cluster created,
you can click on connect on your cluster,
connect your application,
and then it's this connection string
which we need and which we are interested in.
So I'll use that connection string,
and here we now need to replace
some pieces of data like the username, password
and the database here.
Now for username and password,
I will go to database access
and make sure that I have a valid password
for this user here,
create a new password and update it,
and then use my username.
You need at least one user with read and write access
created in MongoDB Atlas
and then add the password here,
and then for the database name,
I'll name this auth-demo,
but that database name is up to you.
That's now the connection string.
Now connect returns a promise and to use async await,
I'll add async in front of the function
and then await this a connection result here
or to be precise to connect the client
which we get back as a response.
Now this could be failing but for the moment,
let's ignore that,
and let's then just return that client here maybe,
so that when someone calls connectToDatabase,
that someone gets this client.
Now that function should be usable from outside this file.
So we'll export it,
and now back in the sign up JS API route here,
I first of all want to call connectToDatabase
and hence import this from that lib folder and the DB file.
Now I'll also turn this into an async function
to use await here,
and then here we get that connected client
and we can then get access to the complete database
by calling client.db() like this,
and now that we've got access to the database,
we want to create a new user
and store it in some collection of that database,
because as you learned before in this course already,
MongoDB works with collections,
collections of documents,
where a single user would be a document
and all the users would be stored together in a collection.
We get access to a collection
by calling the collection method on DB
and we can use any collection we want.
If it's doesn't exist yet, it will be created on the fly.
So here we could maybe work with a users collection.
Now to get the submitted user data
we should accept the request
and response objects here in our API route function.
You'll learn that API route functions give us access
to the incoming request
and then all the tutors' response object
to send back a response,
and then we want to extract the incoming data,
and actually we maybe want to do that first
so that we only proceed
as soon as we know that we have valid data.
So here, we then basically get our data
by accessing request body,
and then from there we get our,
let's say email and password,
and these key names are up to you.
I will extract them from the incoming request body.
We will just have to make sure
that later when we submit this form,
this auth form that we actually do add
email and password to the request
which we sent to this API route
when we want to create a user.
So thereafter now we got that,
and now we got email and password,
we definitely also want to validate it.
We want to check that we don't store invalid data.
So if we don't have an email
or let's say if not email includes an at symbol
or if we don't have a password
or if password trimmed for white space
has a length that is let's say smaller than seven,
so that we require at least seven characters
for security reasons,
if any of these conditions is met,
so if we have invalid data,
then I want to return to not continue
with function execution
and I'll send back a response
with a 422 status code
and some extra information
like a message where I say invalid input,
password should also be at least seven characters long,
something like this.
That's the response we sent back
if we got invalid input.
If we make it past this if check, though,
we know that we have valid input
and then we want to store our user data in the database.
So in that users collection,
hence here we want to insert a new document.
We can do this with insert one,
as we saw before in the course,
and we can create that document on the fly here
and simply add the email field
which is that extracted email we got here,
and then the password.
However, regarding the password,
there is something we have to keep in mind and take care of.
We should not store the plain password
like this in the database,
because if our database ever gets compromised
by some employee or a third party intruder,
then all our user credentials
would be usable just like this.
Hence for security reasons,
it is a best practice and strongly recommended,
that you don't store plain passwords in the database
but that you instead encrypt them
such that they can't be decrypted,
and we will still be able to verify them later, no worries,
but we want to make sure that we store passwords
in encrypted form
and to encrypt passwords,
we can install another third party package
with npm install and that's bcryptjs, like this.
That's a package which helps us encrypt passwords
or encrypt any kind of data of course,
but we'll use it for a password here,
and hence, I'll start my dev server again,
and now I want to encrypt my password
and for this in the lib folder,
I'll add another folder auth.js,
which will hold those authentication
related utility methods,
and here I want to import from bcryptjs
from that package we just installed,
and from there, I want to import the hash function.
Then I want to export an async function called hashPassword
and take the original plain text password as an input,
and then in there,
call hash on that password,
and the second argument then should be a number
of salting rounds,
which in the end will influence how strong that password is,
and here a value of 12 is considered to be safe.
The higher the number,
the longer this function will take,
the lower the number, the more insecure it is,
so 12 should be an okay value
and hash will then also return a promise
which eventually gets gives us that encrypted password.
So we can await this to have our hashed password here
in the end,
and then it's this hashed password
which I return just like this.
Now back in signup.js, we therefor now
want to create such a hashed password
and store data in our collection
instead of the plain password.
So here I get the hashed password
by calling hashPassword and importing that function
from the lib folder and the auth file there,
and then to hashPassword,
we pass our plain text password
and then it's this hashed password,
which we store in the document.
So in the collection overall.
Now insert one always returns a promise,
so we can await this
and this will return the result of this operation,
which then for example gives us access
to the automatically generated user ID.
Now with that, we're storing a user in the database
and now I want to send back
a response offer, a success response
where we say something like created user.
Now I'll not implement error handling here,
since I really want to focus on the core auth parts,
and we saw error handling with MongoDB and so on,
in action before already,
and hence, I'll keep this lean and focused
on the parts that are related to user creation in this case,
and then for now we should be able to test this.
In the next lecture,
let's make sure that we send the request
to this signup API route.