Build an End-To-End Encrypted Chat App in Nuxt.js: Messages and Encryption

moerayo

Moronfolu Olufunke

Posted on October 24, 2022

Build an End-To-End Encrypted Chat App in Nuxt.js: Messages and Encryption

Encryption involves converting information into secret codes to protect the information’s precise details from being readable to unauthorized people, including cyber-criminals.

This article discusses building an end-to-end encrypted chat application in Nuxt.js, using Appwrite to provide the database service and Cryptr for encrypting the exchanged data.

GitHub

Check out the complete source code here.

Prerequisites

Understanding this article requires the following:

  • Installation of Node.js
  • Basic knowledge of TypeScript
  • Docker installation
  • An Appwrite instance; check out this article on how to set up an instance

Moreover, this article is the second part of a two-part series on building an end-to-end encrypted chat app in Nuxt.js. The first part of the article that discusses Setup and Authentication can be found here.

Setting up Appwrite’s database

Appwrite is an end-to-end backend server that provides services that help abstract away some of the complexity of performing backend tasks. One of the services offered by Appwrite is the Database Service, which allows us to store users’ data and fetch them as needed.

Creating a Database

To create a database in Appwrite, click on the Database option in the menu tab, where we can add a database to the system.

Creating a Database

Creating a collection

Appwrite’s database services also provide collection services within its database. These collections act as containers for documents. To create a collection, select the desired database from which we can add a new collection.

Creating a collection

After creating a new collection, the system redirects us to the Settings page, from which we can set the permissions needed to use the database service — an empty permission field will prevent the client from accessing the document. In our case, we will add the role access of “any” to allow any role access to the collection. As seen in the image below:

collection settings

Creating attributes

Our document will have some peculiar attributes. To create them, we will navigate to the Attributes section. In our case, we will choose the New String Attribute.

collection settings

We can then create an Attribute ID with the name “message” with a size of 256 bits.

attributes setting

Connecting the chat app with Appwrite’s database

We will start by importing Appwrite’s SDK initialized in the init.js and Appwrite’s database method. This is done as follows:



import {client} from '~/init'
import { Databases, ID } from 'appwrite';


Enter fullscreen mode Exit fullscreen mode

We will also update the data property in the script tag with message, databaseMessages, and password to hold the user’s and database’s messages, like so:



data: () => ({
    message: '',
    databaseMessages: ['']
  }),


Enter fullscreen mode Exit fullscreen mode

Then, we will create an instance of databases using the imported databases method from Appwrite, which takes the client as parameters.



const databases = new Databases(client);


Enter fullscreen mode Exit fullscreen mode

Listing messages in the database
We can then create a listMessages function in the methods property like so:



listMessages: async function (){
      let promise = await databases.listDocuments('database ID', 'collection ID');
      this.databaseMessages = promise.documents.map(document =>  document.message)
    }, 


Enter fullscreen mode Exit fullscreen mode

The listMessages function in the code block above does the following:

  • listDocuments method accesses the databases method from Appwrite to list all documents in the database.
  • The listDocuments method takes the database ID and collection ID as parameters. These IDs can be found on the Appwrite dashboard.
  • Returned messages in the Appwrite database are stored in this.databaseMessages .

Sending messages to the database

After the above, we can send messages to the Appwrite database by creating a function called sendMessage in the methods:



sendMessage: async function(){
  try {
    await databases.createDocument('632754bf9b28a3d032e7', '632754dccfc24bf0505e', ID.unique(), {'message': this.message})
    alert('message sent')
    this.message = '';
    this.listMessages()
  } catch(e){
    console.log(e)
  }
}


Enter fullscreen mode Exit fullscreen mode

The sendMessage function in the code block above does the following:

  • the createDocument method accesses the databases method from Appwrite to create a new document in the database. This method takes the database ID, collection ID, Appwrite’s ID.unique() method, and the attribute value(s) as parameters. In this case, our attribute value is message , which we created on the Appwrite admin dashboard.
  • An alert that confirms successful message sending.
  • Clears out the message input field, and we call the listMessages() function, which lists all the messages that were sent to the Appwrite’s database

Next, we’ll update our chat application interface to render all the messages sent to the database as follows:



<section v-for="databaseMessage in databaseMessages" :key="JSON.stringify(databaseMessage)">
  <p :class="databaseMessage ? ['bg-lightest-blue', 'pa2', 'mv2', 'br3', 'w-60', 'f7'] : ''">
    {{databaseMessage}}
  </p>
</section>


Enter fullscreen mode Exit fullscreen mode

The code block above loops through the messages returned from the database using Vue’s v-for directive. Individual messages are then rendered with {{databaseMessage}}.

Putting it all together, our Pages/Chat.vue file at this stage will look like this: Follow this GitHub Gist.

Our application interface will also look like the below after sending our first message:

first message interface

Encryption

We will use Cryptr, which is an encryption and decryption aes-256-gcm module for Node.js to encrypt the messages we will be sending on our chat app.

Start using it by installing it in the project like so:



npm install cryptr


Enter fullscreen mode Exit fullscreen mode

We can then import the Cryptr library and create a new instance of Cryptr in our Pages/Chat.vue, with the new Cryptr instance containing our Cryptr’s secret key.



const Cryptr = require('cryptr');
const cryptr = new Cryptr('*****');


Enter fullscreen mode Exit fullscreen mode

We want to encrypt the messages we are sending and get back the decrypted version, as the encrypted messages do not have much meaning to the reader. To achieve this, we will modify the sendMessage function in the Pages/Chat.vue with the following:



sendMessage: async function(){
  const encryptedMessage = cryptr.encrypt(this.message)
  try {
    await databases.createDocument('632754bf9b28a3d032e7', '632754dccfc24bf0505e', ID.unique(), {'message': encryptedMessage})

  } catch(e){
    console.log(e)
  }
}


Enter fullscreen mode Exit fullscreen mode

The code block above does the following:

  • We access the encrypt method on the Cryptr library and pass the message being sent as a parameter. This encrypt method encrypts the message and stores it in the encryptedMessage variable.
  • The data in the encryptedMessage is then passed to Appwrite to be saved in the database.

If we check our Appwrite database, we will see the “How are you?” message we sent encoded with a bunch of numbers and alphabets. We will also see the first message (Hello) — in a human-readable format.

human readable format message

Since humans can’t make sense of the encrypted message, we will need to decrypt the message before it is rendered back in the chat interface. To do this, we will edit the listMessages function in the Pages/Chat.vue, like so:



listMessages: async function (){
  let promise = await databases.listDocuments('632754bf9b28a3d032e7', '632754dccfc24bf0505e');
  this.databaseMessages = promise.documents.map(document =>  cryptr.decrypt(document.message))
}, 


Enter fullscreen mode Exit fullscreen mode

We fetched the messages from Appwrite’s database in the above code using the databases.listDocuments method. We then used Cryptr’s decrypt method to transform the encrypted message we fetched from the database.

At this point, we have successfully created a chat application with end-to-end encryption to keep our conversations secure.
Our application will look like the below:

final application

Resources

💖 💪 🙅 🚩
moerayo
Moronfolu Olufunke

Posted on October 24, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related