Definition of authentication and authorization
Authentication is the process of identifying a user, for example: "The user is Michael Knight".
Authorization is the process of granting that user the rights to the app, something like: "This user can do access locations but can not do seeing bank accounts".
About cookies and JWTs
Due to the stateless nature of HTTP protocol, the server doesn't know what the state of the client is. The client might make a request with the data to identify itself, but the next request will be independent from the previous, being clueless about the identified nature of the user.
Cookies and JWTs are the two ways we can make this interaction stateful.
Back and forward all the time
Both strategies require that every single request send some data for authorization purposes, so the server can identify the client and allow its app privileges.
What is then the difference between cookies and JWTs
You might have heard that cookies are stateful and JWTs are stateless. That might be the most confusing part to understand.
Both cookies and JWTs contain data. So, if they do the same, why one is stateful and the other stateless?
JWT are encoded but not encrypted. That means that anyone that intercepts a JWT will be able to see it's content. That is not what makes JWT significantly different from cookies. The main difference is that JWTs are signed (using a key). This way, if the content in a JWT changes, we will know it has been tampered. That is what JWTs have that cookies don't have.
There is some other differences which we'll talk about later, but this is the main difference between the two of them.
So JWTs can do everything a cookie does, is that right?
In essence, that is right. Cookies are just more convenient -and secure- for some cases. Which are those cases? Are you still confuse? Don't worry, it is normal, there is still the key piece of the puzzle missing here.
The real difference
When we say that we are using cookies or JWTs to manage the state of our application we are talking about strategies, not really about the actual cookie (which is mainly plain data) or JWT (also data after all, but with the capacity to warn us if it has been tampered).
Each one of those strategies uses a different header to send information to the server:
cookie-based strategy: Cookie: <name1>=<value1>; <name2>=<value2>; ...
JWT-based strategy: Authorization: Bearer <token>
Actually, it's fairly common sending JWTs in the "Cookie" header as a value. That confusion between the "Cookie" header and the "cookie" value which is send in the "Cookie" header but it can be a JWT is the most difficult to grasp. After you understand that, the rest is fairly easy.
Don't worry, we will explain now both strategies and its use cases.
cookie-based authentication strategy
Lets see it step by step:
1 - Client sends credentials.
2 - Server matches those credentials with (probably) the "Users" table on its database. Verifying the identity of the client.
3 - Server creates a session and stores it in the database. That session has an "id" that we will call "session_id".
4 - Server attach a cookie with session_id to the response.
5 - Response arrives to client, saving the cookie in the browser. This happens automatically.
6 - Client makes another request. The browser automatically sends the session_id value added to the "Cookie" header in the request.
7 - Request gets to the server, which reads the "Cookie" header, getting "session_id" value.
8 - Server checks that "session_id" corresponds to a session in the database. That session contains the id of the user ("user_id").
Now the server knows who the client is. Because this strategy requires of data being kept both at frontend and backend it is considered stateful.
Let me point out that there are some flags that must be set for the cookies, to avoid security problems:
httpOnly: Prevent client javascript from manipulate the cookie value. Useful to prevent XSS attacks.
secure: Assures this cookie will only be sent through https.
sameSite: 'Strict' value will assure that the cookie will not be sent to third-party apps. Useful to prevent CSRF attacks.
JWT-based authentication strategy
Again, let's see how to achieve the previous example using JWT-based authentication/authorization:
1 - Client sends credentials.
2 - Server matches those credentials with (probably) the "Users" table on its database. Verifying the identity of the client. So far it is the same than the "cookie" case.
3 - Server creates a token JWT containing the user_id data, then signs it with a (secret) key. This secret key belongs to the backend server and should never be reviled to anyone.
4 - Server sends this token as JSON in the response.
5 - Response arrives to client. Must save this token, as will use it in the next request to the server. A good place to store it is the window.localStorage or window.sessionStorage, which are features of HTML Web Storage API.
6 - Client wants to make another request. Before it must retrieve the token from localStorage (or sessionStorage). Adds this token to the "Authorization" header with the format: Authorization: Bearer <token> and sends the request.
7 - Server receives the request and retrieves token from request header.
8 - Using the private key mentioned in step 3, verifies that the token hasn't been changed in any way and reads the content of the token (user_id)
That is how the server knows what the identity of the client is using JWT-based strategy.
Data is self contained in the JWT -there is no session stored in the database-, that is why this strategy is considered stateless.
So far, which one should I use?
For the use case that we have seen so far, it makes more sense to use the cookie-based approach. Let's see why:
1 - It saves the client to save the token, then get it and then attach it to the header every time.
2 - As the the token is stored in local/sessionStorage, it can be manipulated by the javascript code present in the client. Obviously we won't want to to that, but it is a risk if someone has managed to introduce its own javascript code in our client using a XSS attack. This won't happen with cookies, as javascript can't read nor manipulate cookies (if they have the "httpOnly" set to "true").
Nevertheless, there is a case that might incline us to use JWT-based strategy in this case. There is a downside about cookie-based strategy we haven't mentioned yet:
Cookies make scale harder: Sessions are stored in a database. If the case of distributed systems, all systems must have access to the database. JWT won't have the same problem, as only uses a private key to verify. It's easier to share the same private key between systems than access to a database.
When should I use JWT, then?
It's a whole new world out there -well, actually it has been for a while, to be honest- and there is some new players in the field:
Mobile applications won't use a browser, so won't be able to use cookie-based authentication.
There are very convenient services (Google, Facebook, Auth0,...) that will lift the heavyweight of authentication and authorization for you, using standards like OAuth2.
Final note
You might have noticed that neither Cookie nor JWT approach are necessarily encrypted. To avoid exposing them to malicious parties, we rely on https connections, which will make impossible for anyone to take a peek to them, besides the client and the server. It's important to remember that neither of these two strategies are secure if the connections are not made through https.
Don't be shy, leave us a comment