So how does authentication work in general?
And then also specifically in React
and the Next.js applications.
The core idea is always the same.
We have our client,
so our browser and the user visiting our page
through that browser and our server serving that page.
The server also is connected to a database
where for example, all our registered users are stored.
Now let's assume a user did already create an account.
And now that same user visits our page and wants to log in.
How does that work?
Well, the user enters some data into a login form
like this form here for example,
in the log-in mode.
So the user enters some data here and clicks log in
When that happens,
we send the request with JavaScript to the server
our request which contains the user data,
email and password for example,
then on the server, we can validate that input.
We can reach out to our database
and see if we find an account for that email address.
And if the entered password matches the password
we find in the database.
And then we sent back a response
which basically says, yes, your credentials were correct.
You are authenticated
or which declines the authentication request.
And then once we sent back that yes or no response
though it's a bit more than that
as you will see in a second.
But once you send it back that response
the client can there after also reach protected routes
and send requests to other endpoints
on our server to our URLs on our server
that might require authentication.
Like for example that profile page.
We're only able to reach it once we are authenticated.
And we also only can change the password if we are.
So if that request is sent to some backend API
that the old password should be replaced with a new one
that kind of request should only be allowed
and fulfilled if the user is authenticated.
So that request which we send here
needs some extra data attached to it
some extra data attached to the request
which in the end tells the backend
that this user is authenticated.
Because anyone could be sending requests to our API,
and hence the request needs to carry some extra permission
which then should be verified on the server.
And that permission part is important.
Because a simple yes or no sent back by the server
to the client would not be enough.
Because we can of course always send a request
to some API and point that requires authentication
and say, "Hey, I'm authenticated."
We can't just believe that as the author of the API
we need some proof that a user is authenticated.
And therefore authentication involves
this exchange of credentials for permission.
But the permission is more
than just a yes or no.
To be precise we can't just save
and use a simple yes,
because of fake yes, could be sent.
We can't just tell our server that we are authenticated
and then request protected data
or perform protected operations.
And therefore, instead we need some proof.
Some proof which con to be faked.
And for this, we've got two main mechanisms.
We got server-side sessions and authentication tokens.
Now these are two common approaches
for solving this problem of unfakable permissions.
The concept of server-side sessions
works such that we store some unique identifier on a server.
So we generate some session ID, which is unique.
And we store that on the server,
for example, in a database.
And we sent that same identifier to the client
that sent us the credentials.
Then on the client, we can store that ID that identifier.
And then we can use that stored identifier
to attach it to requests which we there after are send
to protected resources on the server.
And then the server is always able to extract
that identifier from the incoming requests
check if it's stored that identifier in the database
and if it did grant access,
and if it does not deny access.
Now then only clients
which have does identifier are able to send requests.
Now you might say, okay, but can this identify be stolen?
Well, whilst it's in transit it shouldn't be stolen
because we should be using SSL
for encrypting to connection anyways.
And when it is stored on the client side
it is stored in a cookie typically
and that cookie can be configured
such that it's not accessible through JavaScript
to prevent against cross-site scripting attacks.
And that it is only readable from the server
when it is attached to outgoing requests.
And even if it would be accessible through JavaScript
you should be protecting
against cross-site scripting attacks anyways.
And if you can rule out cross-site scripting attacks
your authentication permission stored
in some client-side storage is pretty secure.
Because only you, the owner of this computer
and the user of this browser are able to view it.
So you could only hack yourself
and some random dude wouldn't be able to just send
a fake request to the server
because whilst you could come up with a random identifier
the server would not know that identifier.
So that's the concept of server-side sessions.
The other concept is about authentication tokens.
It's not that far off.
The idea here is that the server does not store
any identifiers.
Instead, the server creates and signs tokens.
Which in the end are just a random strings you could say.
Random strings which can be unpacked to data packages
so we take various pieces of data
and sign them together more on that in a second.
And then such a token is sent back to the client.
The client can then save that token
and again attach it to outgoing requests
to tell the server that access should be granted.
Now, even though the server does not store
that token in a database or anywhere else
the server knows how it's signed that token.
And I'll come back to that in a second.
So the server will be able to verify
if a token was created by it or not.
And therefore, again if I just come up
with some random token string
and I send this to a server to get access
to some protected resource access would be denied
because the server would not be able
to verify my random string.
Because it wouldn't be a validly signed token
generated by that server.
Now these are the two approaches we have.
Now when we build a single-page application
which we in the end still kind of do with Next.js
then we typically work with tokens instead of sessions.
And there are reasons for that.
For one, pages are served directly and populated
with logic without necessarily hitting the server.
Of course, when working with Next.js,
you can build pages
which use get server-side props
and therefore there will be a request handled
by the server every time that page is being served.
But you will also have many pages which are pre-generated.
And once the user is on your website
many pages won't be fetched from the backend at all
but instead will be loaded
and generated dynamically with front-end JavaScript
because you still have a single-page application
at least for many pages that make up your website
after you initially loaded your first page
then still JavaScript takes over,
React takes over and handles your website.
So that's important.
You don't send a request for every page you visit
because you're not using get server-side props
on every page.
So to server, doesn't see every request which you send
and therefore you load pages without the server
being able to directly find out
if you are authenticated or not.
In addition, backend APIs which be used
for a single-page applications are typically stateless.
They don't care about the individual connected clients.
They don't keep track of all the connected clients.
Instead, the idea is that,
that API can work pretty much on its own.
And it just is able to hand out permissions
to clients who authenticated so that they can then
later request access to protect the resources.
The API itself does not store any extra information
about any connected clients.
And since this is how we build single-page applications
the server is not involved in every request
and every action that's happening on our page
because we handled that with front-end JavaScript
and we have that stateless API connected to the SPA.
And because of all of that,
we could say that we have kind of a detached front-end
and back-end combination.
They do talk to each other sometimes
but not for every action that's going on on the page.
And because that's the case, servers don't save information
about authenticated clients.
And instead clients should get that standalone permission
which allows them to prove that they are authenticated.
And that's why we use these tokens.
And here specifically,
we use a concept called JSON Web Tokens
that is simply the most common form
of authentication token.
And it's simply describes the way the token is generated
though we don't need to care about that too much
because we'll use a third-party library for generating
and signing that token anyways.
So as it seems, these JSON Web Tokens are important
and therefore we should take a closer look at them.
We should understand how exactly JSON Web Tokens work.
And in the end that JSON Web Tokens
is generated with three main blocks.
We got some issuer data.
Some data which is automatically added into a token
by the server when that token is generated.
That's some metadata which we typically don't set up
ourselves as a developer,
but which is pre-configured by the third party packages
we use for generating tokens.
We can then still also add custom data to our token
for example, some information about the user
and then very important,
we also set up a secret key.
We set up that key on the server.
The client never gets to see that key.
And that's important because if you have that key
you can create valid tokens,
which are accepted by the server.
Hence only the server knows that key.
Then we generate a token with some third-party package
by combining all these pieces together
by creating a random string that incorporates all that data
and is signed by that key.
And that is that JSON Web Token,
a long string.
The important thing here is that
signing does not mean encryption.
The JSON Web Token is not encrypted.
You can unpack it and read the data inside of it
without knowing that key.
That key only proves that a given server created that token.
But you can read the content of the token
without knowing that key.
The key of course will not be included in the token though.
So you can't read that key,
even if you unpack the token.
Now is that token which then is stored by the client?
Sorry. By the browser?
And which then is attached
to requests two protected resources on the server.
So two protected API routes.
And for example, if you wanna change our password
we don't just send the old and the new password
but we also include that token in the outgoing request.
And that token then is validated by the server
which basically checks...
Okay. If I would use my signing key which only I know
would I be able to generate this token?
And if the answer is, yes.
The server knows that it's valid.
If the answer is no.
It's invalid and access is denied.
So that's why we can't just generate a token
without knowing the key.
We can't generate a token,
but it would be an invalid one.
And that's why this is a secure mechanism.
Okay. That was a lot of talking
about of all of that,
but that's the important theory.
The foundation we need for then actually diving
into authentication and implementing authentication.
The great news is,
that we don't need to build all these things from scratch
but that instead for the token creation
and validation and so on,
we can rely on secure third-party packages.