1

I would like to introduce permissions based access control in my Single Page Application (SPA) front-end which authenticates the user with token based authentication (JWT).

Permission Requirement:

In my SPA, each required (html) element is mapped to a permission and depending on the availability of the user permission, the element is shown or hidden. Multiple elements can be mapped to the same permission.

Number of permissions: ~100

The problem I need to solve is:

How to efficiently pass permissions that control view and access of specific front-end elements from backend to the SPA.

I am thinking about two possible approaches with different options on how to implement this:

Approach 1

It seems that in almost all guides and examples on permission based authentication, the permissions are included within the jwt token:

  1. User logs in the web app
  2. The user is authenticated and the server returns a jwt token to the SPA.

    Option A

    The jwt token will contain one claim per permission.

    Option B

    The jwt token will contain one claim that will have as a value all user permissions comma separated or structured.

  3. The SPA parses the jwt token and gets the permissions.

Approach 2

The above solution does not sound efficient from a network traffic perspective so here is the second approach:

  1. User logs in the web app
  2. The user is authenticated and the server returns a jwt token to the SPA.
  3. As soon as the jwt is retrieved successfully, the client requests the permissions of the user in a separate request.
  4. Once the permissions are retrieved, they are cached in the browser session.

Questions:

  • Are JWT claims well suited for passing users permissions?
  • Wouldn't 100 claims be a large size to be passed around in a token?
  • Do you see any issues with the second approach except from the drawback of having to validate the cache if the user permissions change?
  • "The above solution does not sound efficient from a network traffic perspective so here is the second approach:" Can you elaborate on why you think this is so? It's unclear how an additional call to a server is more efficient from a networking perspective. – JimmyJames Jul 22 at 14:51
  • JWT claims can be well suited, if you provide a secure hash to prevent token tampering. Passing 100 claims can be excessive, do you really need that many? Biggest downside to approach 2 is the cache invalidation complexity since you have no natural way to know if the claims have changed. – Berin Loritsch Jul 22 at 15:04
  • Question: are you enumerating specific permissions, or simply setting roles/groups and any other attributes needed to validate permissions? All too often permission schemes can get too complicated when they don't change often enough to justify the complexity. For example, the rules for a user might be able to be packed into 3 fields (role plus other associated information) that restrict what is visible and not--if the permissions are baked in to the SPA. – Berin Loritsch Jul 22 at 15:10
  • @JimmyJames since the jwt will be transferred for accessing resources in all api requests, I would think that there would be an overhead in having all these permissions included in the toke. – pan Jul 23 at 9:18
  • @BerinLoritsch Right, my concern is although I don't have any arguments against jwt claims, if it is recommended to use claims for permissions or if there is another better way. Yes 100 permissions is a reasonable number as per the requirement since each button, element, table will have view/edit/delete permissions and the system will have a large number of pages >50 To your second question, I will be passing enumerated permissions since roles and groups can overlap and can be dynamically modified from user (a role can have different permissions at a time) – pan Jul 23 at 9:21
2

Imho you should opt for the more secure approach 1 - option A (see why) and reduce the permission count by creating groups and leverage the possibility of flags.

The client could have:

const accessMode = Object.freeze({
  none: 0,
  execute: 1,
  write: 2,
  read: 4
});

And combine permissions:

file.accessMode = accessMode.read | accessMode.write;

The server would issue:

Claims.FileAccess: 6

Or for TypeScript see FileAccess example here: https://www.typescriptlang.org/docs/handbook/enums.html#computed-and-constant-members

  • Great suggestion for data specific permissions, which I will be considering in my design. Groups is not an option in my case since permissions can overlap between groups and roles. Also groups and roles can be dynamically modified by users in my application – pan Jul 23 at 9:23
1

It sounds like JWTs are overly complex solution to a problem that doesn't exist, in this case. From what you described, this is just a presentation issue (that happens to be about security) as opposed to an actual security issue. All the code executed on client-side is untrusted, and you have to assume a client can access any bit of it at any time (eg: it's trivial to use F12 developer tools to make a field editable).

So to answer your questions:

  • No, JWTs aren't a good method for passing claims if you're only dealing with a single server
  • 100 claims could make the JWT large, depending on how you store them (eg: a named string for each would be large, a bitwise field would not be)
  • Second approach is fine (JWT or not)

Use of JWTs

JWTs are useful to pass claims between otherwise disconnected systems -- for example, server A issues a JWT to a client, and the client then presents that claim to server B. With a simple token (one-time password), this would require server B to contact server A to check if this token is valid. With a JWT, so long as server B has server A's signing key (usually public key), it can validate the JWT was signed by server A without having to do any secondary communication channel.

SPAs talking to Server

The simpler way is to return an array of permissions. It probably makes most sense to do this when initially logging in, but you could also do a separate call immediately afterwards. If the permissions are likely to change during the session, and you want that to be reflected in the app without requiring a refresh, you'll need a separate way to update the set of permissions.

Keep in mind you still have to validate any actions being performed from the server-side -- you can't do security enforcement on client-side, because that's outside of your control.

  • I completely agree with all your comments above and this is the solution I am leaning to. An additional thought is that the first approach allows me to easily map UI permissions to Api Resource Permissions/Policies in the backend. This means that permissions would be transferred in all requrest within the JWT, parsed by the API, and based on the permissionS, each webservice will understand if the user has access to a specific resource. If I follow the 2nd approach, I would have to check about user accessibility with another call from the target service to the identity service. – pan Jul 23 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.