How to set up Vonage in Appwrite

Obinna Isiwekpeni - Sep 7 '23 - - Dev Community

Appwrite, as a backend-as-a-service, provides phone authentication where users can create accounts and log in using SMS. Appwrite requires an SMS provider to be set up for this to work. One such SMS provider is Vonage.

In this tutorial, we will learn how to set up a Vonage account, configure it to be used with Appwrite, and build a Next.js app to put it all together.

GitHub

Check out the source code here.

Prerequisites

To follow along in this tutorial, we require the following:

  • A basic understanding of JavaScript
  • Familiarity with Next.js
  • Docker

New to Appwrite?

Appwrite is an open-source backend-as-a-service that abstracts all the complexity of building applications by providing a set of APIs for core backend needs. Appwrite does a lot of heavy lifting for developers by providing authentication, database, file storage, functions, webhooks, and much more.

What is Vonage?

Vonage provides APIs that help organizations enhance customer experience by scaling to best meet their needs. They provide several communication channels like Voice, Video, SMS, MMS, and social chat apps like Facebook, Whatsapp, and Viber, which businesses can use to reach and engage customers.

In this tutorial, we will use the SMS API integrated with Appwrite for phone authentication.

Setting up Vonage

We’ll first need to create a Vonage account. To create an account, head over to Vonage’s website to signup to use the Communication APIs. The signup page looks like this:

Vonage signup page

After signing up, the Vonage API dashboard is displayed.

Vonage dashboard

We can send SMS, make voice calls and perform other functions from the dashboard. We can also see the API key and API Secret on the dashboard. We will keep them handy as we need them in the next step.

Installing Appwrite

We need to have an Appwrite server running locally on our system. We can set it up locally or using any of the one-click setups. To set it up locally, run the command below:

docker run -it --rm \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
    --entrypoint="install" \
    appwrite/appwrite:1.3.8
Enter fullscreen mode Exit fullscreen mode

The command installs Appwrite on our local machine. We will use the default configurations provided. An appwrite folder is created that contains a docker-compose.yml and .env file.

Once the installation is complete, we will update two environment variables in the .env file. The environment variables are _APP_SMS_PROVIDER and _APP_SMS_FROM.
_APP_SMS_PROVIDER is a connection string that helps Appwrite connect to the Vonage API, and _APP_SMS_FROM is our Vonage phone number or brand name.

_APP_SMS_PROVIDER=phone://[API KEY]:[API SECRET]@vonage
_APP_SMS_FROM=<YOUR PHONE NUMBER OR BRAND NAME>
Enter fullscreen mode Exit fullscreen mode

After updating the environment variables, we will restart our Appwrite server for the changes to take effect. To restart the server, we will run the command below from the appwrite folder that was created during installation.

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Then, we will navigate to http://localhost to sign up/sign in to our Appwrite account.

Appwrite signup page

Next, we will create a new project. We will name our project Appwrite-Vonage

Creating an Appwrite project

After the creation of the project, we will copy the Project ID and API Endpoint from the Settings tab, as we will need them in the next step.

Fetching Appwrite project credentials

Setting up our Next.js project

We need to create a Next.js project to see the phone authentication in action. Open the terminal and enter the following command to scaffold a Next.js project.

npx create-next-app phone-authentication
Enter fullscreen mode Exit fullscreen mode

We will have a series of prompts

Creating a Next.js application

Next, we will go to the project directory and start the development server on localhost:3000 with the commands below.

cd phone-authentication && npm run dev
Enter fullscreen mode Exit fullscreen mode

To use Appwrite in our application, we will install the Appwrite client-side SDK for web applications using the command:

npm install appwrite
Enter fullscreen mode Exit fullscreen mode

Next, we will create a .env.local file in our project root for our environment variables. We will add the following entries to our .env.local file.

PROJECT_ID=<APPWRITE_PROJECT_ID>
ENDPOINT=<APPWRITE_ENDPOINT>
Enter fullscreen mode Exit fullscreen mode

The values of the environment variables were obtained in the previous step when we created our Appwrite project. To access the environment variables within our application, we will modify the next.config.js file. The file should look like this.

/** @type {import('next').NextConfig} */
const nextConfig = {
    env: {
        PROJECT_ID: process.env.PROJECT_ID,
        ENDPOINT: process.env.ENDPOINT
    }
}
module.exports = nextConfig
Enter fullscreen mode Exit fullscreen mode

Setting up the Appwrite SDK

We will create our Appwrite service that will help us abstract all the SDK calls. Create a file src/appwrite.js and fill it in with the following content.

import { Client, Account } from 'appwrite';

const client = new Client();

client
    .setEndpoint(process.env.ENDPOINT)
    .setProject(process.env.PROJECT_ID);

const account = new Account(client);

export const getAccount = async () => account.get();

export const createPhoneSession = async (number) => account.createPhoneSession('unique()', number);

export const updatePhoneSession = async (userId, secret) => account.updatePhoneSession(userId, secret);

export const deleteSession = async () => account.deleteSession('current');
Enter fullscreen mode Exit fullscreen mode

The code above imports the Client and Account objects from Appwrite. Then, an instance of the Client object is created. We then set our Endpoint and Project ID that we obtained from the previous step into our client instance. Next, an instance of the Account object is also created.

