App and web development have come a long way over the last few years. We use a lot of chat apps everyday. One of the most widely used features is live chat. However, do we really understand how the applications secure our messages? This tutorial helps us answer that question. Using the React CometChat UI Kit, Firebase backend services and Virgil Security, you will learn how to build end to end encrypted chat app with CometChat
Follow along the steps to build an end-to-end encrypted chat app that will allow users:
A way for end-users to signup (email & password is sufficient)
A way for users to log in/out and have a short profile (Name, UID, Photo)
Use the superhero theme to create users as used in the documentation.
Send, receive text messages and voice & video calls.
Group chat - create group, add/remove users
Message encryption at-rest and in-transit and decrypt received messages
Notifications - new messages, unread messages
To follow this tutorial, you must have a degree of understanding of the general use of React.js. This will help you to improve your understanding of this tutorial.
Installing the App Dependencies - Client Side
Step 1: you need to have Node.js installed on your machine
Step 2: create a new project with the name e2e-encrypted-chat-app by running the following statement.
*Note: At this time of writing this tutorial, we are using the Firebase SDK with the version @8.9.1
Installing the App Dependencies - Server Side
In this project, we will Node.js/Express framework to build the server side. We need to use the server side because we need to generate Virgil JWT Token, we will discuss about this in the following sections. To install the app dependencies for the server side, please follow the below steps:
Step 1: Create a new folder called “e2e-encrypted-server”.
Step 2: Open your terminal and change the directory to the create folder and run the following statement.
Step 3: After running the above statement, we will have a new Node.js project. Copy the dependencies from here and paste to the dependencies section in your package.json file.
Step 4: Run the following statement to install the dependencies.
Configuring CometChat SDK
Head to CometChat Dashboard and create an account.
After registering a new account, you need to the log in to the CometChat dashboard.
From the dashboard, add a new app called "e2e-encrypted-chat-app".
Select this newly added app from the list.
From the Quick Start copy the APP_ID, REGION, and AUTH_KEY, which will be used later.
Navigate to the Users tab, and delete all the default users (very important).
Navigate to the Groups tab and delete all the default groups (very important).
Create a file called **.env** in the root folder of your project.
Import and inject your secret keys in the **.env** file containing your CometChat and Firebase in this manner.
10. Make sure to exclude **.env** in your gitIgnore file from being exposed online.
How E2EE Works?
E2EE is a great way to secure your messages. It means that, when you use E2EE to encrypt your messages, no one can see the content of your message including hackers, the government, or event your company. On the other hand, E2EE is much more stronger than standard encryption methods. You can see the below example for more information.
For example, if you send or receive an email using your company’s service, and that service does not use E2EE such as Gmail, and so on. The organization can still see the content of your messages because they know about the encryption keys. Using E2EE, the service provider does not actually handle the decryption key.
The security behind end-to-end encryption is handled by using public-private key pair. For more information about public-private key pair, please follow the next section.
E2EE Public Keys and Private Keys
As mentioned above, the magic behind end-to-end encryption is using public-private key pair. The system will create the public and private cryptographic keys for each user. You can refer to the below image for more information.
The system has two users - Bob and Alice. The end-to-end encrypted system provides each with a public-private key pair. The public keys are stored on the server and their private keys are stored on their device.
Step 1: Alice would like to send Bob an encrypted message. She uses Bob’s public key to encrypt the message before sending to him.
Step 2: When Bob has received the message, he will use his private key on his device to decrypt the message.
If Bob would like to reply, the process will be repeated. It means that Bob will encrypt the message by using Alice’s public key and decrypt the message from Bob.
Secure communication platform for enterprise privacy.
Post-compromise protection for stored data.
Vehicle to everything - secure communication.
Internet of things - lifecycle security.
Password protection against hacking.
On the other hand, its libraries and UI Kits are easy to use and clean code as you can see the below code snippet.
Last but not least, Virgil Securities can be applied to different industries including healthcare, financial, automotive, cloud, internet of things, industry 4.0.
Setting Up Virgil Securities
Setting Up Firebase Project
According to the requirements of the end to end encrypted chat app, you need to let users create a new account and login to the application, Firebase will be used to achieve that. Head to Firebase to create a new project and activate the email and password authentication service. This is how you do it:
To begin using Firebase, you’ll need a Gmail account. Head over to Firebase and create a new project.
Firebase provides support for authentication using different providers. For example, Social Auth, phone numbers, as well as the standard email and password method. Since you will be using the email and password authentication method in this tutorial, you need to enable this method for the project you created in Firebase, as it is by default disabled.
Under the authentication tab for your project, click the sign-in method and you should see a list of providers currently supported by Firebase.
Next, click the edit icon on the email/password provider and enable it.
Now, you need to go enable Firebase Realtime Database. We will Firebase Realtime Database to store the information of the users in the application. Please refer to the following part for more information.
Please follow the guidance from Firebase. After following all steps, you will see the database URL. If you just need to update the “FIREBASE_REALTIME_DATABASE_URL” variable in your “Constants.java” file with that value.
On the other hand, your Firebase real-time database will be expired in the future. To update the rules you just need to select the “Rules” tab and update the date/time in milliseconds as you can see in the image below.
The below images demonstrate the data structure of the application. A user should have an avatar, an email, an id. Because the end-users can log in to the application by using multiple devices. Therefore, we need to save the private key to the Virgil Security cloud and then retrieve it as needed. To do that, we need to use the keyPassword field. Actually, it is just a unique data, we use the uuid library to generate that data and save it to the Firebase Realtime Database. We will discuss about saving and retrieving the private key in the following sections.
Configuring Styling for the Application
Inside your project structure, open the index.css files and paste the codes here. index.css file will contain all CSS of the application.
Initializing CometChat for the Application
The below codes initialize CometChat in your app before it spins up. The App.js file uses your CometChat API Credentials. We will get CometChat API Credentials from the .env file. Please do not share your secret keys on GitHub.
Actually, App.js does not contain only the above code. It also contains other business logic of the application. The full source code of App.js file can be found here.
Configuring the Firebase File
You need to create a “firebase.js” file inside the “src” folder and you need to enable Firebase realtime database. This file is responsible for interfacing with Firebase authentication and database services. Also, it makes ready our google authentication service provider enabling us to sign in with google. Secret keys will be stored in the .env file. As mentioned above, please do not share your secret keys on GitHub.
The below images demonstrate the data structure of the application. A user should have an avatar, an email, an id.
Project Structure - Server Side
The image below reveals the project structure for the server side. Make sure you see the folder arrangement before proceeding.
Now, let's make the rest of the project components as seen in the image above.
Project Structure - Client side
The image below reveals the project structure. Make sure you see the folder arrangement before proceeding.
Now, let's make the rest of the project components as seen in the image above.
Build Generate Virgil JWT Token API
In this tutorial, we need to use Node.js/Express framework for the server side. To initialize E3Kit in the client side, the client side has to get the Virgil JWT Token first. After that, the application has a function to return that token and that function will be passed to the initialize function. The initialize function helps us to initialize E3Kit on the client side. To create the API to return the Virgil JWT Token, please follow the below steps.
Step 1: Create the .env file inside the “e2e-encrypted-server”. And update it with the following content.
*Note: APP_ID, APP_KEY, APP_KEY_ID will be taken from the Virgil Security dashboard as mentioned above.
Step 2: Create the “index.js” file inside the “e2e-encrypted-server”.
Step 3: The content of your index.js file should look like the below code snippet.
As you can see the above code snippet, we include virgil-sdk, virgil-crypto to generate the Virgil JWT Token. We also read the environment from the .env file. Those environment variables were taken from the Virgil Dashboard and saved to the .env file as mentioned above.
The Context.js File
In this project, we need to communicate between component, we should avoid props drilling. We can have several solutions to avoid that. In this project, we can use React Context to pass data through the component tree without passing it down at every level. Therefore, we need to create the context.js file inside the src folder. This file will take responsibility for creating the React Context in the end to end encrypted chat application. Please. follow the below code snippet for more information. The full source code can be found here.
The App.js File
The App.js file is responsible for rendering different components by the given routes. For example, it will render the login page if the user has not logged in, yet or it renders the home page if the user has signed in to the system. On other hand, it will be used to initialize CometChat.
The full source code of the App.js file can be found here.
The Loading Component
The loading component will be shown when the system performs some side effects such as interacting with Firebase or calling CometChat APIs and so on. This component will be used to increase user experience. If we do not have this component, the end-users cannot know when the data is loaded successfully.
The full source code of the loading component can be found here.
The Login Component
This component is responsible for authenticating our users using the Firebase google authentication service. It accepts the user credentials and either signs him up or in, depending on if he is new to our application. See the code below and observe how our app interacts with Firebase and the CometChat SDK. The full source code can be found here.
Before calling any functions from the CometChat service, please make sure that. CometChat was initialized successfully in your application. After the user has logged in successfully. He/She will be redirected to the home page. In this case, we need to store the authenticated user in the AsyncStorage for further usages. You can refer to the code snippet below to understand how to log in to the CometChat.
On the other hand, we need to integrate with E3Kit of the Virgil Security. For this reason, we need to initialize the EThree as what we have done with the CometChat. You can refer to the below code snippet for more information.
The above code snippet indicates that we need to the /virgil-jwt API endpoint to get the Virgil JWT token. After that, we define a function called getVirgilToken. This function will be passed to the window.E3kit.EThree.initialize to get the eThree object. Last but not least, we need to use the created eThree object and call the register function. The register function helps us to create the public key on the Virgil cloud and store the private key on the user’s device.
Backup & Restore the Private Key
The end-user can log in to the application by using multiple devices. For this reason, we need to find a way to retrieve the private key even the end-user is using different devices to test the application. If we do not do that, the application cannot decrypt the messages, it just can work well on a single device. Fortunately, Virgil Security is providing a way to restore the private key. You can refer to this link for more information. To backup and restore the private key, you can follow the below steps:
Step 1: After the Firebase Authentication service has authenticated the user’s credentials and the user information is valid, we need to generate the keyPassword field and update the user data in the Firebase Realtime Database.
Step 2: We pass the updated user to the registerEthree function.
Step 3: In the registerEthree function, we will check if the current user is a new user, we will generate the private key and back up that key by saving it to the Virgil Security cloud by using eThree.backupPrivateKey(user.keyPassword). On the other hand, if that user has been registered before, we need to check whether the current user’s device is containing the private key by calling eThree.hasLocalPrivateKey(). If the device does not have a local private key, we will retrieve it from the cloud by using eThree.restorePrivateKey(user.keyPassword). Therefore, we can see here, the keyPassword is very important in saving/retrieving the private key from/to the cloud.
For the full source code of the Login component, you can find from here.
In the Login component, we are using withModal as a higher-order component. This higher-order component will be used to reuse the code of showing and hiding the custom modal. In this case, we want to show the sign-up modal to let end-users register new accounts. We will discuss the withModal - higher-order component and the sign-up component in the following section.
The withModal - Higher-Order Component
As mentioned above, we would like to show the SignUp component as a modal. Actually, we have multiple modals in the application, and we should avoid duplicating common functionalities such as showing/hiding the modals, and so on. To reuse the common logic, we can have several options. In this project, we will create a higher-order component called “withModal”. That higher-oder component helps us to avoid duplicating code and we can customize the UI for the modals. Please follow the below code snippet for more information. The full source code can be found here.
The Sign Up Component
The sign-up component will help end-users to register new accounts. This component will do two things. The first thing is to register new accounts on Firebase by using the Firebase authentication service. Aside from that, it also registers new accounts on CometChat by using the CometChat SDK. The full source code can be found here.
To create a new account from CometChat, you need to create a new user object based on the User model from CometChat. Following that, the created user will be registered on CometChat by calling the “registerUser” function from the CometChat service. Please do not forget to pass your CometChat auth key as the second parameter You can refer to the below code snippet for more information.
Add The CometChat UI To Our Application
Before we can use the CometChat Pro React UI kit, we need to add it in our project so that we can reference it. In this case, we are using React UI Kit v3.0. To do that, follow the next steps:
Step 2: Copy the folder of the CometChat Pro React UI Kit you just cloned into the src folder of your project:
Step 3: Copy all the dependencies from the package.json file of the CometChat Pro React UI Kit folder and paste them in the dependencies section of the package.json file of your project.
Step 4: Save the file and install the dependencies like so: npm install
As soon as the installation is completed, you now have access to all the React UI Components. The React UI kit contains different chat UI components for different purposes as you can see in the documentation here. It includes:
The Home Component
The application should support text and voice calling. For this reason, we will create the Home component and inside the Home component, we use the CometChatUI component from React UI Kit. The full source code can be found here. Please follow the below code snippet for more information.
We need to use the eThree object inside the UI kit that’s why we pass the eThree object as a prop to the CometChat UI component. Because we use eThree object in different places of the application. Hence, we store the eThree object inside the CometChatContextProvider. Please follow the below steps:
Step 1: In the index.js inside the “CometChatUI” folder, update the return statement.
Step 2: In the CometChatContext.js file, update the state in the constructor.
The Header Component
The header component will take responsibility for rendering the greeting and including the logout icon. After the end-users click on the logout icon. The application should do some clean-up actions. The full source code of the header component can be found here.
As mentioned above, after the end-users click on the logout icon, we need to do some clean-up actions. On the other hand, we need to call the logout function from the CometChat service and the cleanup function from Virgil Security. Aside from that, we also clear the authenticated information from the local storage, set the user information to null in the created React Context, and then redirect the end-users to the login page. You can refer to the below code snippet for more information.
Disable Unused Features
After the end-users had left the CometChat, they cannot leave the Virgil group. For this reason, we need to hide this option. If the end-users would like to leave the group, they need to tell the admin remove them from the group. To disable the leave group option. Please open the UIKitSettings.js file and update the below code.
The Encrypt & Decrypt Features
We are using CometChat React UI Kit. Therefore, we need to integrate the E3Kit - Virgil Security to the UI Kit. To do that, please follow the below steps:
Step 1: Please make sure Virgil Security has been included in the index.html file.
Step 2: Find the index.js file inside the “CometChatMessageComposer” folder.
According to the Virgil Security documentation, we use the below code snippet to encrypt the text message.
We build two kinds of messages - an unencrypted message to be rendered on the UI and encrypted message that will be sent to the API.
After calling the API, we need to decrypt the message and render it on the UI.
Step 3: In the “index.js” file inside the “CometChatMessageList” folder, we need to add the logic to decrypt after receiving the coming message.
If the selected conversation is a group chat, we need to load the group from the Virgil Security cloud because we need to decrypt/encrypt the messages for the selected group. To do that, we create the startVirgilGroupChat function and then call it in the componentDidMount and componentDidUpdate. On the other hand, that function will be called after a new member is added/removed from the group to reload the key. Following that, the decryptUserMessage function is used to decrypt messages for the private chat and the decryptGroupMessage is used to decrypt messages for the group chat.
Step 4: When opening the chat box, we need to fetch the list of messages. However, they are encrypted messages. Hence, we need to encrypt them before rendering them on the UI.
Step 5: We need to update the last message in the conversation list. To do that, in the “index.js” file inside the “CometChatConversationItemList” folder. Please add the following code.
Step 6: If the end-users create new public groups. The application needs to create the corresponding groups in the Virgil Security platform. Please open the index.js file in the “CometChatCreateGroup” folder and update the code as follow.
Step 7: In the CometChat React UI Kit, the end-users can join a public group by clicking on any items in the group list. However, according to the Virgil Security documentation, if we would like to add/remove members from/to the group, the owner should do those tasks. For this reason, we should prevent the end-users to join a public group by clicking on it. Please open the index.js file in the “CometChatGroupList” folder and comment the code as follow.
Step 8: As mentioned above, the group’s owners can add the members to their groups. To do that, we need to modify the index.js file in the “CometChatAddGroupMemberList” folder.
Step 9: If the admin delete the group, the application should delete the Virgil group. Please open the index.js file in the “CometChatGroupDetails” folder and update the onDeleteConfirm function as follow. We need to remove all participants except the owner before deleting the group.
Step 10: If the admin removes a member from the group, the application needs to remove that member from the Virgil group. Please open the index.js in “CometChatViewGroupMemberList” folder, add the getVirgilGroupInstance function and update the kickMember function as follow - the getVirgilGroupInstance function is used to get the Virgil group instance from the cloud if that group instance does not exist in the local storage.
Step 11: The administrators can change the scope of the participants in their CometChat groups. We should disable that feature because we cannot change the scope of the participants in the Virgil groups. To do that, please open the index.js file in the “CometChatGroupDetails” folder and update the following part.
In conclusion, we have done an amazing job in developing an end to end encrypted chat app by leveraging React, Firebase, and CometChat UI KIt You’ve been introduced to the chemistry behind E2EE and how the CometChat UI Kit makes chat applications buildable.
If we use E2EE solutions from the Virgil Security. Other extensions of the CometChat UI Kit will not work.
You have seen how to integrate most of the CometChat functionalities such as texting and real-time messaging, voice/video call, managing users/groups. I hope you enjoyed this tutorial and that you were able to successfully build the end to end encrypted chat app. It's time to get busy and build other related applications with the skills you have gotten from this tutorial. You can start building your chat app for free by signing up to the cometchat dashboard here.
About the Author
Hiep Le is a software engineer. He takes a huge interest in building software products and is a full-time software engineer. Most of his work is focused on one thing - to help people learn.