Simple introduction to authenticating with JWT, JSON Web Tokens, Access tokens
This article is about authentication, the difference between sessions and JWT, Json Web tokens, and how the structure looks like.
What is basic user authentication? Simple. The user creates an account, then logs into an application by entering their email and password. Then they can enter the site, and view more pages without logging in again.
Before JWT, in the past, yes, users would still login to the app with their email and password, the server will verify the credentials and will store a session id for that user in the server’s database. This ID will contain a reference to a table with more info about the user, when they logged in, session expiry time etc.
The server will give this session ID to the user, the first time. Then the user would send this session ID (which can be stored in the browser 🍪) to the server, to authenticate instead of sending their username and password again and again with every request. Then the server will lookup the session ID and see if it is still valid. If it is, it will say yes to the request. ✅
As long as it has not expired yet, the user can keep sending requests with this session id. But every time a session ID is sent, the server needs to check its own records for that session id, in a database. This means the server store the session ids is the only server that can authenticate the users requests.
That is a problem when you are scaling, with multiple servers, and a load balancer. 😥 This means that if your load balancer directs the user to a different server, server B, well then that server might not know about that session ID, since the session ID was first stored on server A.
🖥 🖥 🖥
So to solve this problem.. JWT came into the picture. Users still login with their username and password, but what happens after is slightly different.
With JSON web tokens, users still would login to the app, with their email and password, but instead of receiving a session id from a server, the server will send the user a JSON web token. Think of the JSON web token as an encoded 🔏 string that has information about who the user is, and when the token expires. The user would need to send this JSON web token with every subsequent request after logging in. Then any server would know who that user is and let them in. For example the JSON web token could have information like:
- user id
- expiry time
This token is not stored on the server that issued the token!!! But you might be wondering, hmm. I guess this is easy to hack, I can make my own token and encode it with the same information, and pass it to the server to get in.. 🤖 The server wouldn’t know, since it never stored it on its records.
Dun dun dana…NO. ⚔️ the JSON web token is built in such a way that, it does not need to be stored on the server that issued the token. This is because, before the server sends out the token to the user, it autographs it. 😉 🖊
This is more formally called “signing the token” . This is how the token becomes impossible to re-create unless it knows the secret autograph. When the server signs the token with something called a “secret” key, unless a hacker knows what that secret key is, they cannot make the same token.
Ok, you might be wondering, how does this mysterious token look like. When it is encoded, it is just an arbitrary string of characters, separated by dots. There are three parts
When it is decoded, it will give you information about the algorithm used to make the token, info about the user (payload), and the type of signature, without revealing the secret.
More formally, there is going to be a header which describes the algorithm (ex. HS256) you used to encode your JWT token, and the payload which is the information you are storing on the token, like the username, user id, token expiry, etc. Any data can be here. Do not put sensitive data in here.
Then lastly, is the signature, this is how all your servers will verify that your JWT token is actually valid. The signature requires a “secret key”, to create its signature. If the payload part in the JWT has been changed or messed around with, the signature immediately becomes invalid. Other servers can only generate valid tokens if they found out our secret key 😱, that is why you must store it securely.
Note: The JSON web token is not encrypted. So any information that we put in the token is still readable to anyone who decodes the first two parts of the token. So important not to put in the Payload any user information that an attacker could leverage directly, like user password, DOB.
After getting this token then, the user takes the signed JWT and starts sending it with each HTTP request. The JSON web token, acts as a temporary user credential (until it expires). Without forcing the password to be sent each time with the request.
Whenever the user wants to access a protected route or API, the user should send the JWT, typically in the Authorization header using the Bearer schema.
Authorization: “Bearer <token>”
The server’s protected routes will check for a valid JWT in the Authorization header, and if it exists and is validated, the user will be allowed to access protected resources.
More on the secret key..
The secret key is a significant part of how your tokens will be generated. The secret key will be an integral part of the signature. That same secret key will also be used to verify and validate an incoming JWT token. Therefore the server issuing the token and servers validating the token need to get ahold of this secret key.
The signature portion of the token is composed of = secret key + payload + header
The token can only be issued by knowing the secret key. So if you change the token contents, without knowing the secret key, the signature will become invalid, therefore the JWT token will be invalid. The server would then reject your request.
If you are using distributed systems, then all your services need to know this secret key. This can be risky, since the secret key can be compromised.
Therefore, it might be a good idea to use RSA Keys.
RSA Keys, where you get two keys, as a pair, one private key and one public key where both are generated using cryptographic algorithms. The private key is only used by one server to generate new tokens.
Then the rest of the servers, that need to validate or verify the token is valid, can use the public key.
Public keys, to decode the token you don’t need the secret key.
Ssh-keygen can be used to, to create an RSA key.. It will create public and private key.
- You have a private key: Jwt-key
- And a public key: Jwt-key.pub
Now any server that needs to verify tokens will use jwt-key.pub and this file can be shared around. The private key on the other hand should be protected with your life.
Now only when you need to generate jwt tokens you can use the private key.
jwt.encode(payload, private_key, algorithm=[“RS256”])
To decode and validate jwt tokens, you do not need the extremely secure private key, you can use the public jwt-key.pub, which is encrypted.
jwt.decode(token, public_key, algorithms=[‘RS256’])
Now anybody can verify the token, but nobody can generate the token! 😉