Now that our API server can authenticate users, what identifier should we return to the client so they can attach it in subsequent requests? Generally, there are two types of identifiers:
- Sessions IDs: After the client has successfully authenticated, the server assigns this client a session ID, stores the session ID in the database, and returns it to the client. This session ID is simply a long, randomly generated text that is used to identify the user's session. When the client sends a request and supplies the session ID, the server searches its database for a user with that session, and assumes that the client is the user associated with that session ID. The idea is that because the string is long and random enough that no one would be able to guess a valid session ID, it's also long enough that someone is unlikely to be able to duplicate that session ID.
- Claims (tokens): After the client has successfully authenticated, the server retrieves information that can identify the user (for example, their ID, username, or email). If the system also supports different levels of permissions (for example, edit profile and delete profile) or roles (such as admin, moderator, and user), these should also be retrieved.
All this information, called claims (or a claim set, if there are more than one), is formatted into a standardized format and signed using a key, producing a token. This token is then sent back to the client, which attaches it to every request that requires authentication. When the server receives a request with a token, it will use the key to verify that the token originated from the API server and has not been altered. Once a token is verified, the server can trust the claims presented by the token.
We will use tokens over session IDs because of the following factors:
- Stateless: With session IDs, the server still needs to perform database reads in order to ascertain the identity and permission levels of a user, as well as if the session has expired. With tokens, all the information is contained in the claims of the token; the server does not need to store the token anywhere and it can be verified without interaction with the database.
- Reduced server load: As an extension of being stateless, the server would save a lot of memory and CPU cycles that would have gone into database reads. Furthermore, if the user wishes to log out of the session, all they need to do is delete the token. No actions are required on the server.
- Scalability: With session-based authentication, if the user logs in on one server, the session ID saved in the database on that server may not replicate quickly enough so that if a subsequent request was routed to a different server, that server would not be able to authenticate that user. But because tokens are self-contained, they include all of the information required to identify and authenticate a user. The user would be authenticated on any server that has the decryption key.
- Information-rich: A token can carry much more information than a session ID can. With a session ID, the server would need to read the database and possibly process the user data in order to determine whether the request should be carried out.
- Portable/transferable: Any party that has the token has permission to perform actions that the token allows, and tokens can be passed freely from one party to another. This is useful when a user wishes to grant a third-party platform limited access to their account. Without a token, they must give the third party their ID and password, and hope that they don't do anything malicious. With a token, the user, once authenticated, can request a token with a certain set of permissions, after which he/she can send it to the third party. Now, the third-party can perform the actions it says it will perform, without knowing the user's actual credentials.
- More secure: A session ID's security depends on its implementation. If the session ID can be easily guessed (for example, it's a simple incremental counter) then a malicious party can guess the session ID and hijack a legitimate user's session. With a token, the malicious party must know the key used to sign the token in order to create a valid token.
Therefore, using a token as a means of conveying user authentication information is preferred. But since a token is simply a set of claims in a specific format signed using a key, there are many standards available. Luckily, JSON Web Tokens (JWTs, pronounced "jots") have become the de facto standard for tokens, so the choice is a no-brainer. They are also formally defined in RFC7519 (tools.ietf.org/html/rfc751). We will use JWTs as the format for representing our claims in this project.