Common flows
Brief description of common flows of the framework.
Basic approval flow
All client initiated operations against the ZeroKit API (beside encryption, decryption and login) must be supervised (approved or rejected) by the backend service of your application through the administration API of ZeroKit. This gives you great control over the user activity on the encryption platform. The other reason for this is that the ZeroKit platform does not provide detailed ACLs (that is the responsibility of your application), the granularity of ACL that ZeroKit provides determines whether a given user can or cannot access the right credentials for encrypting and decrypting data.
A typical user-initiated flow against the platform consist of three steps.
- Initiation: The user does something on your web UI (for example: shares encrypted data, which triggers an invite to one of their tresors) which needs an operation against the ZeroKit API. Your app logic first checks if the user can do the given operation, and then calls the ZeroKit SDK, which executes the needed cryptographic operations and the init tasks against the tenant server. The requested operation is now prepared on the platform server but not effective yet. The SDK returns a unique identifier which can be used to refer to this operation.
- Backend notification: The client-side part of your application notifies your backend service about the initiated operation and also sends the operation identifier to the backend.
- Supervision: Your backend service can query the encryption platform for the details of the operation (by its identifier) and then it can decide to approve or reject it. The operation is completed when the backend successfully completes the operation by an approval or rejection. Then the client can be notified that the requested operation is done. For most operations early approval is critical, as approving other operations against the same resource may invalidate every other pending operation. You can see this flow on the next figure.
Communication flow of the client-initiated operation
Note: In some cases the approval can fail due to concurrently approved / rejected operations against the same resource (tresor, user, invitation link etc.). This behavior occurs because the client-side encrypted nature of the platform. (The client-side encrypted structures must be re-created by the client because the server is unable to modify or merge (or even to understand) what is encrypted by the client). In case of a failure, the whole sharing process must be retried from the beginning. (The client should re-initiate it again.)
Registration
The registration flow is the first process that end-users will encounter, and also the most complex one under the hood because of the involved cryptographic operations. Fortunately the ZeroKit SDK makes it fairly easy to implement it.
As ZeroKit offers client-side encryption, its main goal is to prevent any 3rd party code (even the code of your web application) or attackers from accessing the user's password(s) and key(s). This is achieved by iframes hosted by the tenant server. These iframes handle different processes (e.g. registration, login etc.), and they can be integrated into your page and customized by your CSS. The user interface of the registration iframe contains only the password boxes, but internally it encapsulates the complex client-side encryption logic. As all browsers separate the hosting webpage and the embedded iframes completely, there is no way to reach sensitive information from outside of the iframe. The iframe is controlled by the ZeroKit JS SDK through a messaging protocol, so your webpage only has to communicate with the SDK.
The registration form
The registration form has to be assembled from the registration iframe (which contains two password input fields), your desired custom fields (like email, username, address etc.) and your CSS to format the page. If the page is assembled correctly, the user will see no difference between your input fields and the embedded password fields.
Example registration form
Communication flow during registration
The standard one-step registration flow (submit reg form and done) has to be separated into three background steps to ensure maximum security and control over the system. If you understand the sequence, this will be quite easy, and users will see no difference form the standard flow.
Steps:
- User fills out the form (user data and password fields) and clicks on "Submit".
- The submit logic has to be implemented by you in JavaScript using Ajax calls. First, the fields of the form (except iframe/passwords) have to be sent to your application backend. Your backend then can inspect the fields and validate the data.
- If everything is fine, the backend should make a call to the ZeroKit backend through the administration API to initiate a user registration. (Note: the call from the submit logic to the application backend is unanswered yet!) The platform server will send back a generated user ID, a registration session ID and a registration verifier secret on success. The application backend should save all user data along with these three fields.
- Now the backend can answer the submit call by sending back the freshly generated user ID and registration session ID to the client.Warning! Never ever send the registration verifier secret to the client side. This value is security-critical to the process.
- When the submit logic receives this answer, it can call the SDK to complete the registration. (Internally the SDK will send the user ID and the registration session data along with the prepared encrypted data to the ZeroKit backend). After this call completes, the user is registered in both your system and the encryption platform. The call also returns a secret value - called validation secret - which should be sent and stored along with the user data at your backend.Warning! Albeit the user is registered, it is still not active, as the registration has to be validated. Tresors can be shared with unvalidated users, but they cannot log in to the system.
- The validation will be done by an admin call from your server backend to the ZeroKit server. This call needs the registration session ID, the registration verifier secret and the validation verifier secret as input.a) These secrets are returned by the previous calls to the ZeroKit backend and SDK. They ensure that the process cannot be tampered with by a 3rd party between the steps.b) (Optional) If you want to verify the identity of the registering user by any out-of-band method (such as an email or SMS) you should do that validation at this point - before the registration is completed by the SDK and before your backend validates the user registration. (An out-of-band verification is strongly recommended, but if you do not want to implement it, you can validate the user immediately after the registration completion.)c) The actual validation is done by an admin call from your server backend to the ZeroKit server with the registration session ID and the verifiers.
The described process consists of four main parts: 1) user fills out the form and sends data to the app backend, 2) app backend checks data and initiates a ZeroKit user registration session 3) client side logic finishes the registration 4) User registration is validated by the application backend (after an optional out-of-band validation.). Notice that the first three steps are the same as the previously described client-initiated flows, the only difference is the additional verification steps. The following diagram will help you to understand the flow:
Steps and data flow of the registration process
Login
The login flow is also a slightly different from the conventional one. Login is the process when the user proves his/her identity by providing a secret (in this case the password). As the login page has to be hosted by the integrated web app and the system should also maintain its zero knowledge nature, the secret must be entered to the login iframe provided by ZeroKit. The iframe internally does all the cryptographic work needed to prove the identity to the server without leaking the password.
Login form explained
From the perspective of the initialization of the login process we have two distinct login flows. If the flow is initialized by the user (or by any other component) by navigating explicitly to the login page, the flow is called explicit login. After the successful explicit login flow the user will be able to use the ZeroKit JS SDK and the ZeroKit API as a logged in user. The SDK can tell the user's identity to the client side JS logic of the integrating web app, but it cannot prove the user's identity directly to the backend of the web app. To delegate the user's identity to the web app backend, the integrated OpenId/Connect IDP component of the ZeroKit backend can be used by configuring the web app as a client of the IDP. The IDP can delegate the user's identity automatically when the user is logged in to the ZeroKit system. When the application requests the user's identity from the IDP and the user is not logged in yet, the IDP can also initiate the login process and after a successful login, it will complete the identity request automatically. If the login flow is initiated by the IDP, it is called implicit login.
In the following we will discuss the detailed steps of the two login flows.
Note: The core process (the logging in to the ZeroKit server) is the same in both flows, only the initiation and maybe the final redirect differ.
Explicit login
In this case the login flow is initiated by an explicit navigation to the login page. The initiator can be the user or any part of the web app which made a redirect.
The login iframe contains only the password field and the login logic. The presentation of the username field and the login button is the responsibility of the app. When the user presses the login button it should resolve the username entered by the user using the application's own API and then pass the ZeroKit user ID of the user to the SDK as an argument of the login method. The result of the method will be a JS promise which can be used to wait for the result of the login request. If the login succeeds, the SDK will automatically recognize the user's session and all further calls will be made on the behalf of the logged in user until logout. If the app needs, it can redirect the user to any other page when the login is completed. (No automatic redirection will be made by the SDK.)
If the user has a valid ZeroKit session in the browser, the IDP component can automatically delegate his/her identity to its configured clients without asking for his/her credentials again.
Explicit login flow explained
Implicit login (IDP)
In this case the login flow is initiated implicitly by the built-in OpenID Connect identity provider of the ZeroKit server. This occurs when a configured client of the IDP (typically the web app itself) requests the user's identity from the IDP and the provider realizes that the user has no active ZeroKit session. In this case the identity delegation is paused by an intervening login flow (as described previously). After the login succeeded (on the same page and the same way as in the explicit case), the SDK automatically redirects the logged in user to the IDP which will complete the flow and delegate the identity of the user to the web app backend. Please note, that the Promise returned by the login method will not resolve before redirection.
Note: Notice the differences between the two flows: 1) The first one is initiated by an explicit navigation to the login page by the user or by the application 2) In the first case, there is no automatic redirect after the successful login, while after the successful login in the implicit flow the SDK will redirect the user back to the IDP endpoint to finalize the identity request.
Implicit login flow explained
Tresor creation
Tresors are the basic units of encryption and sharing. They take care of all the necessary cryptographic operations such as key sharing, key revocation and key change. If a piece of data was once encrypted with the keys of a tresor, the same tresor has to be used to decrypt the data. Tresors can be created by any active user, but the created tresors have to be approved by the application through the administrative API. This process grants application control over the creation of tresors (key-containers) and an opportunity to save the tresor ID in their database before the users start to use them.
Note: No metadata about the tresor is handled by Tresorit ZeroKit!
The tresor creation itself is initiated on the client side by calling the Create tresor operation, which will take care for the cryptographic initialization and will return the ID of the newly created tresor. This ID then can be passed to the application backend and used as "operation ID" to approve / reject tresor creation. To get information about the creator (initial members) of the tresor, the tresor member listing feature of the administrative API can be used.
Note: The initial membership(s) of the tresor does not need further acceptance, after the validation of the tresor creation they instantly became valid members.
Tresor creation flow explained
Tresor sharing
Tresors can be shared with other users, so they can use the tresor to decrypt data which was encrypted by them or other members of the tresor. Sharing is a key-concept of the tresor and a share can be revoked at any time. After revocation the kicked out member will not be able to retrieve the metadata of the tresor from the platform server, therefore he/she loses the ability to encrypt or decrypt data within the tresor as well.
If a user wants to (or any operation he/she did in the web app needs to) share a tresor with another user, this process must be initialized from the JS SDK and the other user must be also registered into the web app and to the ZeroKit server. After a share is initialized, it has to be approved by the web app backend through the administration API. This approval cycle is the same as described above in the basic approval flow section.
Note: The approval may fail due to concurrent operations on the tresor. In that case, the whole process has to be retried. To minimize the chance of failure, the approval (or rejection) should take place immediately after the initialization.
Tresor sharing explained
Data handling (encryption / decryption)
The most common use-case of the ZeroKit SDK is the actual data handling: encryption and decryption. Both operations are executed on the client side through a tresor in which the user is a member. The encrypted data should be stored by the web app backend and provided to the proper clients when they need it, along with the ID of the tresor used for encryption.
Important:
- The same tresor has to be used for decryption which was used for the encryption of the data.
- Your data never reaches the Tresorit server in any form (even in encrypted form).
- Tresorit does not store any data, it is the responsibility of the web app backend.
The following figures will help to understand the encryption and decryption flows from the viewpoint of the user and the web app.
Encryption
Encryption explained
Decryption
Decryption explained