How to authenticate users on your React app with Appwrite?

How to authenticate users on your React app with Appwrite?

Authenticating users via Email, Phone and Magic URL (with examples)

·

9 min read

If you know what Appwrite is and why we are using it, you can skip the Introduction and continue from Creating a new Appwrite Project.

Introduction

This blog is all about something we often do while using websites and apps - logging in.

Authenticating users means making sure the people trying to get into an app or site are who they say they are. It's like asking for an ID card before letting someone into a club (but on the internet).

While developing applications, you might want your users to be authenticated or logged in. This way, you can provide your users with a personalized experience and it's secure.

The Challenge?

Implementing authentication in your React app may seem straightforward if you have some backend knowledge. However, it involves several steps:

  • Database Connection: First, you need to connect to a database to store user credentials.

  • Backend Integration: You can use React Server components or create a separate backend app to communicate with the database.

  • User Models: Create models to structure user data in the database.

  • User Registration: For new users, you must validate their data, securely hash passwords, and store them.

  • User Login: For existing users, you need server-side logic to verify credentials, create sessions for authentication, and manage user sessions and inactivity.

  • Logout Option: Implement the ability for users to log out securely.

  • Password Recovery: Account for situations where users forget their passwords.

TLDR: Implementing Authentication can be complex and time-consuming.

The solution?

Enter Backend-as-a-service (BaaS)

Backend-as-a-service or Baas provides pre-built backend solutions such as authentication, databases, push notifications, storage, and more. It's like having a ready-made backend so you can channel your energy into the frontend and the heart of your app.

In a nutshell, someone else has done the backend heavy lifting, and you get to build the exciting parts of your app with ease.

Appwrite is one such Backend-as-a-service that's open-source so you can set it up locally, and host it on your own or even use their cloud services.

In this blog, we are going to authenticate users on React applications using Appwrite Cloud- which is a fully managed backend solution so you don't need to stress about deploying and scaling your infrastructure.

Creating a new Appwrite project

Visit Appwrite.io and authenticate using your preferred method.
Once done, You will be redirected to a dashboard console.

Click on Create Project, enter a name for your project and click on Create.
The next step is to add a platform, Since we're using React, you can choose the Web App option. On the following page, enter a name and add "localhost" as the hostname. This will enable your project (i.e. localhost) to communicate with the Appwrite API. Once you deploy, you can change the hostname to your production domain. Now, You can click on Skip optional steps as we will be covering that in a while.

Installing the Appwrite SDK

Now, moving to our code editor, Create a new React app, and once it's done, we'll install the Appwrite web SDK

npm install appwrite //for npm
yarn add appwrite //for yarn

Want to install with CDN? Click here

Now, Import the Appwrite module into the file you are planning to use the authentication APIs.

import { Client, Account, ID } from "appwrite";

Initializing your SDK

Initialize your Appwrite SDK by adding the below code.

const client = new Client();

client
    .setEndpoint('https://cloud.appwrite.io/v1')
    .setProject('[Project_ID]');

For the Project_ID, Head over to the Appwrite Dashboard. Click on Overview, and click on the badge that says Project ID next to your Project's name to copy it. And Paste it as a string argument to the setProject method.

The authenticated State

For any application with an authentication system, we can say that there are 2 states-
1. Where the user is authenticated.
2. Where the user is not authenticated.

Depending on these states, you may render different things or redirect users to a different route.

To keep track of the authenticated state, we're using the useState hook We'll initialize the state as false(indicating that the user is not authenticated) for now.

const [isAuthenticated, setIsAuthenticated] = useState(false);

Now, we need to create a new instance of the Account class using the client object.

const account = new Account(client);

Check if the user is authenticated

We call the get() method on the account object which is to get the currently logged-in user data.

const promise = account.get();

promise.then(function (response) {
    console.log(response); //If the promise was resolved.
    setIsAuthenticated(true);
    //Do something if user is logged in.
}, function (error) {
    console.log(error); //If the promise was rejected.
});
💡
Let's understand a little bit about the SDK here. The account class consists of all the authentication methods. When we call one of these methods on its object, a promise object is returned. To handle the promise, we call the then method which takes two functions as arguments. If the operation is successful, the first function will be executed but if the operation fails, the second function will be executed.

Basically, When we call the get() method and If the operation is successful, the first function will execute. Thus, we can conclude that the user is authenticated. And we can update the isAuthenticated state to true.

If the operation is failed, the second function will execute and the error argument will contain the error object. So here we can conclude that the user is not authenticated or not logged in.

Log out the user

Logging out the user means ending the user's active session. We can use the deleteSession() method to delete a session.

The method takes session ID as a string argument. We can also use the string "current" to delete the current user's session.

const promise = account.deleteSession("current");

    promise.then(
      function (response) {
        console.log(response); 
        setIsAuthenticated(false);
      },
      function (error) {
        console.log(error);
      }
    );

