Storing session data from web apps created with API Gateway and Lambda in DynamoDB

TOC

Storing session data from web apps created with API Gateway and Lambda in DynamoDB

This is one of the topics of the AWS DBA, which is related to development with AWS services.

Think about how to handle session information in web apps created with API Gateway and Lambda functions.

In this article, we will show you how to store session information in DynamoDB.
Cookies are used for session management.

Environment

Diagram of storing session data from web apps created with API Gateway and Lambda in DynamoDB.

Create a DynamoDB table.
This table will store session information.
Specifically, it stores two pieces of data per session.

  • Session ID
  • Counter

The counter records the number of times the application is accessed during a session.

Create a Lambda function.
The function’s action is to access DynamoDB, retrieve/store session information, and store the session ID in a cookie.
The runtime environment for the function is Python 3.8.

Create an API Gateway and place a Lambda function on the backend.
When an HTTP request is received from a user, the API Gateway acts as an endpoint, calling a function on behalf of the user and returning the result of the function invocation to the user.
The API Gateway should be of the HTTP API type.

CloudFormation template files

The above configuration is built with CloudFormation.
The CloudFormation templates are placed at the following URL

https://github.com/awstut-an-r/awstut-dva/tree/main/03/006

Explanation of key points of template files

This page focuses on how to manage session information in DynamoDB.

For basic information on HTTP API type API Gateway, please refer to the following pages.

あわせて読みたい
Serverless apps using Lambda and API Gateway – HTTP API 【Creating Serverless Application with API Gateway and Lambda】 We will combine two resources to create a simple serverless web application. The first is Lam...

DynamoDB

Resources:
  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: session-id
          AttributeType: S
      BillingMode: PAY_PER_REQUEST
      KeySchema:
        - AttributeName: session-id
          KeyType: HASH
      TableName: !Sub "${Prefix}-table"
Code language: YAML (yaml)

Create a DynamoDB table.
For basic information on DynamoDB tables, please refer to the following page.

あわせて読みたい
Introduction to DynamoDB – Building DB for Review Data 【Building Simple DB for Review Data with DynamoDB】 This is one of the AWS DVA topics related to development with AWS services.As an introduction to DynamoD...

Set the “session-id” attribute to the table and use this as the partition key.

Lambda

Function

Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Architectures:
        - !Ref Architecture
      Code:
        ZipFile: |
          import boto3
          import json
          import os
          import uuid
          from http import cookies

          ATTR_COUNTER = 'counter'
          ATTR_SESSION_ID = 'session-id'
          COOKIE_KEY = 'cookie-test'
          DYNAMODB_ITEM_KEY = 'Item'
          TABLE_NAME = os.environ['TABLE_NAME']

          dynamodb_client = boto3.client('dynamodb')

          def lambda_handler(event, context):
            session_id = ''
            if not 'cookies' in event:
              session_id = str(uuid.uuid4())
            else:
              C = cookies.SimpleCookie()
              C.load('; '.join([cookie for cookie in event['cookies']]))
              cookie_dict = {k: v.value for k, v in C.items()}

              if not COOKIE_KEY in cookie_dict:
                session_id = str(uuid.uuid4())
              else:
                session_id = cookie_dict[COOKIE_KEY]

            dynamodb_get_item_response = dynamodb_client.get_item(
              TableName=TABLE_NAME,
              Key={
                ATTR_SESSION_ID: {'S': session_id}
              }
            )
            #print(dynamodb_get_item_response)

            counter = 0
            if not DYNAMODB_ITEM_KEY in dynamodb_get_item_response:
              counter = 1
            else:
              counter = int(
                dynamodb_get_item_response[DYNAMODB_ITEM_KEY][ATTR_COUNTER]['N']) + 1

            dynamodb_put_item_response = dynamodb_client.put_item(
              TableName=TABLE_NAME,
              Item={
                ATTR_SESSION_ID: {'S': session_id},
                ATTR_COUNTER: {'N': str(counter)}
              }
            )
            #print(dynamodb_put_item_response)

            body = 'session-id: {session_id}, counter: {counter}'.format(
              session_id=session_id,
              counter=counter)

            set_cookie = '{cookie_key}={session_id}'.format(
              cookie_key=COOKIE_KEY,
              session_id=session_id)

            return {
              'statusCode': 200,
              'body': body,
              'headers': {
                'Set-Cookie': set_cookie
              }
            }
      Environment:
        Variables:
          TABLE_NAME: !Ref Table
      FunctionName: !Sub "${Prefix}-function"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)

The code to be executed by the Lambda function in inline format.
For more information, please refer to the following page.

あわせて読みたい
3 parterns to create Lambda with CloudFormation (S3/Inline/Container) 【Creating Lambda with CloudFormation】 When creating a Lambda with CloudFormation, there are three main patterns as follows. Uploading the code to an S3 buc...

Define environment variables in the Environment property.
Set the table name of the DynamoDB table mentioned above.

The processing to be performed by this function is as follows

  • Access cookie information from the event object to obtain a session ID.
    If no session ID is registered in the cookie, generate a unique UUID-based ID and use this as the session ID.
  • Attempt to retrieve an item from the DynamoDB table using the session ID, and if there is a corresponding item, retrieve the counter value.
    If there is no corresponding item, set the counter value to 1.
  • Store the counter value in the DynamoDB table using the session ID as the partition key.
  • In the HTTP response header, set the session ID in the cookie.
    In the HTTP response body, set a string containing the session ID and counter value.

IAM Role

Resources:
  FunctionRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: SessionPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - dynamodb:GetItem
                  - dynamodb:PutItem
                Resource:
                  - !Sub "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${Table}"
Code language: YAML (yaml)

Inline policies allow reading and writing to DynamoDB tables.

Architecting

Use CloudFormation to build this environment and check its actual behavior.

Create CloudFormation stacks and check the resources in the stacks

Create CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following pages.

あわせて読みたい
CloudFormation’s nested stack 【How to build an environment with a nested CloudFormation stack】 Examine nested stacks in CloudFormation. CloudFormation allows you to nest stacks. Nested ...

After reviewing the resources in each stack, information on the main resources created in this case is as follows

  • DynamoDB table: dva-03-006-table
  • Lambda function: dva-03-006-function
  • API gateway endpoint: dva-03-006-HttpApi

Check each resource from the AWS Management Console.

First, check the DynamoDB table.

Detail of DyanmoDB 1.

You can see that the table has been successfully created with the partition key as “session-id”.

Check the Lambda function.

Detail of Lambda 1.

You can see that the function has been created successfully.

Check the API Gateway.

Detail of API Gateway 1.
Detail of API Gateway 2.

You can see that the HTTP type API Gateway has been successfully created.
You can also see that the API Gateway and Lambda functions have been integrated.

Action Check

Now that you are ready, access the API Gateway endpoint.

Detail of API Gateway 3.

Session ID and counter are displayed.
The counter value is “1”.

Check cookies in the Developer Tool.

Detail of API Gateway 4.

Indeed, a session ID is set with the name “cookie-test”.

Access the same page again.

Detail of API Gateway 5.

Counter value updated.

Check the DynamoDB table.

Detail of DyanmoDB 2.

The table shows that the session ID and counter values are stored.

Thus, by referring to the cookie value stored in the cookie, the session is continued and the counter value is updated.

Finally, access the same URL in another browser (incognito window).

Detail of API Gateway 6.
Detail of DyanmoDB 3.

A new session ID is generated and registered with a counter value of “1”.
Thus, it can be confirmed that session management is done for each browser.

Summary

We have shown you how to store session information in DynamoDB.

TOC