1

I have a SPA which at the beginning of application startup calls the backend API, sends a JWT and asks for the access permissions of the current user. SPA then caches the permissions in-memory and uses them to check what the user is permitted to do and clears the cache when user takes an action after which the cache might become invalid (out of sync with the backend), like buying something which gives more permissions.

This worked great but now there is a new functionality which allows someone else (not the user which is currently browsing SPA) to grant (or remove) permissions for other users. This is not an issue for the backend because when a new permission is granted for user A, the backend state is immediately updated and subsequent request would check if user A has the required permissions to execute that action. The issue is that the SPA does not know that user A has a new set of permissions since the old ones are cached. That means that even though from the backend's perspective the user is allowed to execute new actions, frontend thinks that the user does not have required permissions and will prevent user from doing that action and will show an error message. This mismatch between frontend and backend would persist until user reloads the page.

  • User A is browsing the SPA website User A asks, let's say an admin, to grant him a permission to do X
  • Admin grants user A a permission to do X (now backend allows user A to do X) and informs user A of this
  • User A tries to do X but the frontend permission cache says that there is no permission for X and so an access denied error is shown

Any suggestions on how this can be handled? One idea is to have a list of users which need their frontend cache invalidated stored in the backend (which is a single machine). With every request, check if the user who is executing that request is in that list and if the answer is yes then "cancel" the request and return some HTTP status code which would indicate that SPA cache needs to be cleared to continue. SPA would know how to handle this response code and would clear the cache, reload the current page (or redirect to home page, or something else) and would repopulate the permission cache. This approach would seem to work but it feels somewhat hacky and complex so it would be great to hear some more insights.

3
  • 2
    Have you considered invalidating the token of the user whose permissions are being updated, as opposed to keeping a list of these users and only deciding to block their token when they send a request?
    – Flater
    Jun 14, 2019 at 9:10
  • 1
    Changes on authorization rules must be followed by the log out and the subsequent log in. In other words, you should be able to invalidate the token. A 401 usually means you have to go back to the log in page and authenticate yourself.
    – Laiv
    Jun 14, 2019 at 11:43
  • Why not just require the user to refresh to gain additional privileges? Nov 5, 2019 at 19:37

4 Answers 4

1

Usually, when you work with JWT tokens, the permissions are baked into the token. So you invalidate the token as soon as the permissions change.

Whether the user needs to log in again or you find some comfortable way to swap tokens when one gets invalidated is up to you and how much work you are willing to invest.

1

You may be able to handle this closer to the network transport layer. A request to the server where permission should be denied should return a "403 Permission Denied" response.

If you have a way of tracking "handlers" for certain kinds of responses, you could have one global "handler" for the 403 response, that throws up a modal window saying something like "Permission denied." and give them a "Reload" button, which reloads the page.

If the user has any unsaved changes to data that are not persisted to the backend they will end up losing that work.

You could take it one UI widget at a time to limit this affect. Reload just that UI widget up to and including removing it from screen if the user no longer has any permissions for it, but that will require extra coding.

If you go with the global 403 "handler" you can also register a global 401 "handler" to opens a login modal over top of the page. That way an expired user session doesn't cause the entire page to reload, and have them lose their work. Then you need to handle logging back in and the requisite UI updates if permissions have changed between user sessions — still more coding.

Basically, being nice to the user after permissions have changed is not an easy task for a single page application (or GUI desktop application for that matter).

0

A solution I have been designing, but for this your SPA requires a BFF (backend for frontend) like asp.net core mvc as its host.

The SPA Access token only consists of user and access claims and the Roles of said user. The SPA also on initial load performs a API call to fetch the current access policies for the roles the current user is using along with his current access token. Permissions check are then evaluated at browser side based on collection of roles which relates to a flat list of permissions.

When a role is updated by a different user only the current permission cache of the application needs to be refreshed since the policies on the API's would already be up to date.

In order to make this work SignalR is used to push changes to the browser. If a role is changed as a post delegate to the save, a method determines which users are linked to the role with a change, which of them have current active connections, and it broadcasts with Signalr that the permission cache has changed which the SPA will then re fetch its permission cache and the UI checks would be updated.

For a user with a role change you only need to push the change to the one user if he has an active connection. And the user would perform a call to request an updated access token from the BFF.

Also note the SPA communicates to the API via a Bearer Token or access token but with the BFF it relies on Session cookies in my case which is generated after successful authentication with my Openid connect provider. My SPA does not handle the authentication and tokens on the browser but lets the BFF act as a confidential client to handle those movements between Identity Provider and Application.

Note if the user is currently on a route and his permission has been revoked the SPA would need to after updated the permission cache issue a reevaluate route check or method so it can handle revoking the user's access and rerouting him to component or default area which he has access to.

0

How about using push notifications?

I've created apps hosting a chat client coupled to a message hub. It's a perfect medium for sending event notifications from server side asynchronous processes or client instances of the app. When the client side chat listener receives a message that is identified as a system message, that message triggers the app to make an api call, to perhaps, reload a store after refresh of the token.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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