2

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?
Robert Harvey
  • 200,592
panda
  • 171

2 Answers2

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

2

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.

gregmac
  • 857