Build a Serverless Login Service Using AWS DynamoDB and Lambda

Build a Serverless Login Service Using AWS DynamoDB and Lambda

Building a token-based login service in 10 minutes

It’s 2022, serverless is not a fancy term that gets thrown around by tech geeks anymore. More and more people are realizing that it’s the way ahead for building fast and highly scalable applications.

In this article, we are going to focus on building a serverless login system from scratch with AWS, using two of their popular services —

If you’re very experienced with these two already, which I am guessing you are not since you’ve read the title, the following tutorial may not be for you. Before we get started you will need an AWS account for this project, you can signup for one here. Let’s take a look at the system we are going to build.

As you can see, we will build a basic login service that uses an API gateway for accessing the lambda which will connect to a DynamoDB. We will have three endpoints in our projects, the first one is for registering a new user and storing the details in the database. The second one would be for user login, and issuing a JWT-Token for the user. And the third one is to verify that the token is valid. Let’s get started.

1. Creating a DynamoDB table

The first step that we’re going to take is to create a DynamoDB table where we will store our user data.

As mentioned earlier DynamoDB is a NoSQL database data is stored and queried mainly using a primary key, which is going to be the partition key and in some cases a combination of partition key and sort key.

DynamoDB uses the partition key’s value as an input to an internal hash function which decides the partition on which your data will be stored. You can read more about keys here, as it would be too much to cover in this article.

To create a table, go to your AWS console and search for DynamoDB in the top search box and select it from the search results. Once you are on the DynamoDB dashboard, click on the create table option (1.1)

1.1

This will take you to a page where you will prompt to give the table details like table name and primary keys (1.2).

We are going to name our table user-table for now and define username as our partition key which is what we will use to search for users. You can see that the sort key is optional, we are going to leave it blank since it’s not necessary for our simple application.

1.2

You can leave the rest of the settings as it is to default values and click on create table at the end of the page. This will create your table in a few moments. Now that the DB is ready, let’s move on to the next step.

2. Creating IAM role

To create an IAM role, search for IAM in the top search box and once you are on the dashboard, go to Roles from the left panel (2.1) to create a new role and click on create the role.

2.1

In step 1, you can select Lambda from the common use cases (2.2) as that is what we are going to be using our role for and go to the next step.

2.2

In the next step, we need to add permissions for our role. For this project, we are going to add two permissions.

The first one is for Dynamo DB and the other one for CloudWatch. To add them simply search for Dynamo DB in the search box and select DynamoDB full access permission from the list (2.3).

2.3

Next, search for CloudWatch full access and add it as well(2.4). We are going to use CloudWatch to see the logs from our lambda function.

2.4

In the next step, give a name for the role (2.5) that we are creating and click on create role at the end of the page.

2.5

This will create an IAM role that we will connect to our lambda, which will allow it to connect to DynamoDB and CloudWatch seamlessly. Now let’s move on to the next step.

3. Create the lambda

Now let’s create the lambda that will serve as the backend for our APIs. Search for lambda in the top search box and click on create role once you are on the lambda dashboard.

In the next step give the lambda service a name and leave the default runtime as Node.js as that is what we will use for this project.

If you are building using Python or Ruby, you can change it here. Next click on the Change default execution role title and select Use an existing role.

Now select the role that we created in the previous step from the dropdown right below it. Once all these are filled in (3.1), click on create function and this will create our lambda in a moment.

3.1

4. Create the API Gateway

Next, we are going to create an API Gateway which will act as the “front door” for our applications to access the service. Search for API Gateway and on the dashboard page, click on create API.

In the next step, select the type of API that we are going to create. Make sure that you select REST API from the list of options given and click on build(4.1).

4.1

On the next page, give a name for the gateway and you can leave the rest of the defaults as it is and click on Create API (4.2)

4.2

Now let’s create the endpoints that we are going to use in our application. First, click on actions and create a resource in this API (4.3)

4.3

Give the resource name and define its path. Here we are going to create one for login, so our path is going to be /login, you can define any path that you want. Make sure that you click on Enable API Gateway CORS to avoid CORS issues in the front-end and click on create resource (4.4).

4.4

Now let’s create the method under this resource. Select the resource first and click on create method from the actions (4.5)

4.5

From the list of methods, select post (4.6) as this is what we are going to use for our API endpoint, and click on the tick mark next to it.