We have created four different functions that return methods of the account instance. The methods are:

  • getAccount : gets the current logged-in user account.
  • createPhoneSession: creates a new phone session by sending an SMS with a secret key for creating a session. This is where we will see our Vonage SMS API action.
  • updatePhoneSession: completes the creation of the phone session by using the code sent via SMS and the userId gotten from the createPhoneSession function.
  • deleteSession : deletes a current session by logging out the user.

We will see these functions in action when we start building our UI in the next section.

Building our user interface

Our UI will consist of two pages, one for the home page and the other for the login.
First, we will modify the file src/app/page.js, which is our home page. The file should look like this

'use client';

import { getAccount, deleteSession } from '@/appwrite';
import React, {useEffect} from 'react';
import { useRouter } from 'next/navigation';

export default function Home() {
  const router = useRouter();

  useEffect(() => {
    (async() => {
      try{
        await getAccount();
      }
      catch(e){
        console.log(e);
        router.push('/login');
      }
    })()
  }, []);

  const logout = async () => {
    try {
      await deleteSession();
      router.push('/login');
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <div style={{textAlign: "center"}}>
      <h1>Welcome</h1>
      <button onClick={logout}>Logout</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The code above tries to fetch the user account details from Appwrite using the getAccount function in our src/appwrite.js file. If the user is not found, an exception is thrown and then caught in the catch block, and the user is redirected to the login page. There is also a logout button that deletes the current session and redirects us to the login page.

Next, we will create a file src/components/SignupForm.js, our signup form component. The content of the file should look like this.

'use client';

import { createPhoneSession } from "@/appwrite";
import React, { useState } from "react";
import VerificationForm from "./VerificationForm";

const SignupForm = () => {
  const [phoneNumber, setPhoneNumber] = useState("");
  const [isVerificationSent, setIsVerificationSent] = useState(false);
  const [userId, setUserId] = useState("");

  const handlePhoneNumberChange = (event) => {
    setPhoneNumber(event.target.value);
  }

  const handleSubmit = (event) => {
    try {
      const response = createPhoneSession(phoneNumber);
      response.then(user => {
        setUserId(user['userId'])
      })
      event.preventDefault();
      alert("verification code has been sent");
      setTimeout(() => {
        setIsVerificationSent(true);
      }, "1000");
    }
    catch (e) {
      console.log("error while creating phone session", e);
    }
  }

  return (
    <>
      {!isVerificationSent && (
        <form onSubmit={handleSubmit}>
          <div>
            <label style={{ marginRight: "10px" }}>
              Phone Number:
            </label>
            <input type="text" name="phoneNumber" value={phoneNumber} onChange={handlePhoneNumberChange} style={{ width: "150px" }} required />
          </div>
          <div style={{ textAlign: "center", marginTop: "20px" }}>
            <input type="submit" value="Get Code" style={{ width: "100px" }} />
          </div>
        </form>
      )}
      {isVerificationSent && (
        <VerificationForm userId={userId}/>
      )}
    </>
  )
}

export default SignupForm;
Enter fullscreen mode Exit fullscreen mode

The code above does the following:

  • Creates a form for inputting the phone number for verification.
  • Then we use the React hook useState to manage the state of the phone number and whether a verification code has been sent.
  • When Get Code button is clicked after inputting the phone number, we create a phone session using the createPhoneSession function and send a verification code to the phone number. If the verification code is sent successfully, the VerificationForm component is displayed.
  • Then, the userId property is passed from the response of the createPhoneSession function as a props to the VerificationForm component.

Next, we will create a new component src/component/verificationForm.js, which is where we will input the verification code sent via SMS to our phone number. The file should look like this:

'use client';

import { updatePhoneSession } from "@/appwrite";
import React, { useState } from "react";
import { useRouter } from 'next/navigation';

const VerificationForm = ({userId}) => {
  const [verificationCode, setVerificationCode] = useState("");
  const router = useRouter();

  const handleVerificationCodeChange = (event) => {
    setVerificationCode(event.target.value);
  };

  const handleSubmit = (event) => {
    try{
      updatePhoneSession(userId, verificationCode)
      router.push('/');
      event.preventDefault();
    }
    catch(e){
      console.log("error while updating phone session", e);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label style={{ marginRight: "10px" }}>
          Verification Code:
        </label>
        <input type="text" name="verificationCode" value={verificationCode} onChange={handleVerificationCodeChange} style={{ width: "150px" }} required />
      </div>
      <div style={{ textAlign: "center", marginTop: "20px" }}>
        <input type="submit" value="Verify" style={{ width: "100px" }} />
      </div>
    </form>
  )
};

export default VerificationForm;
Enter fullscreen mode Exit fullscreen mode

The code above creates a form where we will input our verification code. When the verification code is entered into the form, and the form is submitted, it calls the updatePhoneSession function that completes the sign in process. When we successfully sign in, we are redirected to the home page.

The video below shows a demo of the whole process. The phone number has been masked in the video.

We can also confirm from our Appwrite instance that the user was successfully logged in.

Logged in user

Conclusion

In this tutorial, we learned how to set up phone authentication using Appwrite and Vonage. We introduced Vonage and the services they provide. We also looked at how to set up a Vonage account. We learned how to install an Appwrite server locally and change the environment variables needed for phone authentication. We built a simple Next.js application to tie everything together.

Resources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .