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 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. 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.
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 locally or via one-click install on DigitalOcean or 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:
We can start our Nuxt3 application by running the following command:
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:
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 if we don’t have one.
Learn more about setting up Appwrite here. On the console, click on the Create Project button.
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.
The inner page allows us to copy the Project ID and API Endpoint for use in our Nuxt application.
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:
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:
<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:
<template>
<div>
<Signup/>
</div>
</template>
At this point, our Signup page should look like the below:
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:
<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:
Building the chat interface
To add a chat interface for exchanging chats, we will create a pages/Chat.vue
and add the code below:
<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:
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:
data: () => ({
name: "",
email: "",
password: "",
confirmPassword: ""
}),
We will proceed to import the Appwrite SDK initialized in the init.js
file, like so:
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:
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
andconfirmPassword
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 useduniqueId
to generate one automatically. - Adding
email
andpassword
is also mandatory. - The
name
option is optional, and we can create an account without one.
- To use Appwrite’s create account service, it’s mandatory to add the
-
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
At this point, our application should look like the below:
From our Appwrite dashboard, we can view all created accounts from the Authentication > Users tab, like so:
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:
data: () => ({
email: "",
password: "",
}),
We will also import the Appwrite SDK initialized in the init.js
file, like so:
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:
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:
<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>
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 to authenticate and sign users into the application, the next part in the series will discuss sending messages and encryption.
Top comments (1)
How to store the Cryptr secret key without exposing it in the client side?