JSON Web Tokens (JWT) — the basics
Some of you may not be familiar with concept of tokens, so I’ll try to quickly explain those: Tokens overall is an abstract concept, of representing another object. For instance, you won the lottery, and you need to show the proof you actually are the lucky winner (congrats if you are!). What the proof would be? Most likely a voucher, right? Well, that’s the token in this case. You buy piece of software, and you got to input included serial code, while installation process. The serial would be the token — as the serial represents your ownership.
So just the same as on examples above, JWT is nothing else, but an object, representing your membership. In this case, token is in JSON format, so it’s easy to share some data with. You may ask why I choose JWT over cookies, and “how do you authenticate with them?”. Problem with Cookies is the fact, you can’t share them around. You got one device, that you’re logged onto on specific domain. You can’t share your session across whole domain, you need to log in twice, if your system is build on subdomains. Isn’t it painful? So tokens are the answer. They’re not strict about domain they’re used on — they might be, if that’s what you use them like. But don’t have to.
Authorising user with tokens? How do I do this?
Well, at first, I need to point out this question is put up wrongly, as there’s huge difference between authorising the access and finding user’s identity. Let’s start with authorisation.
At first, I shall mention, there are more, than just one way of doing this; You can use either some database or in-memory data structure store. Such as Redis. Then all you do is, you save session in your memory, you store session ID inside of your token’s payload, and you simply compare your session with token, client is passing to you. This way has definitely some pros, for instance — you’re able to create a blacklist, for users tokens you don’t want to be active anymore. Or you can treat your database as a whitelist — only sessions stored in the memory would be authorised then. But this definitely requires you to have additional database service, which makes your query response a bit longer. Maybe not long enough to make any sort of difference, but it’s worth mentioning.
Second way, the most widely used is simply depending on expiration date. JWT has a field, storing the expiration date, which more-or-less means “if current date is below or equal to token’s expiration date — it’s valid. If it’s not — it’s invalid”. Simple, at yet effective. Although this isn’t always a way to go. Imagine someone hacked onto your facebook account, and you’re aware of that. You change your password quickly, so the person won’t have access to it anymore. What would expect to happen? I would want the person to be instantly log off. But this will not happen, as the only authentication is based on expiration key. Therefore, hacker will still be able to do damage to your account. It doesn’t sound like fun, does it? So, this way of authentication should be used in parts of your application, where there’s no much damage to be done, and there aren’t any confidential data to be seen.
So, you’re now authorized to use the app. But how does it happen, so with simple token, the application is able to find your data? Well, it’s quite tricky, as there are at least two ways of doing this. Maybe even three, that are used widely.
One, the most simple one is simply passing user_id or any other key, you may identify the user by — inside of your token. In it’s payload. Another one, is simply passing all your public data, that you’d like to be used across the whole app. And the third one, is to not pass any sort of data, matching user’s document in the database, but instead, to store all your data, or data required to match with the database record, on your Redis session. As you probably already guessed, you need base your authentication on Redis server.
All those have their pros and their cons. For instance, big plus for passing just piece of data, that user can be found by is the fact, user will always have up to date details. Imagine, you log in from two different devices, and on one of those sessions, you change your username. What would happen if you would use your data from the token? Well, user would be behind. Good approach is to combine them both — to store public details in the tokens, but to still make the query, but from the front-end perspective, instead of fetching data while authorising (on the backend). Why? Because user will be able to make use of the platform while the data is being fetched, and once new data arrives — you may update them on the load. Therefore, User Experience gains on it.
You may wonder, “How do I know the token is valid? And how do I know whether the token isn’t modified on the go?”. Well, those are valid concerns, but the answer for those is really simple: secret key. JWTokens are signed with a secret on your backend. That way, every time someone tries to authenticate with a token that’s signed by different string, it won’t much, therefore user won’t get authenticated.
Same rule applies to modifying tokens — since the whole token is singed, it’s not possible to manipulate with the data, without knowing the secret key — because finally, you have to convert it back to the token format, and it would differ from the original signed token, so it wouldn’t match. Therefore — authentication process would fail.