63

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
        }
    ]
Tom Stock
  • 1,098
  • 1
  • 12
  • 26

4 Answers4

44

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:

  1. Update the database
  2. 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.

Akar
  • 534
  • 5
  • 6
  • 1
    From my experience, if all your systems are using some central role and permission database, you can add all that into JWT. However, this approach might not work well in SSO scenarios when the auth server itself has no idea whatsoever about the target system that will receive and trust the token. It's especially true when you integrate SSO auth with JWT into some legacy systems that have already their permission subsystem in place and thus they need only one claim to be present in JWT - the claim of user identity. – JustAMartin Jul 19 '19 at 09:46
  • 1
    could you expand more... I thought the whole reason, "JWT" was invented was to "remove session", as session has memory limits and distribution limits... so putting in JWT is only option? then the options that i read usually, going back to session... I'm really trying to understand. Although i never quite find a detailed enough answer... articles normally just say pros and cons, but not how the problems are solved. Custom roles with dynamic permissions, how to implement. using either and why is the one better than the other, if the whole put is to more away from Session, due to its limitations. – Seabizkit Dec 03 '19 at 08:15
  • 2
    As having an micro service achitecture: There are small isolated services talking to each other. How would one know of a valid session? Each service would have to talk to an central instance each time Think of the overhead that is generated. With JWT: Everything you can trust is in that JWT. You elminate this hassle since only the first service has to do the update/invalidate and fetch. Other only havto check the signature if its valid. Where do you implement roles? At each service where specific rights are needed. Where to get all roles from? A central auth service the first servcie talks to. – Akar Dec 04 '19 at 23:02
  • 24
    i am surprised to see this answer as accepted as the answer didn't lead to any conclusion about where/how to manage the role-permission. how role permission is done in-memory or how the update is propagated over different services ? pretty confusing and incomplete answer – Zahan Safallwa Jan 11 '21 at 05:29
  • 1
    @ZahanSafallwa All your authorization would be centralized in a service which returns a token, containing the allowed permissions. Then the client can use the token for your different endpoints. Those endpoints can store the required permission either hardcoded or pulling from a shared package with the 'definitions'. You would be able to rollout/change permissions on every release. Hope it makes sense. – David S Apr 29 '21 at 11:58
  • Note with the JWT's in general if you are relying on them entirely for authorization they really need to be *short term* with a refresh token for reasonable security. If you treat them as a bearer token with no server state/database lookup, the user will potentially have access for as long as the JWT is valid. – JCP Jan 08 '22 at 22:37
10

Depends on how you interpret 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.

Felix K.
  • 14,171
  • 9
  • 58
  • 72
1

I struggle with the same problem.

I only store the user role in the Jwt token for the first permission check. So to check if the user is allowed to do a post,get etc. request.

For further permissions I use casl to check which fields the user can access.

CalculatingKraken
  • 651
  • 1
  • 9
  • 20
Patrick
  • 11
  • 3
0

This is a topic that has kept me following since I started building web applications. In the past I have also encoded all authorization (authZ) information in the JWT along with the authentication (authN) information. However, reading more and more into it, I am starting to doubt that approach. Modern authentication is done using some centralized authentication system with which users can benefit from SSO. Whichever provider you are using (Google, Microsoft, etc.), they do not care about the target system, hence how authZ is implemented. Therefore, it is my belief that authZ information should not be added to the token. The JWT with the identity of user gets send to target system and that system should validate what the user is allowed to access.

Good articles:

https://blog.joaograssi.com/posts/2021/asp-net-core-permission-based-authorization-middleware/

https://sdoxsee.github.io/blog/2020/01/06/stop-overloading-jwts-with-permission-claims

Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102