Build an End-To-End Encrypted Chat App in Nuxt.js: Setup and Authentication
Tags: appwrite, nuxt, typescript, javascript
Published: 2022-10-21 09:47:16

Chat apps have evolved significantly since they debuted in the 1980s. Despite this evolution over decades, chat apps like TikTok, Facebook, and Telegram remain some of the [globally most downloaded](https://www.forbes.com/sites/johnkoetsier/2021/12/27/top-10-most-downloaded-apps-and-games-of-2021-tiktok-telegram-big-winners/?sh=2d682b063a1f) applications yearly. With the explosion of messaging applications, there has also been a need for even more encryption on message exchange to prevent unauthorized access to the exchanged data.

This article discusses how to build an end-to-end encrypted chat application with Nuxt.js and [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog). This article is the first of a two-part series, discussing the authentication and user account management using the Appwrite account API.

## GitHub

Check out the complete source code [here](https://github.com/MoeRayo/end-to-end-chat-encryption).

## Prerequisites

Understanding this article requires the following:

- Installation of Node.js
- Basic knowledge of TypeScript
- Docker [installation](https://www.docker.com/get-started/)
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance locally or via one-click install on [DigitalOcean](https://marketplace.digitalocean.com/apps/appwrite) or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod)

## Creating a Nuxt.js project

Use `npx create-nuxt-app <project-name>` to create a new Nuxt.js project. 
The process of scaffolding the project provides a list of options, which should look like this:


![Nuxt application scaffolding options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmskha7z4yh2bnobvy39.png)

We can start our Nuxt3 application by running the following command:

```javascript
cd <project name>
npm run dev
```

Nuxt.js will, in turn, start a hot-reloading development environment that is accessible by default at `http://localhost:3000`.

## Installing Appwrite

To use Appwrite in our project, we will install the Appwrite SDK from the command line, like so:

```javascript
npm install nuxt-appwrite
```

## Creating an Appwrite project

To create a new project, start up the Appwrite instance on your machine and navigate to the specified hostname and port `http://localhost:80`. Next, we need to log into our account or create an [account](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=blog&utm_campaign=hackmamba) if we don’t have one.

![Appwrite sign up page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lauzoust2ncnu66c4p5c.png)

Learn more about setting up Appwrite [here](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba). On the console, click on the **Create Project** button.

![inner create project page for appwrite](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lq39z5sxpsjtz2m6hv9d.png)

The project dashboard becomes visible on the console after the project creation. We can access the inner page from the Settings tab at the top of the page. 

![Appwrite project dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17ygwq9nib76nl8d646y.png)

The inner page allows us to copy the **Project ID** and **API Endpoint** for use in our Nuxt application.

![Appwrite settings page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q95e3wknnv4xrh56wv0e.png)

We will then proceed to create an `init.js` file in our Nuxt.js application’s root directory to initialize the Appwrite Web SDK by adding the code block below:

```javascript
import { Client, Account } from "appwrite";
export const client = new Client();
export const account = new Account(client);
client
    .setEndpoint('http://localhost/v1') // Your API Endpoint
    .setProject('***') // Your project ID
;
```

## Building the signup interface

Building a chat application requires having a signup page for first-time users. To develop our signup page, we will create a `components/Signup.vue` and add the code below:

```javascript
<template>
  <div class="ba b--light-blue bw3 vh-100 pv5 ph2 athelas">
    <form @submit.prevent="signUp" class="bg-lightest-blue br2 mw6 w-40-m w-70 w-20-l center pa3 shadow-5">
    
      <h2 class="ttc tc">Sign up</h2>
      
      <label for="name" class="db mb1 black-70">Name</label>
      <input name="name" id="name" type="text" class="db mb3 w-100 br3 pa2 ba bw2" placeholder="John Doe" v-model="name">
      
      <label for="email" class="db mb1 black-70">Email</label>
      <input name="email" id="email" type="email" class="db mb3 w-100 br3 pa2 ba bw2" placeholder="example@email.com" v-model="email">
      
      <label for="password" class="db mb1 black-70">Password</label>
      <input name="password" id="password" type="password" class="db mb3 w-100 br3 pa2 ba bw2" placeholder="••••••••" v-model="password">
      
      <label for="confirm-password" class="db mb1 black-70">Confirm Password</label>
      <input name="confirm-password" id="confirm-password" type="password" class="db mb3 w-100 br3 pa2 ba bw2" v-model="confirmPassword" placeholder="••••••••">
      
      <button type="submit" class="center db ph4 pv2 bg-navy ba br3 white pointer">Sign up</button>
      
      <p>Already have an account? <a href="" class="black-70 b">Sign in</a> </p>
      
    </form>    
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
methods: {
  signUp: async function(){}
}

})
</script>
```
We will import the `components/Signup.vue` into the `pages/index.vue`, like so: 

```javascript
<template>
  <div>
    <Signup/>
  </div>
</template>
```

At this point, our Signup page should look like the below:

![Signup page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezdyx2ddosiflabz0qto.png)

## Building the sign-in interface

We also need to create a sign-in page that allows already registered users to log in to chat. To do this, we will create a `pages/Signin.vue` and add the code below:

```javascript
<template>
  <div class="ba b--light-blue bw3 vh-100 pv5 ph2 athelas">
    <form @submit.prevent="signinWithEmailandPassword" class="bg-lightest-blue br2 mw6 w-40-m w-70 w-20-l center pa3 shadow-5 ">
    
      <h2 class="ttc tc">Sign In</h2>
      
      <label for="email" class="db mb1 black-70">Email</label>
      <input name="email" id="email" v-model="email" type="email" class="db mb3 w-100 br3 pa2 ba bw2" placeholder="example@email.com">
      
      <label for="password" class="db mb1 black-70">Password</label>
      <input name="password" id="password" v-model="password" type="password" class="db mb3 w-100 br3 pa2 ba bw2" placeholder="••••••••">
      
      <button type="submit" class="center db ph4 pv2 bg-navy ba br3 white pointer">Sign in</button>
      
    </form>    
  </div>
</template>
<script lang="ts">
import Vue from 'vue'
import {signIn} from '~/init'
export default Vue.extend({
  data: () => ({
    email: "",
    password: "",
  }),
  methods: {
    signinWithEmailandPassword(){}
  }
  
})
</script>
```

At this point, our sign-in page should look like the below:


![sign-in page ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v41zki5v1j1sy5z7dzc4.png)

## Building the chat interface

To add a chat interface for exchanging chats, we will create a `pages/Chat.vue` and add the code below:


```javascript
<template>
  <div class="ba b--light-blue bw3 vh-100 pv5 ph2 athelas">
    <form @submit.prevent="" class="bg-lightest-blue br3 mw6 w-40-m w-70 w-20-l center pt3 shadow-5 ">
      <h2 class="near-black tc f3">Chat</h2>
      <div class="bg-white mt3 br-top pa3 vh-50 relative">
        <div class="flex justify-between items-center absolute bottom-1 right-1 left-1">
          <input type="text" class="db w-90 mr2 pv3 ph2 br3 ba" placeholder="Type message">
          <img src="../static/send.png" class="db ma w2 h2 bg-lightest-blue pa2 br2 pointer"  alt="send message image">
        </div>
      </div>
      </form> 
  </div>
</template>
<style>
  .br-top {
    border-top-left-radius: 30px;
    border-top-right-radius: 30px;
    border-bottom-left-radius: .5rem;
    border-bottom-right-radius: .5rem;
  }
</style>
```
At this point, we should have our chat interface looking like the below:

![chat interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i2yvx8pt0r4zxxqcbl1p.png)

## Authenticating users

Having created the signup, sign in, and chat interface, it is time we added authentication — signup and sign-in. We will start by enabling users to sign up to use the application.
### Signing up users
We will start by navigating to the `components/Signup.vue` and updating the `data` property in the `script` tag with `name`, `email`, `password`, and `confirmPassword` to hold the user’s input.  Like so:

```javascript
data: () => ({
  name: "",
  email: "",
  password: "",
  confirmPassword: ""
}),
```
We will proceed to import the Appwrite SDK initialized in the `init.js` file, like so:

```javascript
import {account} from '~/init'
```

We will then create a `signUp` function in the `methods` property, which gets executed when the new user tries to sign up. Like so:

```javascript
signUp: async function(){
  if (this.password.length >= 8){
    if(this.password === this.confirmPassword) {
      try{
        await account.create('unique()', this.email, this.password, this.name)
        alert("account created successfully")
        window.location.href = '/chat'
      } catch (e) {
        console.log(e)
      }
    } else {
      alert("password do not match")
    }
  } else {
    alert("password length should be up to 8 characters")
  }
},
```
The `signUp`  function in the code block above does the following:

- Verifies that the length of the password is equal to or greater than eight characters.
- Confirms that the characters in `password` and `confirmPassword` are the same.
- Accesses Appwrite’s services using the `account.create` to create a new account using the user’s email, password, and name.
    - To use Appwrite’s create account service, it’s mandatory to add the `userId`; in this case, we used `uniqueId` to generate one automatically.
    - Adding `email` and `password` is also mandatory.
    - The `name` option is optional, and we can create an account without one.
- `window.location.href` allowed us to navigate to the chat interface after successful account creation.

Putting everything together, our code block will look like this:

[https://gist.github.com/MoeRayo/af7dfd118901de84578c15baa6e3d406](https://gist.github.com/MoeRayo/af7dfd118901de84578c15baa6e3d406)

At this point, our application should look like the below:


![Signup interface after putting it all together](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ibqiaujp45h5yy46n4zh.gif)

From our Appwrite dashboard, we can view all created accounts from the **Authentication > Users** tab, like so:


![Appwrite users dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ftt7r11hoy6x55huw8i.png)

### Signing users in
We need users who already have an account to sign into the application. To do this, we will navigate to the `components/Signin.vue` and update the `data` property in the `script` tag with email and password. This is done as follows:

```javascript
data: () => ({
  email: "",
  password: "",
}),
```
We will also import the Appwrite SDK initialized in the `init.js` file, like so:

```javascript
import {account} from '~/init'
```
We will then create a `signIn` function in the `methods` property, which gets executed when the new user tries to sign in. Like so:

```javascript
signIn: async function () {
  try{
    await account.createEmailSession(this.email, this.password)
      window.location.href = '/chat'
    alert("user signed in")
  } catch (e){
      console.log(e)
  }
}
```
The `signIn`  function in the code block above does the following:

- Accesses Appwrite’s services using the `account.createEmailSession` to create a new account session using the user’s email, password 
- `window``*.location.*``href` allows us to navigate to the chat interface after successful user login

At this point, the code within our `pages/Signin.vue` will look like this:

```javascript
<script lang="ts">
import Vue from 'vue'
import {account} from '~/init'
export default Vue.extend({
  data: () => ({
    email: "",
    password: "",
  }),
  methods: {
    signIn: async function () {
      try{
        await account.createEmailSession(this.email, this.password)
          window.location.href = '/chat'
        alert("user signed in")
      } catch (e){
          console.log(e)
      }
    }
  }
  
})
</script>
```

![Final authentication product](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajl2juoj5zyn1w9t3o7r.gif)

At this point, we have our chat app ready for users to create a new account and sign in.

## Conclusion

This article is the first part of a two-article series about building an encrypted chat application. Whereas this part discusses using [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) to authenticate and sign users into the application, the next part in the series will discuss sending messages and encryption.

## Resources
- [Create a local Appwrite instance in 3 steps](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) 
- [Appwrite’s account API](https://appwrite.io/docs/client/account?sdk=web-default#accountCreateOAuth2Session?utm_source=hackmamba&utm_medium=blog&utm_campaign=auth0_auth0-authentication-vue)