JWT is an great way to make sure the data send to the user and back is not tampered with, but that makes for some tough choices. At the moment I am in the dilemma of choosing between storing the authorization data in an JWT claim and only touch the database once for the authorization, or just store the user ID and check the authorization levels on each request to the server with the database.
What makes this such a hard choice is that the application works with multiple authorization levels which makes the base64 encoded url quite long and bulky (see below what can be expected to be stored as authorization levels).
On the other hand, to get the authorization, two lookups in the database are necessary.
So my question is as following; 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?
As an sidenote; In the case of permission changes the look-up-in-the-database approach has the benefit of not requiring the user to log in again (see post).
"perms": { "roles": [ { "name": "Admin", "id": 1, "assigned": true }, { "name": "Webmaster", "id": 8, "assigned": true } ], "actions": [ { "id": 1, "name": "cms-edit", "parameters": null, "parameterized": null }, { "id": 9, "name": "admin-syslog", "parameters": null, "parameterized": null }, { "id": 10, "name": "admin-debug", "parameters": null, "parameterized": null }, { "id": 12, "name": "member-list-extended", "parameters": null, "parameterized": null }, { "id": 2, "name": "cms-list", "parameters": null, "parameterized": null }, { "id": 3, "name": "cms-add", "parameters": null, "parameterized": null }, { "id": 5, "name": "member-list", "parameters": null, "parameterized": null }, { "id": 7, "name": "member-view", "parameters": null, "parameterized": null }, { "id": 8, "name": "member-edit", "parameters": null, "parameterized": null } ]
Use cookies to store JWT tokens – always secure, always httpOnly, and with the proper same site flag. This configuration will secure your client's data, it will prevent XSS and CSRF attack and also should simplify web application, because you do not have to care about using tokens manually on frontend code anymore.
To keep them secure, you should always store JWTs inside an httpOnly cookie. This is a special kind of cookie that's only sent in HTTP requests to the server. It's never accessible (both for reading or writing) from JavaScript running in the browser.
jwt Getting started with jwt What to store in a JWTRegistered claims like sub , iss , exp or nbf. Public claims with public names or names registered by IANA which contain values that should be unique like email , address or phone_number . See full list. Private claims to use in your own context and values can ...
In a general case you would not need to keep user credentials in the JWT because the JWT is by itself a dynamically generated credential that represents the login / password provided at the JWT's first generation time.
Depends on how you interpretate efficiency. When speaking about resource efficiency, keep in mind that your JWT is transmitted on every request. So if you have a large application with fine grained access control lists (ACLs) and you always ping-pong these lists back and forth on every request-response then this will definitely cost you a few kilobytes depending on the number of requests you issue.
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:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With