Upon a successful operation, we can update the authenticated state to false.


Appwrite offers various ways to authenticate users. We will explore three of them, along with their full example.

  1. Via Email and Password

  2. Via Phone

  3. Via Magic URL

  1. Email and Password Authentication

To log in via email and a password, the first step is to sign up or create a new account.

The create() method takes 4 string arguments-

  1. userId - We'll use ID.unique() which will generate a new unique ID automatically for any new user created. You can also choose your own unique ID.

  2. email - The user email

  3. password - A password of at least 8 characters

  4. name(optional) - A username of max-length of 128 characters.

The response will contain the account object.

const promise = account.create(ID.unique(), "name@mail.com", "password", "name");

    promise.then(
      function (response) {
        console.log(response);
        // Do something if the user account is created.
      },
      function (error) {
        // Do something or show an error if the operation was failed.
        window.alert(error.message); 
      }
    );

The above method can be used to register a new user. But to log in a user, we'll have to call the createEmailSession().

The method takes just 2 arguments- An email and a password.

const promise = account.createEmailSession("name@mail.com", "password");

    promise.then(
      function (response) {
        console.log(response); 
        setAuthenticated(true);
        // Do something if the user session is created.
      },
      function (error) {
        // Do something or show an error if the operation was failed.
        window.alert(error.message);
      }
    );

For the operation to be successful, the user must already have an account. If successful, the response will contain a session object and we can update our state to true.

Full example on Email and password authentication.

More on Email and Password authentication


  1. Phone authentication

It is one of the more common authentication methods these days. It allows users to create accounts and log in using a verification SMS sent to their phone.

There are 2 steps-
Step 1: Get the user's phone number.
Step 2: Send a secret SMS to that number.

We'll use the method createPhoneSession(), which takes 2 arguments-

  1. the userID

  2. And the phone number (don't forget the "+" and country code!)

const promise = account.createPhoneSession(ID.unique(), '+919999999999');

promise.then(function (response) {
    console.log(response);
    // Get the secret from user for further verfication.
}, function (error) {
    console.log(error);
});

If the operation is successful, a secret key is sent to the user's phone via SMS.

Now the 2nd step is to validate the secret key from the SMS and log the user in. The method updatePhoneSession() takes 2 arguments-

  1. the userID (from the response of the previous method)

  2. And the secret (The 6-digit number received as SMS by the user).

const promise = account.updatePhoneSession("[The above uniqueID]", '[SECRET]');

promise.then(function (response) {
    console.log(response); // Success
    isAuthenticated(true);
}, function (error) {
    console.log(error); // Failure
});

So if the operation is successful, we can update the isAuthenticated state to true and log the user in!

Full example on Phone authentication.

More on Phone authentication


  1. Magic URL

Magic URL is again one of the more popular authentication methods these days where a user would provide their email and they would receive an email with a link. When they click that link, they're in!.

The first step is to get the email from the user and send a link to that email address. The method createMagicURLSession() takes 3 arguments-

  1. A unique ID (for new users),

  2. An email address,

  3. And an optional URL for redirecting the user when they click on the link

const promise = account.createMagicURLSession(ID.unique(), "youremail@mail.com", "http://localhost:3000");

    promise.then(
      function (response) {
        console.log(response);
        window.alert("Verification email sent");
        //Update the user to check their inbox and click on the verification link to login.
      },
      function (error) {
        window.alert(error.message); 
      }
    );

Tip: Ensure that the URL you pass as an argument in the createMagicURLSession() method matches the hostname you specified when setting up your web platform.

If the operation is successful, an email with a link is sent to the user. The link consists of the URL you passed as an argument, along with the user ID and secret value provided as query parameters. It may look something like this:

https://[the-URL-you-passed]?userId=[the-userId]&secret=[the-secret]

So when the user clicks on this link, we want the user to get logged in. But how do we do it?
What we can do is, before our page is rendered, we need to check if the URL contains userId and the secret as parameters, If it does, we can call the method updateMagicURLSession() to log in the user by passing the userId and the secret value (from the query parameters) as arguments.

We can use URLSearchParams() to get the userID and secret values from the URL.

const urlParams = new URLSearchParams(window.location.search);
useEffect(() => {
    // If "userId" parameter is present in the URL, it can be concluded that the user was redirected here from the verfication mail.
    // In this case, create a session for the user. 
    if(urlParams.get("userId")){
      const userId = urlParams.get("userId");
      const secret = urlParams.get("secret");
      const promise = account.updateMagicURLSession(userId, secret);

      promise.then(function (response) {
          console.log(response); 
          getUser();
      }, function (error) {
          console.log(error); // Failure
      });
    }
}, [searchParams]);

If the link is valid and hasn't expired yet, the operation should be successful and we can log in the user by updating the state.

Full example on Magic URL authentication.

More on Magic URL authentication


Happy Authenticating and happy coding!