4.6

In the setup, make sure that you check the Use Lambda proxy Integration to ensure our requests are proxied to lambda (4.7). Also, select the same lambda that we created earlier from the dropdown below. You can leave the rest of the options as default and click on save.

4.7

Follow the same steps to create three endpoints

  • login
  • register
  • verify.

Once done select the root resource and select Deploy API from the list of actions.

4.9

Give your deployment stage a name and description. Click on deploy and your API will be deployed in seconds.

4.10

In the resulting page, copy the invoke URL for later use and this will be the base URL of our API.

4.11

5. Create the backend service

In this step, we are going to code the actual backend that will contain the logic for our login service. We can do this locally and later deploy it to lambda. To get started, create a new empty project in VS Code and run npm init to initialize a Node JS project. The final folder structure will look like this.

Now let’s configure our index.js as the handler for the lambda function. Open index.js and add the following code.

As you can see we are creating a handler, which matches the request method against the paths that we have defined in our API gateway and calls the corresponding service. We will create individual services for-

  • /register — takes the name, username, and password from the request body and creates and new user in the DB. Returns the username upon success.
  • login — takes username and password from the request body and matches it against the DB for existing users. Returns the username and JWT token on success.
  • verify — takes username and JWT token from the request body and decodes it to verify that the token is valid. Returns verified message along with user info if successful.

You can find the code for these methods here on github.

The important part of our project is the DB Helper which will connect to our DynamoDB table to create a new user and also compare their credentials during login. For this, we will create a user.js helper within the dbHelper folder.

This helper will contain two methods, one for checking if the user exists by comparing the primary key, which is the username. And a second method to save a new user using the same primary key.

We will also create auth helper to generate and verify JWT tokens during login and verification. We are using the jsonwebtoken npm library to generate access tokens using a JWT secret that we define. We are going to set the expiry of the token as one hour for now, but you can change this as per your needs.

One thing to note here is that we are using an environment variable JWT_SECRET for encoding the JWT token, we will define this later. We have used three npm packages in this project -

  • aws-sdk — for connecting to DynamoDB.
  • [bcryptjs](https://www.npmjs.com/package/bcryptjs) — for encrypting the user’s password before storing it in the DB.
  • jsonwebtoken — To generate and verify JWT tokens.

You can find the full code for the project in this GitHub repo.

6. Deploy the code to lambda

Before uploading the project to lambda make sure that you have run npm i to install the packages that we are using. There are multiple methods to upload our code lambda including CLI, VS Code extension, serverless, etc. For now, we are going to upload the code manually from the console. Run the command zip -r ./archive.zip * after installing npm packages, to zip our project for lambda.

Now let’s go back to our lambda in the AWS console. Here you can find the upload from option (6.1) under the code tab of the lambda. Click on it and upload the zip file from the project that you just created.

This will upload our code and deploy the lambda in a few moments. Please note that when you are uploading a new version for an existing lambda it may take a minute in some cases to reflect that changes when you call it from your app.

6.1 Upload zip to lambda

7. Configure the secret key

If you remember from step 6, we need need to define an environment variable JWT_SECRET for encoding the JWT token. Let's configure this environment variable in our Lambda. Go to the configuration tab and select Environment variables from the left menu.

Click on edit, and create a new environment variable with the same name that we used in our code (7.1). You can define your secret key, but remember never to share this with anyone or keep the key value in your code. Save this key and our API is now ready.

7.1

8. Testing from Postman

Now lets our APIs one by one from the postman and check if everything is running smoothly. You can use the API URL that we copied at the end of step 4 or go to your API gateway and copy the URL (4.11).

First, let’s create a new user using the register API. As you can see we are passing name, username, and password as inputs, and returns the created username upon success (8.1)

8.1

Let’s also check that the user is created our DynamoDB table. Goto the table from the console and click on explore table items (8.2). If you run a scan, you can see that the user entry has been created in the table.

8.2

We can try our login API using the same credentials to generate an access token (8.3).

8.3

And finally, use the verify API to validate the token against a user (8.4).

8.4

You can import the complete postman collection here, but remember to update the base URL with your API URL.

More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Check out our Community Discord and join our Talent Collective.

Click on image

Did you find this article valuable?

Support Nabil Nalakath by becoming a sponsor. Any amount is appreciated!