Your first question:
Is the extra overhead on each request by sending the permissions to the server worth avoiding the hassle of looking up the permissions upon each request?
Answer:
Let’s have a look at the description jwt.io provides on when to use JWTs:
Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
That means that you need to generate the token on the server side once the user logs in.
It contains:
- The user (as an id or name)
- The roles the client has (user, admin, guest, whatsoever…)
Once the client requests or sends data to the server, the server checks first, if the given token is valid and known and then it would check if the roles meet the criteria for accessing a certain resource.
All of the role/access data can be read once at system startup and kept in-memory. Moreover, the roles that a client has are only read once from the database as the client logs in. That way you have no subsequent database accesses and thus, a great performance increase.
On the other hand, if the client requests data or wants to perform an action, you need an authentication mechanism that evaluates if the token that was passed got the roles that are required in order to do so.
That way we solved the database hassle, plus we eliminated the danger of exposing too much information to the client (Even the client can’t tamper the data, it can read the data!)
Note on that: https://jwt.io/introduction
Do note that with signed tokens, all the information contained within the token is exposed to users or other parties, even though they are unable to change it. This means you should not put secret information within the token.
See A3 (Sensitive Data Exposure): https://www.owasp.org/index.php/Top_10-2017_Top_10
Finally: Do also invalidate the tokens if the client is idle too long or logs out on purpose.
Follow-up question:
The case of permission changes the look-up-in-the-database approach has the benefit of not requiring the user to log in again
Answer:
Depending on your server’s infrastructure you can either write a refresh mechanism (if the role updates, the server generates a new token and sends it to the client along with the answer generated, invalidating the old, the client uses only the recent token and overrides the old) or add some state, like a client session, on server side:
Eliminate the roles/permissions at the token. You are better with generating a session for the client and feed the session roles/permissions on the server side. The client gets the session token (usually an id) it can authenticate with. Once a permission/role change we have to do two things:
- Update the database
- Update the roles/permissions of the session
Again, every subsequent request will do the role/permission check in-memory and needs no database communication, while the client has only a small session token (or your JWT). Thus, role/permission changes are transparent to the client (no relog is required) and we eliminated the JWT refresh requirement.