We will discuss here the general rules followed by ActiveUI regarding authorization and authentication against the servers it communicates with.
No Security client side
Since ActiveUI runs inside a Web browser, its code is considered always inspectable and alterable. So it does not contain any security enforcement mechanisms and nothing sensitive, such as passwords, will be stored by ActiveUI SDK in the browser. All the authorization checks are done server-side. ActiveUI's goal is to provide a UI and logic to request the credentials from the user and use them for authentication of the servers. Nothing more is required.
Security only when Needed
ActiveUI can also be used to process and display static or public data. In these situations, the user doesn't need to authenticate against ActivePivot. This is why the authentication user interface is displayed at the last moment: when a server responds to an HTTP request with a 401 status code.
The underlying technology used to store credentials safely in ActiveUI and transfer them to the servers is JWT. It is recommended that you familiarize yourself with it: Wikipedia article and JWT website.
The JWT contains the ordered list of the user's roles. This is used by ActiveUI in some components to decide which roles to assign to objects that are created. For instance, bookmarks and KPIs will, by default, use the roles of the user in their permissions. This is just for convenience, since the server (Content Server or ActiveMonitor) will check on its side that the roles of the created content are allowed by the JWT.
By default, ActiveUI will use JSON Web Tokens for its authentication with ActivePivot, ActiveMonitor and the Content Server.
When trying to communicate with any of these servers, if ActiveUI receives a 401 HTTP response, it will use the first Content Server it knows of to generate a JWT.
This token will be stored in the local storage of the browser and sent as part of every request in the
Reuse ActiveUI Authentication
The API of ActiveUI can be used to call project-specific server-side endpoints that would share the same security as the ActivePivot server. To do so, the following code can be used as an inspiration and a template:
// Fetching the discovery and logging it const requestOptions = await activeUI.security.getRequestOptions(); const response = await fetch( 'https://activepivot:port/pivot/rest/v3/cube/discovery', requestOptions, ); const result = await response.json(); console.log(result);
For more documentation on:
fetch, read its documentation on MDN.
The content of
requestOptions depends on the
authentication-holder plugin implementation being used.
The interaction between the different actors of the authentication will be represented by sequence diagrams. In these sequence diagrams the Browser runs ActiveUI code so it includes it alongside the cookies and redirect logic.
The sequence diagram of the first authentication on the first launch is shown below:
The WebSocket part of the authentication will be described below.
The JWT produced by the Content Server will expire at some point (this is configured in the Content Server), triggering the token refresh process:
The rest of the process is the same as on first connect.
ActiveUI will use a single JWT to communicate with all the servers. This is how a user can enter their credentials only once and be able to connect to multiple ActivePivot servers. The constraint on the servers is that all security filters should validate with the public key corresponding to the private key that the Content Server used when generating the token.
ActiveUI will store the token in the local storage,so that when a user closes and then reopens the browser, the user doesn't need to enter their credentials again. It will store within it the username entered by the user so that when the token expires we can already fill the username in the "Log in" popup. A malicious browser extension or other person with access to the browser could steal this token, but could then gain server access only for the TTL of this token.
This TTL duration is controlled by the Content Server, and it is recommended to be that it should be approximately a day or half a day of work. A longer duration is not recommended because of the possibility of token theft. A shorter duration is also not recommended because token expiration requires the user to enter their credentials again which would be not user friendly,as it would happen many times a day.
The password entered by the user is handled briefly by ActiveUI to create the
Authentication header (using Basic Access Authentication) to retrieve the JWT.
After that, the password is not kept anywhere so no extensions or other user could steal it.
Cloud Security (Keycloak)
The cloud security relies on Keycloak. Among all the features that Keycloak includes, those that are important for ActiveUI are:
- that it follows the OAuth2 protocol.
- that it uses JWT.
ActiveUI interaction with server security is quite different in this situation. ActiveUI is served by a server with a Keycloak compatible security filter. This means that the user must log in before using ActiveUI. ActiveUI will then work with Keycloak JWT.
The steps in italics are implemented in ActiveViam products, either ActiveUI or ActiveViam web components used in ActiveCloud. The other steps are part of Keycloak server and the security filters it provides.
The above process has quite interesting properties:
- Almost all the steps are pure Keycloak implementation, following the OAuth2 protocol.
- There are many more steps than there are in the pure JWT implementation. This is because this protocol has more capabilities than a pure JWT-based implementation such as the default behavior.
- No ActiveViam components (neither ActiveUI nor ActivePivot nor the Content Server) see the user credentials at any point. They all work solely with the JWT produced by Keycloak.
- There is more than one token. There is a Single Usage Token, an Access Token, and there is also a Refresh Token that is described below.
- The Access Token stored by ActiveUI at the end of the process is sufficient to connect with every ActiveViam server, with the right roles. This means that all servers that this ActiveUI will connect to should validate in their security filters the Access Token produced by Keycloak. They should therefore have the public key corresponding to the Keycloak private key in their security configuration.
Next Authentication Shortly After
The next time the user opens the ActiveUI application while the S2 cookie is still alive (basically after having closed and then reopened the browser, see Timeouts), the following process starts:
The session mechanism of the Content Server is sufficient here. There is no need to communicate with the Keycloak server.
If the user reopens ActiveUI later (basically the next day, see Timeouts), the Refresh Token mechanism of OAuth2 will be used. The Refresh Token is also a token generated by Keycloak and stored on the Content Server (it is quite sensitive, so it should never go client side). The goal of this token is to recreate an Access Token from it from time to time. On almost every request to a server, the server will quickly check the validity of the Access Token. Once in a while, when the Access Token expires, ActiveUI and the Content Server will reach up to the Keycloak server to check if the user has not been removed in the intervening period. The Access Token acts like an authentication cache that can be checked very quickly, the Refresh Token is more expensive to check but more powerful.
Request on Server with expired Access Token
We describe now what happens when ActiveUI performs an HTTP request on a server like ActivePivot or ActiveMonitor and its Access Token has expired:
It can be seen that in this situation, Keycloak is asked to produce a new token and can deny the request if the corresponding account was terminated in the meantime. In the above situation, everything goes well. On the other hand if the user has been disconnected, we will have the following sequence:
When the Keycloak server tells ActiveUI (via the Content Server) that the user is no longer connected, ActiveUI will open a pop-up containing the Keycloak login form. Once the user has validated the form, ActiveUI will again be able to ask the Content Server its Access Token and resume its operations. During all these steps all HTTP queries are paused and will resume transparently with the new credentials.
A Keycloak authentication flow uses the following authorization holders that each have a different timeout:
|Access Token||Connect to ActivePivot, ActiveMonitor and Content Servers||15 minutes|
|Refresh Token||Recreate Access Token when it expires||30 days|
|Content Server Cookie||Find Access Token||15 minutes|
|Keycloak Cookie||Reconnect to Keycloak when Access Token expires to find Refresh Token again||30 days|
|ActivePivot Server Cookie||Bridge between REST and WebSocket security||1 minute|
These recommended TTL are the consequence of the following observations:
- The Access Token can have a short TTL since its refresh does not require any user interaction.
- The Access Token TTL should not be too short since it still requires multiple HTTP calls and Database reads when it expires.
- The Access Token should not have a too long TTL otherwise users could continue to use the system for a long time even though the administrator banned them.
- It would be too expensive to ask Keycloak on each call if the Access Token represents a user that was not banned, so the Access Token cannot be revoked.
- The Refresh Token can be revoked since we request it from Keycloak everytime we use it.
- The Refresh Token never leaves the Servers so it cannot be stolen and can therefore have a longer TTL.
- Cookies should have a similar TTL to the token they carry otherwise they would maintain sessions with expired tokens.
- Keycloak invalidates the sessions when a logout happens so it is safe to give a long TTL to Keycloak cookies.
- The ActivePivot Server Cookie usage is always very short so it is unnecessary to give it a long duration.
When Keycloak is used, ActiveUI doesn't even persist the Access Token it uses (since the cookie is sufficient). The Content Server stores the Access and Refresh Tokens in its sessions.
When used with Keycloak, ActiveUI never sees any credentials. All the login forms are served by the Keycloak servers via redirects so no ActiveViam components have access to the credentials.
Implementation and Configuration Details
The above documentation was relevant for REST calls, but WebSocket authentication is quite different.
This is due to the fact that the WebSocket Client API does not allow ActiveUI to send an
Authorization header when opening a WebSocket, which greatly limits our authentication mechanisms.
We can only rely on the cookies to authenticate WebSockets. So we solve this by first performing an HTTP request with a JWT. This request will create a session on the target server that we will then be able to use for our WebSocket. This gives the following sequence: