<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samuel Danquah</title>
    <description>The latest articles on DEV Community by Samuel Danquah (@samiell).</description>
    <link>https://dev.to/samiell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F396308%2F3d384de5-c8b7-4ed1-8ce2-5162add5cbe9.png</url>
      <title>DEV Community: Samuel Danquah</title>
      <link>https://dev.to/samiell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samiell"/>
    <language>en</language>
    <item>
      <title>Firebase and Fauna B2C Auth App in AWS Land</title>
      <dc:creator>Samuel Danquah</dc:creator>
      <pubDate>Tue, 14 Dec 2021 05:34:11 +0000</pubDate>
      <link>https://dev.to/samiell/firebase-and-fauna-b2c-auth-app-in-aws-land-1f4f</link>
      <guid>https://dev.to/samiell/firebase-and-fauna-b2c-auth-app-in-aws-land-1f4f</guid>
      <description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/samiell/authentication-with-firebase-and-fauna-introduction-1nnh"&gt;previous article&lt;/a&gt;, we explored the authentication offerings of &lt;a href="https://firebase.google.com/docs/auth"&gt;Firebase&lt;/a&gt; to turbocharge your next Fauna-powered application and introduce the building blocks to architect applications for the client-serverless paradigm. Now we will build a serverless authentication &lt;a href="https://smallbusiness.chron.com/explain-business-consumer-model-2258.html"&gt;business-to-consumer&lt;/a&gt; (B2C) backend with services from the AWS ecosystem. But first, let's look at a few prerequisites needed to begin our journey:&lt;/p&gt;

&lt;h3 id="requirements"&gt;Requirements&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/#requirements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=free"&gt;Register for a Firebase account&lt;/a&gt; if you don't have one and enjoy the &lt;a href="https://firebase.google.com/pricing"&gt;free tier&lt;/a&gt;. Payment information is not required to begin until you upgrade your plan.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;Create a Fauna account&lt;/a&gt; and benefit from the&lt;a href="https://fauna.com/pricing?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt; free plan&lt;/a&gt; offerings. Payment information is not needed to get started.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.aws.amazon.com/billing/signup"&gt;Sign up for an AWS account&lt;/a&gt; if you don't already have an account with these &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/"&gt;instructions&lt;/a&gt;. A payment method is required to create an account and access the AWS &lt;a href="https://aws.amazon.com/free/"&gt;free tier&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="authentication-requirements-and-strategy"&gt;Authentication Requirements and Strategy&lt;/h2&gt;

&lt;p&gt;The client (frontend) application will communicate with our backend through an &lt;a href="https://en.wikipedia.org/wiki/API"&gt;API&lt;/a&gt;. AWS provides &lt;a href="https://aws.amazon.com/api-gateway/"&gt;API Gateway&lt;/a&gt;, which enables us to create, maintain, and secure our authentication API at any scale. API Gateway offers two &lt;a href="https://restfulapi.net/"&gt;RESTful API&lt;/a&gt; options for our B2C authentication application - &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html"&gt;HTTP&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html"&gt;REST&lt;/a&gt;. Which option do we utilize for our business-to-consumer backend application? These two preliminary questions hold the clue to this puzzle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where are the target users located?&lt;/li&gt;
&lt;li&gt;How do users access the authentication API?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This comprehensive &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html"&gt;guide&lt;/a&gt; helps you decide between the two for your application needs. For this sample application, we are working with the following assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our users reside in a single continent.&lt;/li&gt;
&lt;li&gt;Our users will access the authentication API from our client application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence, we utilize the HTTP API option, which provides low latency and cheap regional API to proxy our business logic for authentication. &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; will encapsulate this business logic. To make working with the various AWS services a breeze, we will use the &lt;a href="https://www.serverless.com/"&gt;serverless framework&lt;/a&gt;. Let's build this faunabase authentication application.&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wv0R4Blx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkl274s4g6ysqoguslw8.jpg" alt="Serverless Authentication App in AWS ecosystem" width="720" height="540"&gt;&lt;span&gt;&lt;u&gt;Serverless Authentication App in AWS ecosystem&lt;/u&gt;&lt;/span&gt;
&lt;/center&gt;

&lt;h2 id="faunabase-authentication-app"&gt;Faunabase Authentication App&lt;/h2&gt;

&lt;p&gt;We install the serverless framework and get a free dashboard account from this &lt;a href="https://www.serverless.com/framework/docs/getting-started/#set-up-your-free-dashboard-account"&gt;guide&lt;/a&gt;. If you don't have &lt;a href="https://nodejs.org/en/download/package-manager/"&gt;Node&lt;/a&gt; installed, be sure to do that first. The recommended  &lt;a href="https://nodejs.org/en/download/"&gt;NodeJS version&lt;/a&gt; for installation is the long-term support (LTS) version. Next, we create our faunabase-auth application using the AWS NodeJS template with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;nodejs&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;center&gt;or the shorthand version:&lt;/center&gt;
&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;sls&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;nodejs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open the &lt;em&gt;faunabase-auth&lt;/em&gt; folder with your text editor and you should see the results below:&lt;/p&gt;


&lt;center&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9oMCt_qg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptfs3tu97v48kbegs7ag.jpg" alt="faunabase-auth folder in code editor" width="720" height="540"&gt;&lt;/center&gt;
&lt;br&gt;
The command we run created two files: &lt;code&gt;serverless.yml&lt;/code&gt; and &lt;code&gt;handler.js&lt;/code&gt;. The &lt;code&gt;serverless.yml&lt;/code&gt; file serves as the control panel to manage all our AWS infrastructure resources. Let's go through the default configuration and make a few customizations. &lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i_3aSKEs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8cg1o7i2d9pbw7hqna4r.jpg" alt="default serverless.yml file" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
We will change the provider runtime from &lt;code&gt;nodejs12.x&lt;/code&gt; to the current LTS version, which is &lt;code&gt;nodejs14.x&lt;/code&gt;. We add a region and stage to the provider and set the default stage to &lt;code&gt;dev&lt;/code&gt; and the default region to &lt;code&gt;us-east-1&lt;/code&gt; for this demo. Set this region to the location of your end-users. You should get the following results:

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pwSDMKOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95i4qiojuihf5lban8n4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pwSDMKOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95i4qiojuihf5lban8n4.jpg" alt="serverless.yml use node14.X" width="720" height="540"&gt;&lt;/a&gt;&lt;br&gt;
Let's clear all the comments to tidy up the &lt;code&gt;serverless.yml&lt;/code&gt; file. Next, we set up our authentication API and integrate it with the business logic encapsulated in &lt;a href="https://aws.amazon.com/lambda/"&gt;Lambda&lt;/a&gt; functions.&lt;/p&gt;

&lt;h3 id="setting-up-firebase-project"&gt;Setting up Firebase project&lt;/h3&gt;

&lt;p&gt;Before we proceed to create our API, let's create a Firebase project and Fauna database. Open the &lt;a href="https://console.firebase.google.com"&gt;Firebase console&lt;/a&gt; and click the &lt;code&gt;Create a project&lt;/code&gt; button. &lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oUV6ZKct--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8a0ruzh6orbl4owpomzz.jpg" alt="Firebase console" width="720" height="540"&gt;&lt;br&gt;
&lt;/center&gt;
&lt;br&gt;
Enter &lt;code&gt;faunabase&lt;/code&gt; for the project name and click continue.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IBm_yIkv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwqnftv0hm6n9ainn5nb.jpg" alt="create firebase project" width="720" height="540"&gt;&lt;br&gt;
&lt;/center&gt;
&lt;br&gt;
Click &lt;code&gt;Continue&lt;/code&gt; and disable Google Analytics for this project. Finally, click on the &lt;code&gt;Create project&lt;/code&gt; button to complete the process.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YQ8KGHaO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvpm8m0sjzukf3bh6png.jpg" alt="Create firebase project 2" width="720" height="540"&gt;&lt;br&gt;
&lt;/center&gt;
&lt;br&gt;
Click &lt;code&gt;Continue&lt;/code&gt; to proceed to the project dashboard.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t1IlMZpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk7q3fnh7lt0e8b6cmua.jpg" alt="create firebase project 3" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Click on the &lt;code&gt;Settings gear icon&lt;/code&gt; button in the sidebar menu and select &lt;code&gt;Project settings&lt;/code&gt;.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h0S6bA_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xwv6zmz29kexe24abow.jpg" alt="firebase settings" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Copy the Project ID for the project and save it. We need it later for the authentication API.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CvXLE0R4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7uk7wrooh1vxgmpy4b2g.jpg" alt="Image description" width="720" height="540"&gt;
&lt;/center&gt;

&lt;h3 id="creating-a-database-in-fauna"&gt;Creating a Database in Fauna&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dashboard.fauna.com/?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;Login to your Fauna Dashboard&lt;/a&gt;, and click on the &lt;code&gt;Create Database&lt;/code&gt; button to create a new database for this demo. &lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H1xKayUJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9zo7qhotlasovfeqm72.jpg" alt="create fauna database" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Type &lt;code&gt;faunabase-b2c&lt;/code&gt; as the database name, and select the &lt;code&gt;United States&lt;/code&gt; option for the region group. Click on the &lt;code&gt;Create&lt;/code&gt; button to complete the process. &lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U9vOss0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0on2gk9nlvh56fdrmvca.jpg" alt="create fauna database 2" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
We chose the United States region for data locality and better latency since our authentication API (AWS) is in the United States. Once the database is available, select the &lt;code&gt;Security&lt;/code&gt; tab and choose &lt;code&gt;New Key&lt;/code&gt; to create your first key. Accept the defaults of the current database and &lt;code&gt;Server&lt;/code&gt; for the role. Enter AWS Serverless Framework as the &lt;code&gt;Key Name&lt;/code&gt;, and choose &lt;code&gt;Save&lt;/code&gt; to create a new key. &lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SVJcJ9XJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j0dsel86foujk7c8chpn.jpg" alt="create fauna database 3" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Copy the generated secret. We are going to store it in the next step.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I7StyTxS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ymc96tahz6v7ve6a7alr.jpg" alt="create fauna database 4" width="800" height="600"&gt;
&lt;/center&gt;

&lt;h3 id="securing-secret-with-aws-parameter-store"&gt;Securing Secret with AWS Parameter Store&lt;/h3&gt;

&lt;p&gt;The secret we generated in the previous step provides unlimited access to the newly created database. We have to store it safely and provide access to our authentication API logic. Hence, we use &lt;a href="https://console.aws.amazon.com/systems-manager/parameters"&gt;Parameter Store&lt;/a&gt; to safeguard the secret using &lt;code&gt;SecureString&lt;/code&gt; value, which encrypts the key accessed from the authentication API. Click on the &lt;code&gt;Create parameter&lt;/code&gt; button.&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rtFfKkwl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4d7q5bimu7kptralqns.jpg" alt="aws ssm" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Parameter Store creates the &lt;code&gt;fauna-secret&lt;/code&gt; parameter and displays it on the &lt;code&gt;My parameters&lt;/code&gt; tab. We will access this secret from our authentication logic encapsulated in our &lt;a href="https://aws.amazon.com/lambda/"&gt;lambda functions&lt;/a&gt;.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---g_hw-1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6uf9wo2vlwb1ymgjfb8.jpg" alt="aws ssm 2" width="720" height="540"&gt;
&lt;/center&gt;

&lt;h3 id="configure-aws-credentials-for-serverless-framework"&gt;Configure AWS Credentials for Serverless Framework&lt;/h3&gt;

&lt;p&gt;The Serverless Framework needs access to our AWS cloud account to create and manage resources on our behalf. Since we are using the serverless command-line interface (CLI) to set up our AWS credentials, we use an AWS profile and create an &lt;a href="https://console.aws.amazon.com/iamv2/home#/users"&gt;IAM&lt;/a&gt; user for the serverless framework. Ensure that you are logged in to your AWS account to proceed. Click on &lt;code&gt;Users&lt;/code&gt; and then &lt;code&gt;Add users&lt;/code&gt;. Type &lt;code&gt;faunabase-serverless&lt;/code&gt; for the User name and tick the &lt;code&gt;Access key&lt;/code&gt; checkbox to enable programmatic access. Click &lt;code&gt;Next: Permissions&lt;/code&gt; to go to the permissions page.&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0bqacct_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5otcptgdjsyjnkncw8v.jpg" alt="aws credential" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Click on &lt;code&gt;Attach existing policies directly&lt;/code&gt;. Select &lt;code&gt;the AdministratorAccess&lt;/code&gt; option in the list, then click &lt;code&gt;Next: Tags&lt;/code&gt;.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Gm73beg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h53elesug6g5v0d86h9y.jpg" alt="aws credential 2" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Add optional tags in the next step and select &lt;code&gt;Review&lt;/code&gt; to complete the process. Check to make sure everything looks good and click &lt;code&gt;Create user&lt;/code&gt;. View and copy the &lt;code&gt;Access Key ID&lt;/code&gt; &amp;amp; &lt;code&gt;Secret Access Key&lt;/code&gt; as we will use them in the next step to configure our serverless CLI.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtFLm1Cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6y6z72jmxlw4eb18yb9.jpg" alt="aws credential 3" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
It is important to note that the above step grants unrestricted access to resources in the AWS account and makes starting our application easy. The recommended security practice is to provide fine-grained permissions through &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html"&gt;IAM policies&lt;/a&gt; to match the scope of the application. Configure the serverless CLI with the following command using the copied values from the previous step:&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="nx"&gt;copied&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="nx"&gt;copied&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, we proceed to set up API Gateway and integrate it with Lambda functions.&lt;/p&gt;

&lt;h3 id="configuring-api-gateway"&gt;Configuring API Gateway&lt;/h3&gt;

&lt;p&gt;Our authentication API has two endpoints: signup and login. The client applications will use the Firebase client SDK to request an &lt;a href="https://firebase.google.com/docs/auth/admin/verify-id-tokens"&gt;ID token&lt;/a&gt; - a &lt;a href="https://jwt.io/"&gt;JSON Web Token&lt;/a&gt; (JWT). It will send this token to our authentication API, which will verify the integrity of the JWT before calling the business logic at the API endpoint. But first, let us do a little housekeeping in our &lt;code&gt;faunabase-auth&lt;/code&gt; folder. Create a folder at the root location and name it &lt;code&gt;src&lt;/code&gt;. Inside the &lt;code&gt;src&lt;/code&gt; folder, we will create the following folders - &lt;code&gt;functions&lt;/code&gt;, &lt;code&gt;libs&lt;/code&gt;. &lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wmrt3M-r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dl7toolv97hmkbdo9w7q.jpg" alt="faunabase-auth folder" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Delete the &lt;code&gt;handler.js&lt;/code&gt; file. Next, let us set up our API in the &lt;code&gt;serverless.yml&lt;/code&gt; file and connect it with the functions. In the &lt;a href="https://yaml.org/"&gt;YAML&lt;/a&gt; file under &lt;code&gt;provider&lt;/code&gt;, add the &lt;code&gt;apiName&lt;/code&gt; property and give it a value of &lt;code&gt;auth-b2c-api&lt;/code&gt;. This value is our API Gateway name.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xNAtZbDY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oq4ipexeo112begxjca2.jpg" alt="faunabase-auth folder 2" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Delete the &lt;code&gt;hello&lt;/code&gt; function with its &lt;code&gt;handler&lt;/code&gt; property and add the &lt;code&gt;httpApi&lt;/code&gt; property under &lt;code&gt;provider&lt;/code&gt;. Next, set the &lt;code&gt;cors&lt;/code&gt; and &lt;code&gt;metrics&lt;/code&gt; properties to &lt;code&gt;true&lt;/code&gt; under &lt;code&gt;httpApi&lt;/code&gt;. We enabled &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;Cross-Origin Resource Sharing (CORS)&lt;/a&gt; and &lt;a href="https://aws.amazon.com/cloudwatch/"&gt;Cloudwatch&lt;/a&gt; metrics by setting &lt;code&gt;cors&lt;/code&gt; and &lt;code&gt;metrics&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. With CORS, we can specify which clients can access our API and what actions they can take. We also set up monitoring and alerts for our API with Cloudwatch. We enable logging for API Gateway - type &lt;code&gt;logs&lt;/code&gt; under &lt;code&gt;provider&lt;/code&gt;. Next, set &lt;code&gt;httpApi&lt;/code&gt; under &lt;code&gt;logs&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. Let's set up authorization logic in the authentication API. Firebase has an &lt;a href="https://openid.net/connect/"&gt;OIDC&lt;/a&gt; discovery document that will help us verify users from our firebase project in our authentication API. First, we will need the firebase project ID from earlier when setting up our firebase account. Go to the &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL"&gt;URL&lt;/a&gt; below and replace project-id with your firebase project ID.&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//securetoken.google.com/project-id/.well-known/openid-configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this demo, the project ID from the earlier is &lt;code&gt;faunabase-687bd&lt;/code&gt;, so we visit the link below. &lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_ofDzQ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/056hsfnwbhm8w5iyeyaz.jpg" alt="faunabase-auth folder 3" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Our firebase project can accept and verify ID tokens from the discovery document. In &lt;code&gt;serverless.yml&lt;/code&gt;, create &lt;code&gt;authorizers&lt;/code&gt; property under &lt;code&gt;httpApi&lt;/code&gt;. Under &lt;code&gt;authorizers&lt;/code&gt;, we name our authorizer as &lt;code&gt;firebaseB2CAuthorizer&lt;/code&gt;. We have to set an identity source, issuer URL, and audience. Under &lt;code&gt;firebaseB2CAuthorizer&lt;/code&gt;, set &lt;code&gt;identitySource&lt;/code&gt; to &lt;code&gt;$request.header.Authorization&lt;/code&gt;, &lt;code&gt;issuerUrl&lt;/code&gt; to  &lt;code&gt;https://securetoken.google.com/project-id&lt;/code&gt;, and &lt;code&gt;audience&lt;/code&gt; to &lt;code&gt;project-id&lt;/code&gt;. Remember to replace project-id with your Firebase project ID. Change the &lt;code&gt;frameworkVersion&lt;/code&gt; value from &lt;code&gt;'2'&lt;/code&gt; to &lt;code&gt;'&amp;gt;=2.66.1 &amp;amp;lt;4.0.0'&lt;/code&gt; - serverless framework at the time of this writing is transitioning from version 2 to version 3 which is currently in beta. &lt;a href="https://www.serverless.com/blog/serverless-framework-v3-beta"&gt;Version 3&lt;/a&gt; comes with breaking changes to the &lt;code&gt;serverless.yml&lt;/code&gt; configuration. The current version of the stable serverless node library is &lt;code&gt;2.66.1&lt;/code&gt;. The current code can upgrade to the version 3 package when it becomes stable.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UfVYxBhc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inzsv02600wuvub74tir.jpg" alt="faunabase-auth folder 4" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
The frontend client must pass a valid Firebase ID token in the authorization header of the request. The identity source tells our authentication API where to find the ID token, a JWT to perform validation checks before providing access to the business logic encapsulated in a function. We have configured our authentication API. Now let's connect it to our two authentication endpoints - &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt;. We create the first endpoint for signing up. Navigate to the &lt;code&gt;src&lt;/code&gt; folder --&amp;gt; then the &lt;code&gt;functions&lt;/code&gt; folder. Create a &lt;code&gt;signup.js&lt;/code&gt; and &lt;code&gt;login.js&lt;/code&gt; file inside the functions folder.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9BhwpO_z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tb88hqkh3961su2xy39c.jpg" alt="faunabase-auth folder 5" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Enter the code below in both the signup.js and login.js.&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We have set up the boilerplate code for our two endpoints - &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt;. Let's link the business logic to the endpoints in &lt;code&gt;serverless.yml&lt;/code&gt;, beginning with the &lt;code&gt;signup&lt;/code&gt; endpoint. Enter &lt;code&gt;signup&lt;/code&gt; under &lt;code&gt;functions&lt;/code&gt;. Next, we write the following code under &lt;code&gt;signup:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;handler&lt;/code&gt; property sets the source file location for the business logic&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;description&lt;/code&gt; property provides information about the business logic&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;memorySize&lt;/code&gt; property sets the memory size for this business logic in MB&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;timeout&lt;/code&gt; property determines how long the business logic is allowed to run before termination in seconds&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;events&lt;/code&gt; property shows which AWS events can trigger the business logic to execute&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;httpApi&lt;/code&gt; property under &lt;code&gt;events&lt;/code&gt; tells us that the API Gateway &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html"&gt;HTTP API&lt;/a&gt; event can run this business logic&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;method&lt;/code&gt; property under &lt;code&gt;httpApi&lt;/code&gt; sets which HTTP method can access the API endpoint&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;path&lt;/code&gt; property under &lt;code&gt;httpApi&lt;/code&gt; sets the pathname for the API endpoint&lt;/li&gt;
&lt;li&gt;Under &lt;code&gt;httpApi&lt;/code&gt;, the &lt;code&gt;authorizer&lt;/code&gt; property with &lt;code&gt;name&lt;/code&gt;, its child property allows us to use the &lt;code&gt;firebaseB2CAuthorizer&lt;/code&gt; we configured earlier to provide a security layer for the endpoint. Requests without a valid ID token from our Firebase project cannot execute the underlying business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have set up our signup endpoint for authentication API. Let's repeat the same process for our login endpoint. The final code configuration should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;
  &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to make the database secret in Fauna available to the &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt; functions from the AWS Parameter Store. The serverless framework provides access to plugins. We will install the Serverless SSM Fetch plugin to help us retrieve the stored fauna secret in AWS. Run the command below to install the plugin as an &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the installed plugin to the serverless.yml file with the following code at the root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add a custom variable called &lt;code&gt;FAUNA_SECRET&lt;/code&gt; as a property of &lt;code&gt;custom.serverlessSsmFetch&lt;/code&gt; with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;serverlessSsmFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;FAUNA_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;serverlessSsmFetch&lt;/code&gt; is an accessor provided by the plugin we installed, which decrypts the encrypted fauna-secret parameter with the  &lt;code&gt;~true&lt;/code&gt; flag and automatically injects the AWS Parameter Store Fauna server key as an environment variable, accessible in all our functions. Add the &lt;code&gt;logRetentionInDays&lt;/code&gt; property under &lt;code&gt;provider&lt;/code&gt; and set the number of days to retain the Cloudwatch logs we set up for the authentication API. We proceed to code the business logic for signup and login. The current &lt;code&gt;serverless.yml&lt;/code&gt; file should have the following results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;
&lt;span class="nx"&gt;frameworkVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;=2.66.1 &amp;lt;4.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nodejs14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
  &lt;span class="nx"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;b2c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;
  &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;authorizers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;identitySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Authorization&lt;/span&gt;
        &lt;span class="nx"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//securetoken.google.com/faunabase-687bd&lt;/span&gt;
        &lt;span class="nx"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;687&lt;/span&gt;&lt;span class="nx"&gt;bd&lt;/span&gt;
  &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;logRetentionDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;
  &lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;lambdaHashingVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20201221&lt;/span&gt;

&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;

&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;serverlessSsmFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;FAUNA_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;
  &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3 id="business-logic-in-lambda-functions"&gt;Business Logic in Lambda Functions&lt;/h3&gt;

&lt;p&gt;We need to create a collection in our database called &lt;code&gt;User&lt;/code&gt; to house each user’s document. We access the database from our client application through &lt;a href="https://docs.fauna.com/fauna/current/api/fql/?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;FQL&lt;/a&gt; or &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;GraphQL&lt;/a&gt; in Fauna. FQL can access resources created with the GraphQL API but not the converse. We create a &lt;code&gt;main.gql&lt;/code&gt; file which we will upload to bootstrap resources for our database in this demo. Inside the &lt;code&gt;src&lt;/code&gt; folder, navigate to the &lt;code&gt;libs&lt;/code&gt; folder and create a new folder with &lt;code&gt;fauna&lt;/code&gt;. Create a folder named &lt;code&gt;schema&lt;/code&gt; within the &lt;code&gt;fauna&lt;/code&gt; folder. Create the &lt;code&gt;main.gql&lt;/code&gt; file inside the &lt;code&gt;schema&lt;/code&gt; folder and type the following code in the &lt;a href="https://graphql.org/learn/schema/"&gt;schema&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ADMIN&lt;/span&gt;
  &lt;span class="nx"&gt;MEMBER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Mutation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;logoutUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we started with creating a collection in our database using &lt;code&gt;type User&lt;/code&gt; with the &lt;code&gt;role&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; attributes. We also set a uniqueness constraint on the email field to validate against duplicates using the&lt;a href="https://docs.fauna.com/fauna/current/api/graphql/directives/d_unique?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt; &lt;code&gt;@uinque&lt;/code&gt;&lt;/a&gt; directive. The &lt;code&gt;role&lt;/code&gt; attribute will allow us to implement fine-grained access control for the database using &lt;a href="https://docs.fauna.com/fauna/current/security/roles?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;User Defined Roles&lt;/a&gt;. The &lt;code&gt;logoutUser&lt;/code&gt; mutation allows our client application to revoke the access token from the frontend. We will implement the logout logic in the &lt;code&gt;logout_user&lt;/code&gt; &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/functions?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;User Defined Function&lt;/a&gt; (UDF).&lt;/p&gt;

&lt;p&gt;On the &lt;a href="https://dashboard.fauna.com/?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;Fauna dashboard&lt;/a&gt;, Select the &lt;code&gt;faunabase-b2c&lt;/code&gt; database and click the &lt;code&gt;GraphQL&lt;/code&gt; tab. Click on the &lt;code&gt;IMPORT SCHEMA&lt;/code&gt; button to upload the schema we created.&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fj9W8FpU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bnmlqz7vr64nsfc8vfvs.jpg" alt="faunabase-auth folder 6" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
If successful, you should obtain the following results:&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1tAjIR6G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sgdv6vooc72b9inty9jr.jpg" alt="faunabase-auth folder 7" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Click on &lt;code&gt;Functions&lt;/code&gt; located to the left of your Fauna dashboard. We will update the logic for the &lt;code&gt;logout_user&lt;/code&gt; which was created when we uploaded our graphql schema.&lt;br&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E7qzmSWS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3nlna0liaz5jlc6gwbgz.jpg" alt="faunabase-auth folder 8" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
Clear the contents of &lt;code&gt;Function Body&lt;/code&gt; and replace it with the following code:&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;Logout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This logout logic revokes all authenticated token access for the current user. The client application can access this functionality from the GraphQL API. Click on the &lt;code&gt;SAVE&lt;/code&gt; button to update the logout logic.&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ckjebWvX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4i6eeul9ekt70xt7lrnf.jpg" alt="faunabase-auth folder 9" width="720" height="540"&gt;
&lt;/center&gt;

&lt;h4 id="setting-up-libraries"&gt;Setting up Libraries&lt;/h4&gt;

&lt;p&gt;Next, let’s install the &lt;code&gt;fauna&lt;/code&gt; driver with this command inside our codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt; &lt;span class="nx"&gt;faunadb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;firebase&lt;/code&gt; folder inside the &lt;code&gt;libs&lt;/code&gt; folder in addition to the &lt;code&gt;fauna&lt;/code&gt; folder. Also, create a &lt;code&gt;util.js&lt;/code&gt; file inside the &lt;code&gt;libs&lt;/code&gt; folder. The &lt;code&gt;fauna&lt;/code&gt; folder will house all code about our Fauna database. The &lt;code&gt;firebase&lt;/code&gt; folder will house functionality on Firebase authentication. The &lt;code&gt;util.js&lt;/code&gt; file will contain all reusable utility functions which we will use in the login and signup endpoints. Paste the following code inside &lt;code&gt;util.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;response&lt;/code&gt; function accepts an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP#:~:text=Hypertext%20Transfer%20Protocol%20(HTTP)%20is,be%20used%20for%20other%20purposes."&gt;HTTP&lt;/a&gt; code and message and returns the response to the client application requesting the authentication API endpoint. The client application will send a request to API Gateway which will validate the JWT in the authorization header and call the AWS Lambda function. Inside the &lt;code&gt;firebase&lt;/code&gt; folder, create an &lt;code&gt;index.js&lt;/code&gt; which will contain logic to decode the JWT. Run the following code to install the &lt;code&gt;jwt-decode&lt;/code&gt; node library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the following code in the &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jwt-decode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;decodeJWT&lt;/code&gt; function accepts a JWT as input and decodes the payload. Inside the &lt;code&gt;fauna&lt;/code&gt; folder, create a &lt;code&gt;function.js&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TimeAdd&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faunadb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginUDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Tokens&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nx"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unique_User_email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nx"&gt;email&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TimeAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hour&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerUDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MEMBER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loginUDF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerUDF&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file contains the &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt; business logic. The &lt;code&gt;loginUDF&lt;/code&gt; exchanges the decoded Firebase JWT token for an access token to the Fauna database which is valid for one hour. The &lt;code&gt;registerUDF&lt;/code&gt; creates a new user in the Fauna database with the decoded email input from the Firebase JWT and assigns a basic role of &lt;code&gt;MEMBER&lt;/code&gt;. Finally, we export the &lt;code&gt;loginUDF&lt;/code&gt; and &lt;code&gt;registerUDF&lt;/code&gt; with &lt;code&gt;module.exports&lt;/code&gt;. Next, we set up our Fauna client to query the database - create an &lt;code&gt;index.js&lt;/code&gt; file and input the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;faunadb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faunadb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// import faunadb driver&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loginUDF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerUDF&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// import UDFs&lt;/span&gt;
&lt;span class="c1"&gt;// create new fauna client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;faunadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FAUNA_SECRET&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// get Fauna access token from verified and decoded JWT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginUDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// create new Fauna user from verified and decoded JWT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;registerUDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above imports the Fauna driver as well as the user-defined functions (UDFs) we created in &lt;code&gt;function.js&lt;/code&gt;. A new Fauna client is instantiated to perform the login and register operations in our database. Finally, we export the &lt;code&gt;login&lt;/code&gt; and &lt;code&gt;register&lt;/code&gt; functions for use in our API endpoints. Next, we finalize the &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt; lambdas in the &lt;code&gt;functions&lt;/code&gt; folder.&lt;/p&gt;

&lt;h4 id="signup-lambda"&gt;Signup Lambda&lt;/h4&gt;

&lt;p&gt;Open the &lt;code&gt;signup.js&lt;/code&gt; file inside the &lt;code&gt;functions&lt;/code&gt; folder. We import the &lt;code&gt;register&lt;/code&gt; function, we just created from the index.js file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/fauna&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also import Firebase JWT decode function with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we import the &lt;code&gt;response&lt;/code&gt; function from the &lt;code&gt;util.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;handler&lt;/code&gt; function accepts &lt;code&gt;event&lt;/code&gt; and &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html"&gt;&lt;code&gt;context&lt;/code&gt;&lt;/a&gt; as inputs. The &lt;code&gt;event&lt;/code&gt; input contains &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html"&gt;&lt;code&gt;information&lt;/code&gt;&lt;/a&gt; such as the JWT token in the authorization header from the API invoker. The context input provides information on the invocation, function, and execution environment. Since AWS Lambda utilizes a pay-as-you-go model, we want to timeout our function immediately after receiving a response from our promise instead of waiting for the event loop to complete. We configure the runtime to send the response immediately with &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html"&gt;&lt;code&gt;context.callbackWaitsForEmptyEventLoop&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;false&lt;/code&gt; inside the &lt;code&gt;handler&lt;/code&gt; function to obtain the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/fauna&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callbackWaitsForEmptyEventLoop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code after &lt;code&gt;context.callbackWaitsForEmptyEventLoop&lt;/code&gt; inside the &lt;code&gt;handler&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization header not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we decode the JWT located in &lt;code&gt;event.headers.authorization&lt;/code&gt; and store it in &lt;code&gt;user&lt;/code&gt; if the authorization header exists, else we exit the function with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401"&gt;401 HTTP response&lt;/a&gt;. Next, we implement the logic to create a new user in our Fauna database with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create fauna user error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Oops! Something went wrong from our end.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
   &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
 &lt;span class="p"&gt;){&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;register&lt;/code&gt; function executes successfully, &lt;code&gt;dbResponse&lt;/code&gt; data is sent to the invoker and the function exited immediately. However, if an error occurs during this process, the invoker is notified and the error logged to &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html"&gt;AWS Cloudwatch Logs&lt;/a&gt;. The complete code for signup is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/fauna&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callbackWaitsForEmptyEventLoop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization header not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create fauna user error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Oops! Something went wrong from our end.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup trycatch error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we implement the &lt;em&gt;login&lt;/em&gt; lambda function.&lt;/p&gt;

&lt;h4 id="login-lambda"&gt;Login Lambda&lt;/h4&gt;

&lt;p&gt;Open the &lt;code&gt;login.js&lt;/code&gt; file inside the &lt;code&gt;functions&lt;/code&gt; folder. Next, we import the &lt;code&gt;login&lt;/code&gt; function from the &lt;code&gt;fauna&lt;/code&gt; folder, &lt;code&gt;decodeJWT&lt;/code&gt; from the &lt;code&gt;firebase&lt;/code&gt; folder, and &lt;code&gt;response&lt;/code&gt; function from &lt;code&gt;utils.js&lt;/code&gt; all located in the &lt;code&gt;libs&lt;/code&gt; folder. Set the &lt;code&gt;context.callbackWaitsForEmptyEventLoop&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; inside &lt;em&gt;handler&lt;/em&gt; like we did earlier in &lt;code&gt;signup.js&lt;/code&gt;.The code should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/fauna&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callbackWaitsForEmptyEventLoop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the following code after &lt;code&gt;context.callbackWaitsForEmptyEventLoop&lt;/code&gt; inside the &lt;code&gt;handler&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization header not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login trycatch error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise in the &lt;code&gt;signup&lt;/code&gt; lambda, we decode the JWT located in the &lt;code&gt;event.headers.authorization&lt;/code&gt; and store it in &lt;code&gt;user&lt;/code&gt; if the authorization header exists, else we exit the function with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401"&gt;401 HTTP response&lt;/a&gt;. Next, we implement the login logic to create a user access token from the verified and  decoded email with the code below inside the &lt;code&gt;trycatch&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create fauna token error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Oops! Something went wrong from our end.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
   &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On successful execution of the &lt;code&gt;login&lt;/code&gt; function, &lt;code&gt;dbResponse&lt;/code&gt; data is sent to the invoker and the function exited immediately. However, if an error occurs during this process, the invoker is notified and the error logged to &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html"&gt;AWS Cloudwatch Logs&lt;/a&gt;. The complete code for &lt;code&gt;login&lt;/code&gt; is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/fauna&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callbackWaitsForEmptyEventLoop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;decodeJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization header not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create fauna token error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Oops! Something went wrong from our end.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;dbResponse&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login trycatch error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have completed the &lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;login&lt;/code&gt; lambda functions. Next, we look at the process to deploy the code to the AWS Cloud Service.&lt;/p&gt;

&lt;h3 id="deploy-api-to-aws"&gt;Deploy API to AWS&lt;/h3&gt;

&lt;p&gt;We are in the last mile of building our application and deploying it to the AWS cloud. Serverless Framework packages all node libraries and their dependencies which can result in very bloated lambda functions. Lambda functions in general are plagued by &lt;a href="https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/"&gt;cold-starts&lt;/a&gt; which is the time taken to prepare the execution environment. This time scales with the code size. Therefore, we can reduce the cold-start time with &lt;a href="https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/webpack.html"&gt;webpack bundling&lt;/a&gt;, importing only the code needed for each function. The overarching benefit is that end-users get a better user experience with reduced latencies. The &lt;a href="https://www.serverless.com/plugins/serverless-webpack"&gt;serverless-webpack&lt;/a&gt; plugin provides an option to achieve this optimization. &lt;/p&gt;

&lt;h4 id="webpack-bundle-setup"&gt;Webpack Bundle Setup&lt;/h4&gt;

&lt;p&gt;Install the &lt;code&gt;serverless-webpack&lt;/code&gt; plugin, and &lt;code&gt;webpack&lt;/code&gt;, as &lt;code&gt;[devDependencies](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file)&lt;/code&gt; with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to the &lt;code&gt;serverless.yml&lt;/code&gt; file at the root of the &lt;code&gt;faunabase-auth&lt;/code&gt; folder and add the &lt;code&gt;serverless-webpack&lt;/code&gt; plugin under &lt;code&gt;plugins&lt;/code&gt; after &lt;code&gt;serverless-ssm-fetch&lt;/code&gt;.&lt;br&gt;
Next, we add the webpack configuration file to &lt;code&gt;serverless.yml&lt;/code&gt;. Under the &lt;code&gt;custom:&lt;/code&gt; property add the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;serverless.yml&lt;/code&gt; file should be similar to the code below:&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tNnkhnou--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btpo4hzesn99o7cmffjv.jpg" alt="faunabase-auth folder 10" width="720" height="540"&gt;
&lt;/center&gt;
&lt;br&gt;
We exclude any GraphQL files (.gql) from the bundle with the &lt;code&gt;excludeFiles&lt;/code&gt; property under &lt;code&gt;webpack:&lt;/code&gt; in our &lt;code&gt;src&lt;/code&gt; folder.&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;excludeFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="cm"&gt;/**/&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After the &lt;code&gt;plugins&lt;/code&gt; property at the root of the &lt;code&gt;serverless.yml&lt;/code&gt; file add the code below to tell webpack to package our functions individually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;individually&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final &lt;code&gt;serverless.yml&lt;/code&gt; file should have the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;
&lt;span class="nx"&gt;frameworkVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;=2.66.1 &amp;lt;4.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nodejs14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
  &lt;span class="nx"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;b2c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;
  &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;authorizers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nx"&gt;identitySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Authorization&lt;/span&gt;
        &lt;span class="nx"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//securetoken.google.com/faunabase-687bd&lt;/span&gt;
        &lt;span class="nx"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faunabase&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;687&lt;/span&gt;&lt;span class="nx"&gt;bd&lt;/span&gt;
  &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;logRetentionDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;
  &lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nl"&gt;lambdaHashingVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20201221&lt;/span&gt;

&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;

&lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;individually&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;serverlessSsmFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;FAUNA_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;excludeFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="cm"&gt;/**/&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gql&lt;/span&gt;

&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1048&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;signup&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;
  &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;fauna&lt;/span&gt;
    &lt;span class="nx"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;httpApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;          
          &lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firebaseB2CAuthorizer&lt;/span&gt;




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;webpack.config.js&lt;/code&gt; file in the root directory and import the &lt;code&gt;serverless-webpack&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serverless-webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file we created will house all our code bundling configurations for deployment. Export an empty object with &lt;code&gt;module.exports&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serverless-webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the empty object, we set the &lt;code&gt;target&lt;/code&gt; property to &lt;code&gt;node&lt;/code&gt;. At build time, the plugin can automatically determine the correct entry point for each lambda function. We set the &lt;code&gt;entry&lt;/code&gt; to use &lt;code&gt;slsw.lib.entries&lt;/code&gt; inside the exported object. Finally, we set &lt;code&gt;mode&lt;/code&gt; to run in development mode if we are running locally else production mode with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLocal&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final webpack configuration file should be similar to this result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serverless-webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodeExternals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack-node-externals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;slsw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLocal&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4 id="serverless-deploy-to-aws-cloud"&gt;Serverless Deploy to AWS Cloud&lt;/h4&gt;

&lt;p&gt;We run the &lt;code&gt;serverless deploy&lt;/code&gt; command to deploy all artifacts to the AWS cloud with the default stage and region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;serverless&lt;/span&gt; &lt;span class="nx"&gt;deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;We have been able to build a serverless authentication API for a business-to-consumer application. The frontend or client application can access the signup endpoint to create a new user and the login endpoint to exchange a Firebase JWT for an access token to the Fauna database to facilitate direct communication. It is important to secure the access token with a &lt;a href="https://docs.fauna.com/fauna/current/security/roles.html?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;user-defined role (UDR)&lt;/a&gt;. We will do that in a future article in this series. In the next article, we explore building a serverless authentication API for a multi-tenant business-to-business application.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
      <category>firebase</category>
      <category>fauna</category>
    </item>
    <item>
      <title>Authentication with Firebase and Fauna: Introduction</title>
      <dc:creator>Samuel Danquah</dc:creator>
      <pubDate>Fri, 03 Sep 2021 20:36:43 +0000</pubDate>
      <link>https://dev.to/samiell/authentication-with-firebase-and-fauna-introduction-1nnh</link>
      <guid>https://dev.to/samiell/authentication-with-firebase-and-fauna-introduction-1nnh</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Getting authentication (authN) and authorization (authZ) right is vital to modern applications identifying legitimate users while controlling access to system resources. This process can be daunting for the novice as one wrong turn breaks the mantra - "a chain is only as strong as its weakest link." To the uninitiated, authN answers the question: is the user who they claim to be? Authentication precedes authorization but not the converse. AuthZ is primarily concerned with the inquest - are you permitted to access this resource? The journey from authN to authZ starts with the identity of the end-user.&lt;/p&gt;

&lt;p&gt;An end-user of any application represents the basic unit or entity. However, it is not practical to fully describe the entity with regards to its attributes and actions. Therefore, an identity depicts a snapshot of properties that characterize an entity in the application's context. Furthermore, an entity can have multiple identities. Consider this instance - a citizen belongs to a country. A citizen can have multiple identification cards (contexts) such as a passport and driver's license. The internet has made it possible to have numerous identities scattered across social media and other online platforms. The average user today has one hundred online accounts, according to research from NordPass. This number could increase under the current global pandemic conditions as people subscribe to more online services. A close inspection of these scattered identities shows a duplication of similar attributes. What attribute is common to all identification cards for any citizen of a country? At least the full name is the common denominator. Asking new users for common attributes such as names creates fatigue on sign-up over time. The user experience is analogous to this scenario: someone walks up to you and asks, "have you eaten?" to which you reply, "Yes." A few minutes later, another person comes to ask the same question and you politely answer. If more people keep asking the same question, the situation escalates from politeness to "I dare you to ask that question again." This common situation can benefit from a federated identity. After a successful identity proof and verification, end users get an access token which they can use to access permitted resources. Next, we look at the authentication and authorization components of the software application. We begin with Firebase for authentication with its perks and benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/auth"&gt;Firebase authentication&lt;/a&gt; provides an identity management solution as a service offered by Google. It is not a silver bullet to authentication and leverages industry standards such as &lt;a href="https://oauth.net/2"&gt;OAuth 2.0&lt;/a&gt; and &lt;a href="https://openid.net/connect"&gt;OpenID Connect&lt;/a&gt;. Hence, we investigate its benefits and offerings to meet the authentication requirements for your next software application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compliance, Privacy, and Security
&lt;/h3&gt;

&lt;p&gt;Firebase authentication enables modern applications to surf the waves of privacy regulations. It meets the standards of the EU General Data Protection Regulation (GDPR). This regulation still applies to software applications having users located in the European Union (EU) despite operating primarily from a non-EU territory. Firebase acts as the data processor in GDPR land, with the business entity behind the software application starring as the data controller. Depending on the end-user roadmap and acquisition plan, the business entity will require a Data Protection Officer (DPO) stipulated in &lt;a href="https://gdpr-info.eu/art-37-gdpr/"&gt;Article 37&lt;/a&gt; of the GDPR. If a DPO is required, Firebase gives you an option to list the DPO on its cloud console. In addition, the California Consumer Privacy Act (CCPA) provides privacy regulations to protect only California residents. Firebase meets the requirements of the CCPA in the capacity of the service provider. Furthermore, Firebase authentication is certified under major privacy and security standards such as &lt;a href="https://firebase.google.com/downloads/gdpr/NOVext2020_Firebase_ISO_27001.pdf"&gt;ISO 27001&lt;/a&gt;, &lt;a href="https://cloud.google.com/security/compliance/iso-27017"&gt;ISO 27017&lt;/a&gt;, &lt;a href="https://cloud.google.com/security/compliance/iso-27018"&gt;ISO 27018&lt;/a&gt;, &lt;a href="https://www.aicpa.org/InterestAreas/FRC/AssuranceAdvisoryServices/Pages/AICPASOC1Report.aspx"&gt;SOC 1&lt;/a&gt;, &lt;a href="https://www.aicpa.org/InterestAreas/FRC/AssuranceAdvisoryServices/Pages/AICPASOC2Report.aspx"&gt;SOC 2&lt;/a&gt;, and &lt;a href="https://firebase.google.com/downloads/gdpr/DEC2020_Firebase_SOC3_Report.pdf"&gt;SOC 3&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication Options
&lt;/h3&gt;

&lt;p&gt;Firebase authentication provides several alternatives to authenticate end-users: email, password, phone number, federated identity, anonymous, and custom authentication systems. &lt;/p&gt;

&lt;p&gt;Password authentication is the most common authentication method seen in software applications. The Open Web Application Security Project (OWASP) recommends that modern software applications transition to a post-password future, given the public release of over &lt;a href="https://haveibeenpwned.com/PwnedWebsites"&gt;5 billion password breaches&lt;/a&gt; down the years in the current version of the &lt;a href="https://github.com/OWASP/ASVS/raw/v4.0.2/4.0/OWASP%20Application%20Security%20Verification%20Standard%204.0.2-en.pdf"&gt;Application Security and Verification Standard&lt;/a&gt; (ASVS). The fundamental issue does not stem from the security robustness of password authentication systems but rather a behavioral defect from end-users often overlooked: the average user has numerous online accounts and is likely to reuse passwords across different accounts.; users generally are susceptible to generate weak passwords as it has to be memorable and easily remembered.&lt;/p&gt;

&lt;p&gt;Email or passwordless authentication provides a much secure alternative to password authentication. It achieves this through the elimination of user behavior that can compromise the safety of authentication systems. One famous implementation is Slack's magic link login. However, critical observation on authenticating through email links raises concern as email channels also have vulnerabilities, such as&lt;a href="https://en.wikipedia.org/wiki/Phishing"&gt; phishing attacks&lt;/a&gt; and &lt;a href="https://www.fbi.gov/scams-and-safety/common-scams-and-crimes/business-email-compromise"&gt;business email compromise&lt;/a&gt; (BEC) scams. It is important to note that compromised email accounts can also reset passwords in password authentication. There are no additional risks when comparing passwordless to password authentication. Having a second authentication factor, such as authenticator apps on the email channel, mitigates this risk directly.&lt;/p&gt;

&lt;p&gt;Phone number or Short Message Service(SMS) authentication is considered a weak authenticator in the OWASP Application Security Verification Standard. SMS messages can be intercepted and &lt;a href="https://www.wired.com/2016/06/hey-stop-using-texts-two-factor-authentication/"&gt;compromised&lt;/a&gt;. The current version of the ASVS echoes this woe and recommends that modern applications begin a migration journey devoid of SMS as a future version of the current standard will phase it out. Hence, SMS is a weak link in the security chain whether used as the first or second authentication factor. &lt;/p&gt;

&lt;p&gt;Firebase provides several federated identity providers out of the box, creating a frictionless authentication option. These include Facebook, Google, Twitter, Github, Apple, Twitter, and Microsoft. Do software applications using Firebase federated identity authentication have to write code for multi-factor authentication? Examining the problem closely with a focus on user experience brings to light the following observations: federated identity providers have native multi-factor authentication. Rolling out multi-factor authentication code in your software application becomes redundant when already enabled on the federated identity provider. Hence, end-users should be encouraged to use multi-factor authentication ingrained in the federated identity provider. However, end-users must be encouraged to avoid SMS as a second authentication factor to keep the security integrity of the authentication system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branding, and UI Customization
&lt;/h3&gt;

&lt;p&gt;Firebase authentication allows for the complete customization of the entire authentication experience for beginners with speed and ease. Nobody sets out to build a bespoke authentication solution unless your business is identity like Auth0. Firebase authentication provides an open-source user interface (UI) &lt;a href="https://github.com/firebase/firebaseui-web"&gt;library &lt;/a&gt;which streamlines building diverse authentication flows for a great user experience. These flows come with the wisdom of years of UX research optimizing the authentication on Google, Youtube, and Android to get you started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Availability and Scalability
&lt;/h3&gt;

&lt;p&gt;Firebase authentication does not offer service level agreements (SLA). It comes at no cost and does not limit the number of monthly active users (MAU). It charges using SMS verifications which impacts authentication security negatively. If SLA guarantees are a requirement for your business, consider using the &lt;a href="https://cloud.google.com/identity-platform"&gt;Google Cloud Identity Platform&lt;/a&gt; or any other identity provider (IDP) that meets your SLA needs. Firebase authentication shines for new ventures and small and medium enterprises who need to cut costs at the onset in uncertain business climates. Despite not having an SLA, you can investigate the availability guarantees from the Firebase authentication status &lt;a href="https://status.firebase.google.com/products/ty5dcfcAmf92kaN1vKuj/history"&gt;history&lt;/a&gt;. For instance, the downtime in 2019 was 1 hour, 48 minutes which is about 3 minutes shy of a &lt;a href="https://uptime.is/"&gt;99.98% uptime SLA&lt;/a&gt;. The previous year's SLA was much worse, with an approximate 99.4% SLA. As a free service, Firebase operates within certain tenable&lt;a href="https://firebase.google.com/docs/auth/limits"&gt; limits&lt;/a&gt;. The authentication API can handle a maximum of 10 million request operations per day.&lt;/p&gt;

&lt;h3&gt;
  
  
  SDK Integrations
&lt;/h3&gt;

&lt;p&gt;Firebase authentication has support for web, ios, android, c++, and unity platforms on clients. Software development kit (SDK) libraries are available for the supported platforms providing coverage for use cases across mobile, web, internet of things (IOT), and games coupled with &lt;a href="https://firebase.google.com/docs/auth"&gt;documentation&lt;/a&gt;. The &lt;a href="https://firebase.google.com/docs/web/modular-upgrade"&gt;beta release&lt;/a&gt; of the client SDKs provides up to an 80% reduction in file size for the web platform. However, the beta release library will not work with the Google Cloud Identity Platform and the open-source Firebase UI library. &lt;/p&gt;

&lt;p&gt;It also offers admin libraries for secure server environments with &lt;a href="https://nodejs.org/"&gt;Node&lt;/a&gt;, &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;, &lt;a href="https://www.java.com/"&gt;Java&lt;/a&gt;, and &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt; making it a breeze to work with for serverless architectures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Considerations
&lt;/h3&gt;

&lt;p&gt;Firebase provides other additional services such as &lt;a href="https://firebase.google.com/docs/analytics"&gt;Google Analytics&lt;/a&gt;, &lt;a href="https://firebase.google.com/docs/storage"&gt;Cloud Storage&lt;/a&gt;, and &lt;a href="https://firebase.google.com/docs/cloud-messaging/"&gt;Cloud Messaging&lt;/a&gt; that pair well with Firebase authentication. Analytics helps you target a segment of end-users who can receive messages inside the software application at no cost.&lt;/p&gt;

&lt;p&gt;Cloud storage provides a scalable file storage option that uses security rules to provide authorization logic for file access. It integrates seamlessly with Firebase authentication using identity attributes to give permissions to file resources.&lt;/p&gt;

&lt;p&gt;For advanced authentication needs such as support for &lt;a href="https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language"&gt;SAML &lt;/a&gt;and &lt;a href="https://openid.net/connect/"&gt;OIDC&lt;/a&gt;, Firebase authentication leverages identity-aware proxies (IAP) to external providers, not covered under the initial identity provider offerings. Furthermore, the native Microsoft federated identity can connect to Azure Active Directory (AD). This access provides support for Web Services (WS) Federation protocol giving access to the Microsoft ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization
&lt;/h2&gt;

&lt;p&gt;The client-serverless model is enabling modern applications to encapsulate business and application logic close to the underlying resources. &lt;a href="https://fauna.com/?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;Fauna&lt;/a&gt; facilitates authorization logic encapsulated in &lt;a href="https://docs.fauna.com/fauna/current/security/roles?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;User Defined Roles&lt;/a&gt; (UDR) and &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/functions?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_FirebaseFaunaAuth_SDanquah"&gt;User Defined Functions&lt;/a&gt; (UDF) using Attribute-Based Access Control (ABAC) for authZ at the database layer. This approach means public clients can talk directly to the database in a secure fashion. Other critical non-database business services run in serverless functions behind a serverless API gateway with inherent authorization logic.&lt;/p&gt;

&lt;p&gt;When public clients use bearer tokens, software applications must mitigate &lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;cross-site scripting&lt;/a&gt; (XSS) attacks. A compromised application can steal bearer tokens - whether in memory or service workers.&lt;/p&gt;

&lt;p&gt;Next, we look at architectural components for our serverless identity solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern Application Architecture
&lt;/h2&gt;

&lt;p&gt;Software application architecture has evolved through generations, giving birth to the modern client serverless architecture. Despite the leaps from generation to generation, the underlying component layers remain almost the same. These components are the presentation, application, business, and data layers. &lt;/p&gt;

&lt;p&gt;The presentation layer, also referred to as the view or user interface layer is where users access software applications directly through public clients such as browsers and mobile applications. Public clients, by nature, are not capable of keeping passwords or secrets private.&lt;/p&gt;

&lt;p&gt;The application layer does overlap with the business layer. The requirements for building software applications determine the extent of the overlap. The application layer is a gateway to the business layer when encapsulating an API interface to business functionality from the presentation layer. The business layer houses the business rules that govern the set of problems that the application should address. The data layer handles the persistence and storage of data in databases and file storage systems. One factor guiding communication between these layers concerning authorization is that logic for granting permissions should reside close to the resource it controls. The penalty for going contrary is two folds: it increases latency and can lead to a bad user experience; it creates a bottleneck when the server housing the authorization logic is offline while the resource server is online and creates an inconvenience. If the authorization logic and permitted resource reside on the same server, an outage reflects that both are unavailable and notify the end-user of the situation. &lt;/p&gt;

&lt;p&gt;In the next installment of this series, we assemble the building blocks to implement a business-to-consumer (B2C) client-serverless architecture and build a serverless authentication solution with &lt;a href="https://aws.amazon.com"&gt;Amazon Web Services&lt;/a&gt; (AWS).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>fauna</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Collaborative Editing with Gridsome and FaunaDB: Introduction</title>
      <dc:creator>Samuel Danquah</dc:creator>
      <pubDate>Fri, 26 Jun 2020 01:33:58 +0000</pubDate>
      <link>https://dev.to/samiell/collaborative-editing-with-gridsome-and-faunadb-introduction-351m</link>
      <guid>https://dev.to/samiell/collaborative-editing-with-gridsome-and-faunadb-introduction-351m</guid>
      <description>&lt;h1&gt;
  
  
  Document Conventions
&lt;/h1&gt;

&lt;p&gt;The following conventions are employed throughout this article.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;JAMstack
   &lt;/td&gt;
   &lt;td&gt;Javascript, Api’s, and Markup technology stack
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;CMS
   &lt;/td&gt;
   &lt;td&gt;Content Management System
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;OT
   &lt;/td&gt;
   &lt;td&gt;Operational Transformation
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;CRDT
   &lt;/td&gt;
   &lt;td&gt;Conflict-Free Replicated Data Types
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;CmRDT
   &lt;/td&gt;
   &lt;td&gt;Commutative Replicated Data Types
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;P2P
   &lt;/td&gt;
   &lt;td&gt;Peer-to-Peer
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;ABAC
   &lt;/td&gt;
   &lt;td&gt;Attribute-Based Access Control
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;The dawn of Web 2.0 sparked an explosive interest in browser-based document editing tools. Today, creating contents necessitate collaboration to arrive at the final masterpiece - a gestalt in addition to the many revisions throughout a content’s life cycle. Google Docs, a free collaborative editing tool, gained a meteoric rise in the collaborative editing space on the bedrock of real-time collaboration. In this modern era of the JAMstack, headless content management systems provide a cheap, scalable, and developer-friendly approach to content creation and management in contrast to classical monoliths. However, the concept of collaborative editing is not native to the JAMstack ecosystem of headless CMS nor its monolithic predecessors. Therefore, we observe mundane use cases where content collaboration happens on Google Docs (and other alternatives) before content transfer to a CMS of choice. This approach throttles down on the overall productivity engine of the user experience. How can dynamic JAMstack applications trapped in this scenario surmount this bottleneck? To answer this question, we explore the real-time collaborative editing landscape available to the JAMstack ecosystem as a starting reference, the challenges, and finally build a collaborative text editor using WebRTC with data persistence to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But first, we will dive under the hood of a real-time collaborative editor to comprehend the moving parts and their integration.&lt;/p&gt;

&lt;h1&gt;
  
  
  Mechanics of a real-time collaborative editor
&lt;/h1&gt;

&lt;p&gt;Version control system like Git is the polar opposite of a real-time collaborative editor. A real-time collaborative editor has a singular goal of providing consensus across multiple users editing the same content in near real-time at its foundation. Furthermore, a practical distributed team of content editors may place additional constraints on the ability to collaborate on slow and flaky internet connections with seamless content resynchronization capabilities as a minimum requirement. Fundamentally, a real-time collaborative editor mandates a delicate balancing act across the trident of clients (content editors), concurrency (simultaneous editing), and communication (centralized or decentralized).&lt;/p&gt;

&lt;h2&gt;
  
  
  Clients
&lt;/h2&gt;

&lt;p&gt;Clients represent end-users who need to collaborate on content with write permissions in real-time. The number may vary depending on the size of the collaboration team wanting to edit the content at any instant of time (Figure 1). Increasing clients spike the complexity of scalability for any given collaborative editing solution. For example, Google Docs allows a maximum of 100 users to both comment and edit a document at the same time. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy55jqmpbc2dikkzbin70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy55jqmpbc2dikkzbin70.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency
&lt;/h2&gt;

&lt;p&gt;Concurrency relates to conflicts that are bound to arise from clients simultaneously editing the same content and a primary challenge for real-time collaborative editing. Addressing this issue requires a blend of concurrency control and consistency maintenance across all simultaneous users accessing the content. Whether creating, updating, or deleting content from concurrent actions from users, collaborative editing operations can be classified as insert and delete actions at a basic level. A real-time collaborative editor then has to satisfy two challenging axioms: commutativity and idempotency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Commutativity&lt;/strong&gt;: Concurrent insert and delete operations converge to the same result despite the order of application.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Idempotency&lt;/strong&gt;: Repeated delete operations must produce the same result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real-time collaborative editing solutions diverge into two broad strategies to tackle these challenges. The first and popular approach is Operational Transformation (OT). This strategy gave birth to the first generation of real-time collaborative editors - Etherpad, Firepad, Google Wave, among others. However, OT is more complicated to implement in a decentralized communication model than a centralized one. The plethora of OT algorithms with several trade-offs published in academic papers to address a fully distributed approach highlights the complexities involved. Current real-time collaborative editors still use OT and mostly adopt a centralized communication model. The alternative approach to OT is Conflict-Free Replicated Data Types (CRDT). Researchers discovered this alternate strategy in a quest to improve the robustness and simplicity of OT. CRDT has created a new generation of real-time collaborative editors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication
&lt;/h2&gt;

&lt;p&gt;Communication protocols provide an avenue to relay content changes from the content creator to other concurrent content editors. The major obstacle with real-time communication is the physical distance between users for which there are no plausible solutions. The two prevalent models are centralized and decentralized architectures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Centralized Architecture
&lt;/h3&gt;

&lt;p&gt;This architecture (Figure 2) relies on a client-server model. It requires a central server to mediate content changes between multiple concurrent clients and achieve convergence across users. The central server handles concurrency control and consistency maintenance. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffzkfp4ptyy1jzhvy1uc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffzkfp4ptyy1jzhvy1uc2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paradoxically, it serves as both a single source of truth for conflict resolutions as well as a single source of failure. If the server fails, content editors can no longer collaborate. Another limitation is the introduction of undesirable high latencies between nearby clients who are both far away from the central server (Figure 3). The central server has to receive content updates from one user(Anna) for conflict resolution before sending it to another user(Evan) even though they are near (&lt;strong&gt;10ms&lt;/strong&gt;) to each other. Furthermore, a centralized server presents scaling challenges for increasing large distributed clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fip7to769rhbwe71z0f0v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fip7to769rhbwe71z0f0v.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Decentralized Architecture
&lt;/h3&gt;

&lt;p&gt;This architecture is a peer-to-peer system. Each end-user(peer) acts as both client and server with direct communication between users and no need for a central server. However, a signaling server is required to broker the initial peer-to-peer connections and serves no further purpose. After, clients exchange content updates directly. WebRTC is the protocol that enables real-time communication among concurrent users and is present in modern browsers. This decentralized approach is in stark contrast to the centralized architecture (Figure 4) and does not suffer from the issues of a centralized server. However, there is a limit on the maximum number of peer-to-peer connections possible, and the client browser decides this limit. For instance, the Chromium browser sets a limit of 500 peer-to-peer connections from its &lt;a href="https://chromium.googlesource.com/chromium/src/third_party/+/master/blink/renderer/modules/peerconnection/rtc_peer_connection.cc#144" rel="noopener noreferrer"&gt;source code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftxccof4wtkyb0c4mehop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftxccof4wtkyb0c4mehop.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Comparison of real-time collaboration solutions
&lt;/h1&gt;

&lt;p&gt;This is not an exhaustive list and is meant to be a referencing guide.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;Yjs&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;CKEditor&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;Collab&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;ShareDB&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Communication
   &lt;/td&gt;
   &lt;td&gt;P2P
   &lt;/td&gt;
   &lt;td&gt;Centralized
   &lt;/td&gt;
   &lt;td&gt;Centralized
   &lt;/td&gt;
   &lt;td&gt;Centralized
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;License
   &lt;/td&gt;
   &lt;td&gt;MIT
   &lt;/td&gt;
   &lt;td&gt;Proprietary
   &lt;/td&gt;
   &lt;td&gt;MIT
   &lt;/td&gt;
   &lt;td&gt;MIT
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Network-agnostic
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Offline editing
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;`No
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Algorithm
   &lt;/td&gt;
   &lt;td&gt;CRDT
   &lt;/td&gt;
   &lt;td&gt;OT
   &lt;/td&gt;
   &lt;td&gt;Reconciliation
   &lt;/td&gt;
   &lt;td&gt;OT
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td rowspan="5"&gt;Supported Editors
   &lt;/td&gt;
   &lt;td&gt;ProseMirror
   &lt;/td&gt;
   &lt;td rowspan="5"&gt;CKEditor
   &lt;/td&gt;
   &lt;td rowspan="5"&gt;ProseMirror
   &lt;/td&gt;
   &lt;td&gt;CodeMirror
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Quill
   &lt;/td&gt;
   &lt;td&gt;Quill
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Monaco
   &lt;/td&gt;
   &lt;td rowspan="3"&gt;Ace
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ace
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;CodeMirror
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Support Content Types other than Text
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Scalable
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Server Data 
&lt;p&gt;
Loss Sync
   &lt;/p&gt;
&lt;/td&gt;
   &lt;td&gt;Yes
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
   &lt;td&gt;No
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Real-time collaborative text editor: Case Study
&lt;/h1&gt;

&lt;p&gt;Let us look at a hypothetical company X with a real-world collaboration problem while building a real-time collaborative text editor solution in the process:&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Company X has a small distributed team of content editors, spread across the US and Europe who have to collaborate on contents simultaneously. For every published content, 30 content editors (maximum) have to work simultaneously. Company X seeks a seamless solution to the problem, and like most companies is on a tight budget. How can company X address this issue? We are going to explore an example application to cover this use case. &lt;/p&gt;

&lt;p&gt;The maximum number of clients working at any instant on a single content is 30. Next, we have to look at concurrency control and what communication protocols present a united front on efficiency and cost. Do we employ an OT or CRDT approach for concurrency and conflict resolution? An OT strategy simplifies concurrency to one conflict at a time, which a centralized server can resolve before propagating content changes to all clients. However, this solution is also a single point of failure and has little fault tolerance. If the central server goes offline at any point, the team cannot collaborate, which is undesirable. OT also creates inefficiencies as content editors living in nearby neighborhoods always have to send content updates first to a distant central server for conflict resolution before neighboring content editors can receive the content changes. Lastly, running on a central server for conflict resolution implies OT brings extra costs for a tight budget. We already have non-negotiable bills for persisting content updates to a database. It is important to note that in other scenarios where a large distributed team needs to collaborate simultaneously, server costs for communication is inevitable. Can a CRDT alternative solve these challenges for company X with a tight budget? Contrary to OT, CRDT provides a decentralized autonomous conflict resolution strategy without a central server for concurrency control irrespective of the number of connected clients (content editors). This approach eliminates a single point of failure observed in the OT strategy and is more fault-tolerant. &lt;/p&gt;

&lt;p&gt;Furthermore, CRDT guarantees automatic content synchronization across all connected clients. For clients with flaky internet connections, content updates are seamless upon reconnection and resolve to a unified state for the team of content editors. In the earlier situation of nearby content editors with the distant central server, CRDT has an option for direct peer-to-peer communication through protocols such as WebRTC present in modern browsers. This option for P2P can save costs for running active communication servers. However, CRDT needs a signaling server to establish the initial direct connection among peers or clients. This signaling server is analogous to a matchmaker for clients and takes no further part in the actual communication of content updates among connected clients. There are free to use public signaling servers as well as self-hosted private options. In summary, our solution will employ a CRDT strategy, and hence we use Yjs, a network agnostic shared editing framework.&lt;/p&gt;

&lt;p&gt;Lastly, we need to persist the content to a database. Since we are working with a distributed team of content editors, &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; presents a viable solution (Figure 5) for data persistence with its global serverless database, which provides low latency data access and distributed ACID transactions without sacrificing data consistency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1bdjjtq3ck3v5q94m7jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1bdjjtq3ck3v5q94m7jk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yjs has integrations for several text editors, as seen in the comparison chart for real-time collaborative editing solutions. For our scenario, we use ProseMirror, an open-source toolkit for building renderless and extensible rich text editors. Yjs also provides a communication connector for webRTC, among others. We will use this connector (y-webrtc) in our application, which satisfies our current threshold of 30 maximum collaborators per document or content for company X. Now, we have the ingredients to make a real-time collaborative text editor with data persistence. But, how secure is this system end to end?&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Figure 6 shows the communication model for our decentralized real-time collaborative editing solution. Communication channels exist across client to client (P2P), client to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;, and the client to signaling server bridges. From figure 6, Evan creates the document for collaboration with a unique room attribute as well as a shared secret attribute and persists to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; (GraphQL or FQL).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fky2gvd8p8y07k09b5944.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fky2gvd8p8y07k09b5944.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;’s attribute-based security model (&lt;a href="https://docs.fauna.com/fauna/current/security/abac.html" rel="noopener noreferrer"&gt;ABAC&lt;/a&gt;), we can implement fine-grained access control for collaborators (content editors). Hence, Evan grants Anna collaboration rights to his document. Evan retrieves the document from &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; in our real-time collaborative text editor. This communication channel between Evan (client) and &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; (database) is secure over HTTPS, among other security mechanisms. Communication between client and database is an already solved problem, especially for JAMstack apps. &lt;/p&gt;

&lt;p&gt;Therefore, we focus on the other two communication channels, according to Figure 6. Next, both Evan and Anna access our real-time collaborative editing (we-collab) app. They both communicate with the signaling server, which is responsible for handling unique and relevant signaling information such as the IP address and facilitates the necessary exchange of this information to connected clients. Signaling servers follow a publish-subscribe pattern. Yjs provides an option for a shared secret as well as a room for collaboration. &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; secures this information with its strong ABAC model. Yjs uses the shared secret to encrypt the information exchange from the client to the signaling server. &lt;/p&gt;

&lt;p&gt;Hence, Evan and Anna can exchange signaling information securely, even on a public signaling server without any trust. Yjs also provides an option to utilize multiple signaling servers in our we-collab app for scalability. &lt;/p&gt;

&lt;p&gt;Finally, the last communication channel occurs between clients, in our case Evan and Anna (Figure 6). Clients receive content updates through webRTC, directly from one client browser to the other. How secure is webRTC? This a legitimate concern, and the answer is VERY SECURE. WebRTC employs biometric encryption protocols with content sharing across encrypted data channels from peer to peer. Therefore content updates exchanged among peers (clients) are secure by default. The tight coupling of Yjs, webRTC, and &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; with its strong ABAC model provides an end to end security for this application.&lt;/p&gt;

&lt;h2&gt;
  
  
  We-Collab App
&lt;/h2&gt;

&lt;p&gt;We will now proceed to build the we-collab app using Gridsome(Vue) for Markup, WebRTC for peer-to-peer communication, and &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; for data persistence, through its GraphQL API. Let’s get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;Login to your &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; account and create a new database &lt;code&gt;collab&lt;/code&gt; from the cloud console. Next, create a  &lt;code&gt;schema.gql&lt;/code&gt;  with the following code and upload it to the cloud console.&lt;/p&gt;

&lt;pre&gt;
type Page {
   name: String! &lt;a class="mentioned-user" href="https://dev.to/unique"&gt;@unique&lt;/a&gt;
   room: String! &lt;a class="mentioned-user" href="https://dev.to/unique"&gt;@unique&lt;/a&gt;
   secret: String
   type: PageType!
   content: String!
 }

 enum PageType {
   PUBLIC
   PRIVATE
 }

 type Query {
   allPages: [Page!]
   findPageByName(name: String!): Page
 }
&lt;/pre&gt;

&lt;p&gt;Let’s create some data into the &lt;code&gt;collab&lt;/code&gt; database. Go to the GraphQL Playground in the cloud console and run the following code:&lt;/p&gt;

&lt;pre&gt;
mutation createPublicPage {
  createPage(
    data: {
      name: "Public Page"
      room: "live-wall"
      type: PUBLIC
      secret: "afreeworld"
      content: ""
    }
  ){
    _id
    name
    room
    type
    secret
    content
  }
}
&lt;/pre&gt;

&lt;p&gt;If successful, your screen should look similar to the picture below (Figure 7):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F81i7lguuwgig8fz5kywq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F81i7lguuwgig8fz5kywq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we create a Guest Role using ABAC from &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;. Still logged in, click on the &lt;strong&gt;SECURITY&lt;/strong&gt; tab to the left of your screen, highlighted below. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F66v615ziet2d9tkbojs0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F66v615ziet2d9tkbojs0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, click on &lt;strong&gt;MANAGE ROLES&lt;/strong&gt; as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fifgfi5g4foa0hd3ox0nc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fifgfi5g4foa0hd3ox0nc.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;NEW ROLE&lt;/strong&gt; to create our guest role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F782k6kdk23p1hjwslpwa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F782k6kdk23p1hjwslpwa.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the new screen, change &lt;strong&gt;MyCustomRole&lt;/strong&gt; text to Guest.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwwiseu3b1ahq2l8h99qm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwwiseu3b1ahq2l8h99qm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the &lt;strong&gt;Page&lt;/strong&gt; collection with &lt;strong&gt;Read&lt;/strong&gt; and &lt;strong&gt;Write&lt;/strong&gt; privileges under &lt;em&gt;Collections&lt;/em&gt;. Next, add the &lt;strong&gt;findPageByName&lt;/strong&gt; index with &lt;strong&gt;Read&lt;/strong&gt; permission under &lt;em&gt;Indexes&lt;/em&gt;. Lastly, add &lt;strong&gt;collections&lt;/strong&gt; with &lt;strong&gt;Read&lt;/strong&gt; and &lt;strong&gt;Write&lt;/strong&gt; privileges under &lt;em&gt;Schemas&lt;/em&gt;. Your screen should be similar to image below and click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsidqngd9jgqqcq23nfmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsidqngd9jgqqcq23nfmv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now create a new key for the Guest Role by clicking on &lt;strong&gt;NEW KEY&lt;/strong&gt; under the &lt;strong&gt;SECURITY&lt;/strong&gt; tab as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuujxvc8010dlm5vlmuku.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuujxvc8010dlm5vlmuku.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, choose the &lt;code&gt;collab (Current Database)&lt;/code&gt;  under Database, and select &lt;code&gt;Guest&lt;/code&gt; under Role. You can also provide an optional key name. Select &lt;strong&gt;Save&lt;/strong&gt; when you are done.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fru535x2pyo6sohq9crfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fru535x2pyo6sohq9crfu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see the screen below with the guest key to connect our frontend. Save this key as an &lt;a href="https://gridsome.org/docs/environment-variables/" rel="noopener noreferrer"&gt;environment variable&lt;/a&gt;. In our frontend, we use this key as the &lt;strong&gt;GRIDSOME_GUEST_KEY&lt;/strong&gt; variable. Now we are going to connect our frontend to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;’s GraphQL API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fad28cpskv08gyweyoieo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fad28cpskv08gyweyoieo.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;Our frontend uses vuetify for UI. Vuetify is a Vue UI library that comes out of the box with ready-to-use handcrafted material design components. We set up our application to consume the &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; GraphQL API. Since we are not utilizing GraphQL subscriptions, we use Axios, a lightweight HTTP client library. You can also use the native browser Fetch API or the graphql-request library, a minimal GraphQL client. We create a mutation with the following code, to update our database with the current document version in a &lt;strong&gt;&lt;em&gt;mutation.js&lt;/em&gt;&lt;/strong&gt; file.&lt;/p&gt;

&lt;pre&gt;
export const updatePageMutation = (payload) =&amp;gt; {
   return `
     mutation updatePublicPage{
       updatePage(
         id: "${payload._id}"
         data: {
           content:"${payload.content}"
           name: "${payload.name}"
           room: "${payload.room}"
           type: ${payload.type}
         }
       ) {
         _id
         name
         room
         secret
         type
         content
       }
     }
   `
}
&lt;/pre&gt;

&lt;p&gt;Next, we create a query in a &lt;strong&gt;_query.js _&lt;/strong&gt;file with the following code to fetch a document (content) from our database with the updated document version, whenever a new client (content editor) opens our app for the first time. &lt;/p&gt;

&lt;pre&gt;
export const getPageQuery = (payload) =&amp;gt; {
   return `
     query getPage {
       getPage: findPageByName(
         name: "${payload}"
       ) {
         _id
         name
         room
         secret
         type
         content
       }
     }
   `
}
&lt;/pre&gt;

&lt;p&gt;This content has two vital attributes (room and shared secret) for integration with &lt;a href="https://yjs.dev/" rel="noopener noreferrer"&gt;Yjs&lt;/a&gt;. Then we create an &lt;strong&gt;&lt;em&gt;index.js&lt;/em&gt;&lt;/strong&gt; that houses HTTP calls to our database for queries and mutations through the GraphQL API with the following code:&lt;/p&gt;

&lt;pre&gt;
import { guest, page, url } from "./config"
import { updatePageMutation } from "./mutation"
import { getPageQuery } from './query'
import axios from 'axios'

export const updatePage = (dataPayload) =&amp;gt; {
   return axios({
       method: 'post',
       url,
       headers: {
           "authorization": `Bearer ${guest}`
       },
       data: {
           query: updatePageMutation(dataPayload)
       }
   })
}

export const getPage = async () =&amp;gt; {
   return await axios({
       method: 'post',
       url,
       headers: {
           "authorization": `Bearer ${guest}`
       },
       data: {
           query: getPageQuery(page)
       }
   })
}
&lt;/pre&gt;

&lt;p&gt;We now look at integrating Yjs as a real-time extension to the ProseMirror text editor in our app. Since we are using Gridsome, we will use Tiptap, a Vue flavored ProseMirror text editor. We begin by adding Yjs and y-webrtc connector to the &lt;strong&gt;&lt;em&gt;realtime.js&lt;/em&gt;&lt;/strong&gt; file.&lt;/p&gt;

&lt;pre&gt;
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
&lt;/pre&gt;

&lt;p&gt;Next, we create an extension for our real-time collaboration functionality with the code below:&lt;/p&gt;

&lt;pre&gt;
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
import { Extension } from 'tiptap'


export default class Realtime extends Extension {
  get name() {
    return 'realtime'
  }
}
&lt;/pre&gt;

&lt;p&gt;Our real-time extension accepts a document from our &lt;strong&gt;&lt;em&gt;getPage query.&lt;/em&gt;&lt;/strong&gt; We create a new instance for both Yjs and the y-webrtc connector and pass the fetched document from the database in the &lt;strong&gt;&lt;em&gt;init&lt;/em&gt;&lt;/strong&gt; function. We are storing the content for collaboration as a string in &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; whereas Yjs uses the content as a Uint8Array. Therefore, we use the &lt;strong&gt;&lt;em&gt;fromBase64&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;toBase64&lt;/em&gt;&lt;/strong&gt; to handle the conversion between Uint8Array and a Base64 String. Every time the document is updated with the update hook of Yjs, all connected clients receive just the update and not the whole state of the document. At the same time, we persist the entire state of the document to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;. Any new client who joins to collaborate will first fetch the current copy from the database and receive direct incremental updates from connected clients as real-time updates arise. Y-webrtc also provides an option to set the maximum number of allowed connections per client browser. We set the maximum number of connected clients per client browser with &lt;strong&gt;&lt;em&gt;maxConns&lt;/em&gt;&lt;/strong&gt;. It is important that we add an element of randomness to connected clients to prevent them from forming clusters as shown in the code below:&lt;/p&gt;

&lt;pre&gt;
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
import { Extension } from 'tiptap'
import { fromBase64, toBase64 } from '@aws-sdk/util-base64-browser'

export default class Realtime extends Extension {
  get name() {
    return 'realtime'
  }
  get defaultOptions() {
    return {
        pageDoc: {},
        type: null,
        provider: null
    }
  }
  init() {
      const ydoc = new Y.Doc()

      if (
          typeof this.options.pageDoc.content === 'string' &amp;amp;&amp;amp;
          this.options.pageDoc.content.length !== 0
      ) {
          this.options.pageDoc.content = fromBase64(this.options.pageDoc.content)
          Y.applyUpdate(ydoc, this.options.pageDoc.content)
      }

      ydoc.on('update', update =&amp;gt; {
          this.options.pageDoc.content = toBase64(Y.encodeStateAsUpdate(ydoc))
          updatePage(this.options.pageDoc)
      })

      this.options.provider = new WebrtcProvider(
          this.options.pageDoc.room, ydoc, {
              password: this.options.pageDoc.secret,
              maxConns: 40 + Math.floor(Math.random() * 30),
          }
      )
      this.options.type = ydoc.getXmlFragment('prosemirror')
  }
}
&lt;/pre&gt;

&lt;p&gt;Yjs also provides shared editing cursors for connected clients just like in Google Docs to create a better user experience for all connected clients with undo and redo capabilities. You can customize what happens for undo and redo in the shared editing space. Finally, we add the shared editing cursors to our app with the code below:&lt;/p&gt;

&lt;pre&gt;
import { keymap } from 'prosemirror-keymap' 
import { Extension } from 'tiptap' 
import { redo, undo, yCursorPlugin, ySyncPlugin, yUndoPlugin } from 'y-prosemirror' 
import * as Y from 'yjs' 
import { WebrtcProvider } from 'y-webrtc' 
import { updatePage } from '@/services/fauna/index.js' 
import { fromBase64, toBase64 } from '@aws-sdk/util-base64-browser' 
 
export default class Realtime extends Extension { 
    get name() { 
        return 'realtime' 
    } 
 
    get defaultOptions() { 
        return { 
            pageDoc: {}, 
            type: null, 
            provider: null 
        } 
    } 
    init() { 
        const ydoc = new Y.Doc() 
 
        if ( 
            typeof this.options.pageDoc.content === 'string' &amp;amp;&amp;amp; 
            this.options.pageDoc.content.length !== 0 
        ) { 
            this.options.pageDoc.content =     fromBase64(this.options.pageDoc.content) 
            Y.applyUpdate(ydoc, this.options.pageDoc.content) 
        } 
 
        ydoc.on('update', update =&amp;gt; { 
            this.options.pageDoc.content = toBase64(Y.encodeStateAsUpdate(ydoc)) 
            updatePage(this.options.pageDoc) 
        }) 
 
        this.options.provider = new WebrtcProvider( 
            this.options.pageDoc.room, ydoc, { 
                password: this.options.pageDoc.secret, 
                maxConns: 40 + Math.floor(Math.random() * 30), 
            } 
        ) 
        this.options.type = ydoc.getXmlFragment('prosemirror') 
    } 
 
    get plugins() { 
        return [ 
            ySyncPlugin(this.options.type), 
            yCursorPlugin(this.options.provider.awareness), 
            yUndoPlugin(), 
            keymap({ 
                'Mod-z': undo, 
                'Mod-y': redo, 
                'Mod-Shift-z': redo 
            }) 
        ] 
    } 
&lt;/pre&gt;

&lt;p&gt;The final code is available at this &lt;a href="https://github.com/samiell/we-collab-app" rel="noopener noreferrer"&gt;repo&lt;/a&gt;. Finally, we have built a real-time collaborative text editor for company X. You can play around with the demo (Figure 8) at &lt;a href="https://we-collab.netlify.app" rel="noopener noreferrer"&gt;https://we-collab.netlify.app&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this article, we took a deep dive under the hood of a real-time collaborative text editor to understand its internal mechanisms and moving parts. We explored the real-time collaborative editing solution landscape to provide viable options to build a real-time collaborative text editor for various use cases. We saw firsthand how webRTC can provide both a low-cost and secure option for real-time collaboration for small distributed teams. &lt;/p&gt;

&lt;p&gt;Finally, the globally distributed architecture of &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt; with strong consistency and low latency provides a unique set of complements to any real-time collaborative editing solution. Let’s not forget &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_CollaborativeEditing_SamuelD" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;’s powerful ABAC feature provided out of the box to implement robust security and expand the creative freedom to architect security solutions for various use cases without any external setup.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
      <category>yjs</category>
      <category>fauna</category>
    </item>
  </channel>
</rss>
