DEV Community

Cover image for Implementing Key and Token Authentication for a NodeJS API on AWS SAM
Benson Macharia for AWS Community Builders

Posted on • Edited on

Implementing Key and Token Authentication for a NodeJS API on AWS SAM


In this article, we will look at how to create a securely authenticated serverless NodeJs API leveraging on AWS API Gateway key and custom JWT token. AWS SAM (Serverless Application Model) creates secure, high-performing APIs and provides developers with a simplified development environment that allows them to solely focus on writing code without worrying about server management, infrastructure scaling, or hardware failures.

Designs

High-Level Architecture Diagram

  • The API calls from a mobile or web client are routed through an API Gateway to a Lambda function for processing. Data is persisted in a DynamoDb and logs stored on CloudWatch.

High-Level architecture diagram for a NodeJS API deployed on AWS SAM

Sequence Diagram

  • The user first creates an account and then login using their credentials i.e. username and a password to get an access token. Making use of the access token, the user is then able to create a new event, update details and view all the events available.

Sequence diagram for a NodeJS API deployed on AWS SAM

Prerequisite

  • Basic skill in NodeJs
  • AWS Account
  • AWS CLI

Follow this guide (AWS CLI) to install.

$ aws --version aws-cli/2.7.32 Python/3.9.11 Darwin/23.0.0 exe/x86_64 prompt/off 
Enter fullscreen mode Exit fullscreen mode
  • SAM CLI

Follow this guide (SAM CLI) to install.

$ sam --version SAM CLI, version 1.73.0 
Enter fullscreen mode Exit fullscreen mode

Source
You can get the full source code here.

$ git clone https://github.com/bensonmacharia/sam-node-api.git 
Enter fullscreen mode Exit fullscreen mode

Let's Build

1. Environment Setup

  • Create the project folder and initialise the sam project.
$ mkdir sam-projects && cd sam-projects $ sam init -r nodejs18.x -d npm -n sam-node-api Which template source would you like to use? Choice: 1 Choose an AWS Quick Start application template Template: 6 Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: N Would you like to enable monitoring using CloudWatch Application Insights? [y/N]: N 
Enter fullscreen mode Exit fullscreen mode
  • We are specifying to use nodejs version 18, npm as the dependency manager for our Lambda runtime and sam-node-api as the name of our project. We have also chosen not to enable X-Ray tracing and CloudWatch Application Insights.

2. Let's do some clean up

  • Open the generated source code in your favourite IDE. In this case, vscode.
$ cd sam-node-api && code . 
Enter fullscreen mode Exit fullscreen mode
  • Delete the files that we will not be using.
$ rm -rf sam-node-dev events && rm -rf __tests__ 
Enter fullscreen mode Exit fullscreen mode

3. Time now to edit the template.yaml file

  • In AWS SAM, template.yaml serves as the foundational configuration file for defining our serverless application. Clear the content on the template.yaml file and add the following below content.

  • AWSTemplateFormatVersion: Specifies the AWS CloudFormation template version. For SAM templates, this is usually set to '2010-09-09'.

AWSTemplateFormatVersion: 2010-09-09 
Enter fullscreen mode Exit fullscreen mode
  • Transform: Specifies the macro (transform) that AWS CloudFormation uses to process the template. For SAM templates, this should be set to 'AWS::Serverless-2016-10-31'.
- AWS::Serverless-2016-10-31 
Enter fullscreen mode Exit fullscreen mode
  • Description: Provides a description of the CloudFormation stack and the serverless application.
Description: >- sam-node-api 
Enter fullscreen mode Exit fullscreen mode
  • Resources: This section defines the AWS resources that make up your serverless application, such as AWS Lambda functions, API Gateway APIs, DynamoDB tables, and more. In this case we have an API Gateway, Secrets Manager, Lambda functions and DynamoDB as below.

API Gateway

Resources: # Create an API Gateway and add an API Key ApiGatewayEndpoint: Type: 'AWS::Serverless::Api' Properties: StageName: Prod Auth: # Require API Key for all endpoints ApiKeyRequired: true UsagePlan: CreateUsagePlan: PER_API UsagePlanName: GatewayAuthorization 
Enter fullscreen mode Exit fullscreen mode

Secret Manager

JWTUserTokenSecret: Type: AWS::SecretsManager::Secret Properties: Name: JWTUserTokenSecret Description: "This secret has a dynamically generated secret password." # Generate a random string 30 charatcers long for the JWT secret GenerateSecretString: GenerateStringKey: 'jwt_secret' PasswordLength: 30 ExcludeCharacters: '"@/\:;+*''' SecretStringTemplate: '{"secret_name": "sam-node-app-jwt-secret"}' 
Enter fullscreen mode Exit fullscreen mode

Get All Events Lambda Function

# This is a Lambda function config associated with the source code: get-all-events.js for getting all the Events. getAlleventsFunction: Type: AWS::Serverless::Function Properties: Handler: src/handlers/get-all-events.getAllEventsHandler Runtime: nodejs18.x Architectures: - x86_64 MemorySize: 128 Timeout: 100 Description: Includes a HTTP get method to get all events from a DynamoDB table. Policies: # Give Create/Read/Update/Delete Permissions to the EventsTable - DynamoDBCrudPolicy: TableName: !Ref EventsTable - AWSSecretsManagerGetSecretValuePolicy: SecretArn: !Ref JWTUserTokenSecret Environment: Variables: # Make table name accessible as environment variable from function code during execution SAMPLE_TABLE: !Ref EventsTable Events: Api: Type: Api Properties: RestApiId: Ref: ApiGatewayEndpoint # Expose the API through the path /v1/api/events Path: /v1/api/events Method: GET 
Enter fullscreen mode Exit fullscreen mode

Get Single Event By ID Lambda Function

# This is a Lambda function config associated with the source code: get-event.js for getting a single Event by its ID getEventByIdFunction: Type: AWS::Serverless::Function Properties: Handler: src/handlers/get-event.getEventByIdHandler Runtime: nodejs18.x Architectures: - x86_64 MemorySize: 128 Timeout: 100 Description: Includes a HTTP get method to get one event by id from a DynamoDB table. Policies: # Give Create/Read/Update/Delete Permissions to the EventsTable - DynamoDBCrudPolicy: TableName: !Ref EventsTable - AWSSecretsManagerGetSecretValuePolicy: SecretArn: !Ref JWTUserTokenSecret Environment: Variables: # Make table name accessible as environment variable from function code during execution SAMPLE_TABLE: !Ref EventsTable Events: Api: Type: Api Properties: RestApiId: Ref: ApiGatewayEndpoint # Expose the API through the path /v1/api/event/<id> Path: /v1/api/event/{id} Method: GET 
Enter fullscreen mode Exit fullscreen mode

Create an Event Lambda Function

# This is a Lambda function config associated with the source code: put-item.js for posting an Event into the database addEventFunction: Type: AWS::Serverless::Function Properties: Handler: src/handlers/add-event.addEventHandler Runtime: nodejs18.x Architectures: - x86_64 MemorySize: 128 Timeout: 100 Description: Includes a HTTP post method to add one event to a DynamoDB table. Policies: # Give Create/Read/Update/Delete Permissions to the EventsTable - DynamoDBCrudPolicy: TableName: !Ref EventsTable - AWSSecretsManagerGetSecretValuePolicy: SecretArn: !Ref JWTUserTokenSecret Environment: Variables: # Make table name accessible as environment variable from function code during execution SAMPLE_TABLE: !Ref EventsTable Events: Api: Type: Api Properties: RestApiId: Ref: ApiGatewayEndpoint # Expose the API through the path /v1/api/event Path: /v1/api/event Method: POST 
Enter fullscreen mode Exit fullscreen mode

User Registration Lambda Function

# This is a Lambda function config associated with the source code: user-register.js for adding a new user record into the database registerUserFunction: Type: AWS::Serverless::Function Properties: Handler: src/handlers/user-register.registerUserHandler Runtime: nodejs18.x Architectures: - x86_64 MemorySize: 128 Timeout: 100 Description: A HTTP post method to register a user and add record to a DynamoDB table. Policies: # Give Create/Read/Update/Delete Permissions to the UsersTable - DynamoDBCrudPolicy: TableName: !Ref UsersTable Environment: Variables: # Make table name accessible as environment variable from function code during execution USER_TABLE: !Ref UsersTable Events: Api: Type: Api Properties: RestApiId: Ref: ApiGatewayEndpoint # Expose the API through the path /v1/auth/user/register Path: /v1/auth/user/register Method: POST Auth: ApiKeyRequired: false 
Enter fullscreen mode Exit fullscreen mode

User Login Lambda Function

# This is a Lambda function config associated with the source code: user-login.js for fetching a user record from the database loginUserFunction: Type: AWS::Serverless::Function Properties: Handler: src/handlers/user-login.loginUserHandler Runtime: nodejs18.x Architectures: - x86_64 MemorySize: 128 Timeout: 100 Description: A HTTP post method to login a user and fetch record from a DynamoDB table. Policies: # Give Create/Read/Update/Delete Permissions to the UsersTable - DynamoDBCrudPolicy: TableName: !Ref UsersTable - AWSSecretsManagerGetSecretValuePolicy: SecretArn: !Ref JWTUserTokenSecret Environment: Variables: # Make table name accessible as environment variable from function code during execution USER_TABLE: !Ref UsersTable Events: Api: Type: Api Properties: RestApiId: Ref: ApiGatewayEndpoint # Expose the API through the path /v1/auth/user/login Path: /v1/auth/user/login Method: POST Auth: ApiKeyRequired: false 
Enter fullscreen mode Exit fullscreen mode

Events DynamoDB Table

# DynamoDB table to store Event details EventsTable: Type: AWS::Serverless::SimpleTable Properties: PrimaryKey: Name: id Type: String ProvisionedThroughput: ReadCapacityUnits: 2 WriteCapacityUnits: 2 
Enter fullscreen mode Exit fullscreen mode

Users DynamoDB Table

 # DynamoDB table to store User details UsersTable: Type: AWS::Serverless::SimpleTable Properties: PrimaryKey: Name: username Type: String ProvisionedThroughput: ReadCapacityUnits: 2 WriteCapacityUnits: 2 
Enter fullscreen mode Exit fullscreen mode
  • Outputs: Specifies the values to be exported from the stack once it's created. Outputs can include endpoint URLs, resource IDs, or any other information you want to make accessible after the deployment.
Outputs: ApiGateway: Description: "The URL is:" Value: !Sub "https://${ApiGatewayEndpoint}.execute-api.${AWS::Region}.amazonaws.com/Prod/" ApiKey: Description: "You can find your API Key in the AWS console: (Put in the request HEADER as 'x-api-key')" Value: !Sub "https://console.aws.amazon.com/apigateway/home?region=${AWS::Region}#/api-keys/${ApiGatewayEndpointApiKey}" 
Enter fullscreen mode Exit fullscreen mode

4. Edit the Environment Variables

  • Modify the env.json file to define our variables for the database names as below.
{ "getAllEventsFunction": { "SAMPLE_TABLE": "EventsTable" }, "getEventByIdFunction": { "SAMPLE_TABLE": "EventsTable" }, "addEventFunction": { "SAMPLE_TABLE": "EventsTable" }, "registerUserFunction": { "USER_TABLE": "UsersTable" }, "loginUserFunction": { "USER_TABLE": "UsersTable" } } 
Enter fullscreen mode Exit fullscreen mode

5. Install Dependencies

# Install API Gateway SDK $ npm install @aws-sdk/client-api-gateway # Install Secrets Manager SDK $ npm install @aws-sdk/client-secrets-manager # Install jsonwebtoken package for generating JWT tokens $ npm install jsonwebtoken # Install bcryptjs package for password encryption $ npm install bcryptjs # Install uuid package for generating uuidv4 ids $ npm install uuid 
Enter fullscreen mode Exit fullscreen mode

6. Let's Handle User Registration

  • Create a new file user-register.mjs under sam-node-api/src/handlers
$ touch src/handlers/user-register.mjs 
Enter fullscreen mode Exit fullscreen mode
  • Add the user registration logic on user-register.mjs
// Import bcrypt for encrypting user password import bcrypt from 'bcryptjs'; // Import uuid for generating unique user ID import { v4 as uuidv4 } from 'uuid'; // Create a DocumentClient that represents the query to add an item import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; const client = new DynamoDBClient({}); const ddbDocClient = DynamoDBDocumentClient.from(client); // Get the DynamoDB table name from environment variables const tableName = process.env.USER_TABLE; /** * A HTTP post method to add one user to a DynamoDB table. */ export const registerUserHandler = async (event) => { if (event.httpMethod !== 'POST') { throw new Error(`postMethod only accepts POST method, you tried: ${event.httpMethod} method.`); } // All log statements are written to CloudWatch console.info('received:', event); // Get username and password from the body of the request const body = JSON.parse(event.body); // Use a random uuidv4 string as a User ID  const id = uuidv4(); const username = body.username; // Generate a hashed password string const password = bcrypt.hashSync(body.password, 8); // Creates a new user, or replaces an old user record with a new one var params = { TableName: tableName, Item: { id: id, username: username, password: password } }; try { const data = await ddbDocClient.send(new PutCommand(params)); console.log("Success - user registered or updated", data); } catch (err) { console.log("Error", err.stack); } const responseData = { message: "Registration successful. Proceed to login", username: body.username } const response = { statusCode: 201, body: JSON.stringify(responseData) }; // All log statements are written to CloudWatch console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); return response; }; 
Enter fullscreen mode Exit fullscreen mode

7. Handle User Login

  • Create a new file user-login.mjs under sam-node-api/src/handlers
$ touch src/handlers/user-login.mjs 
Enter fullscreen mode Exit fullscreen mode
  • Add the user login logic on user-login.mjs
// Import bcrypt for encrypting user password and comparing the password hash import bcrypt from 'bcryptjs'; // Create a DocumentClient that represents the query to add an item import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for generating the user token import jwt from 'jsonwebtoken'; const client = new DynamoDBClient({}); const ddbDocClient = DynamoDBDocumentClient.from(client); const clientsecret = new SecretsManagerClient(); // Get the DynamoDB table name from environment variables const tableName = process.env.USER_TABLE; /** * A HTTP post method for user login. */ export const loginUserHandler = async (event) => { if (event.httpMethod !== 'POST') { throw new Error(`postMethod only accepts POST method, you tried: ${event.httpMethod} method.`); } // All log statements are written to CloudWatch console.info('received:', event); // Get username and password from the body of the request const body = JSON.parse(event.body); const username = body.username; const password = body.password; // Fetch the JWT secret string from AWS Secrets Manager  const secret_value = await clientsecret.send(new GetSecretValueCommand({ SecretId: "JWTUserTokenSecret", })); const jwt_secret = JSON.parse(secret_value.SecretString); var params = { TableName: tableName, Key: { username: username }, }; var token = ""; var message = ""; var status_code = 200; try { const data = await ddbDocClient.send(new GetCommand(params)); var item = data.Item; // Comprate a hash of the received password from the request body with the password hash from the database, if they match login the user and generate a JWT token if (bcrypt.compareSync(password, item.password)) { // create JWT token const jwttoken = jwt.sign( { userId: item.id, userName: body.username, }, jwt_secret.jwt_secret, { expiresIn: "1h" } ); message = "Login successful."; token = jwttoken; } else { status_code = 403; message = "Wrong password. Try again"; token = "NOT OKAY"; } } catch (err) { console.log("Error", err); } const responseData = { message: message, user: { username: body.username, token: token } } const response = { statusCode: status_code, body: JSON.stringify(responseData) }; // All log statements are written to CloudWatch console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); return response; }; 
Enter fullscreen mode Exit fullscreen mode

8. Add Events Logic

  • Rename the file put-item.mjs in sam-node-api/src/handlers to add-event.mjs
$ mv src/handlers/put-item.mjs src/handlers/add-event.mjs 
Enter fullscreen mode Exit fullscreen mode
  • Edit the add-event.mjs file
// Create a DocumentClient that represents the query to add an item import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken'; const client = new DynamoDBClient({}); const ddbDocClient = DynamoDBDocumentClient.from(client); const clientsecret = new SecretsManagerClient(); // Get the DynamoDB table name from environment variables const tableName = process.env.SAMPLE_TABLE; /** * A HTTP post method to add one Event item to a DynamoDB table. */ export const addEventHandler = async (event) => { if (event.httpMethod !== 'POST') { throw new Error(`postMethod only accepts POST method, you tried: ${event.httpMethod} method.`); } // All log statements are written to CloudWatch console.info('received:', event); // Get token from the authorization header const token = await event.headers.Authorization.split(" ")[1]; if (!token) { throw new Error(`Authorization token required.`); } // Get Event id, title and description from the body of the request const body = JSON.parse(event.body); const id = body.id; const title = body.title; const description = body.description; // Fetch the JWT secret string from AWS Secrets Manager  const secret_value = await clientsecret.send(new GetSecretValueCommand({ SecretId: "JWTUserTokenSecret", })); const jwt_secret = JSON.parse(secret_value.SecretString); //Check if the token is valid const decodedToken = jwt.verify(token, jwt_secret.jwt_secret); if (!decodedToken) { throw new Error(`Authorization token invalid or it has expired.`); } // Decode the JWT token to get user data const userID = decodedToken.userId; const userName = decodedToken.userName; console.info('decode-username:', userName); console.info('decode-userid:', userID); // Creates a new Event item, or replaces an old item with a new item var params = { TableName : tableName, Item: { id : id, title: title, description: description, added_by: userName, added_by_id: userID} }; try { const data = await ddbDocClient.send(new PutCommand(params)); console.log("Success - event added successfully", data); } catch (err) { console.log("Error", err.stack); } const response = { statusCode: 200, body: JSON.stringify(body) }; // All log statements are written to CloudWatch console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); return response; }; 
Enter fullscreen mode Exit fullscreen mode

9. View All Events Login

  • Rename the file get-all-items.mjs in sam-node-api/src/handlers to get-all-events.mjs
$ mv src/handlers/get-all-items.mjs src/handlers/get-all-events.mjs 
Enter fullscreen mode Exit fullscreen mode
  • Edit the get-all-events.mjs file
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken'; const client = new DynamoDBClient({}); const ddbDocClient = DynamoDBDocumentClient.from(client); const clientsecret = new SecretsManagerClient(); // Get the DynamoDB table name from environment variables const tableName = process.env.SAMPLE_TABLE; /** * A HTTP get method to get all events from a DynamoDB table. */ export const getAllEventsHandler = async (event) => { if (event.httpMethod !== 'GET') { throw new Error(`getAllItems only accept GET method, you tried: ${event.httpMethod}`); } // All log statements are written to CloudWatch console.info('received:', event); // Get token from the authorization header const token = await event.headers.Authorization.split(" ")[1]; if (!token) { throw new Error(`Authorization token required.`); } // Fetch the JWT secret string from AWS Secrets Manager  const secret_value = await clientsecret.send(new GetSecretValueCommand({ SecretId: "JWTUserTokenSecret", })); const jwt_secret = JSON.parse(secret_value.SecretString); //Check if the token is valid const decodedToken = jwt.verify(token, jwt_secret.jwt_secret); if (!decodedToken) { throw new Error(`Authorization token invalid or it has expired.`); } // Get all items from the table (only first 1MB data, you can use `LastEvaluatedKey` to get the rest of data) var params = { TableName : tableName }; try { const data = await ddbDocClient.send(new ScanCommand(params)); var items = data.Items; } catch (err) { console.log("Error", err); } const response = { statusCode: 200, body: JSON.stringify(items) }; // All log statements are written to CloudWatch console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); return response; } 
Enter fullscreen mode Exit fullscreen mode

10. View Event By ID Login

  • Rename the file get-by-id.mjs in sam-node-api/src/handlers to get-event.mjs
$ mv src/handlers/get-by-id.mjs src/handlers/get-event.mjs 
Enter fullscreen mode Exit fullscreen mode
  • Edit the get-event.mjs file
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken'; const client = new DynamoDBClient({}); const ddbDocClient = DynamoDBDocumentClient.from(client); const clientsecret = new SecretsManagerClient(); // Get the DynamoDB table name from environment variables const tableName = process.env.SAMPLE_TABLE; /** * A HTTP get method to get one Event item by id from a DynamoDB table. */ export const getEventByIdHandler = async (event) => { if (event.httpMethod !== 'GET') { throw new Error(`getMethod only accept GET method, you tried: ${event.httpMethod}`); } // All log statements are written to CloudWatch console.info('received:', event); // Get the token from the authorization header const token = await event.headers.Authorization.split(" ")[1]; if (!token) { throw new Error(`Authorization token required.`); } // Fetch the JWT secret string from AWS Secrets Manager  const secret_value = await clientsecret.send(new GetSecretValueCommand({ SecretId: "JWTUserTokenSecret", })); const jwt_secret = JSON.parse(secret_value.SecretString); //Check if the token is valid const decodedToken = jwt.verify(token, jwt_secret.jwt_secret); if (!decodedToken) { throw new Error(`Authorization token invalid or it has expired.`); } // Get id from pathParameters from APIGateway because of `/{id}` at template.yaml const id = event.pathParameters.id; // Get the item from the table var params = { TableName : tableName, Key: { id: id }, }; var scode = 200; var bdy = ""; try { const data = await ddbDocClient.send(new GetCommand(params)); var item = data.Item; bdy = JSON.stringify(item); if (!bdy) { scode = 404; bdy = JSON.stringify("Event details not found."); } } catch (err) { console.log("Error", err); scode = 400; bdy = err; } const response = { statusCode: scode, body: bdy }; // All log statements are written to CloudWatch console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); return response; } 
Enter fullscreen mode Exit fullscreen mode

11. Build and Deploy the API

  • Build the SAM App
$ sam build Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml 
Enter fullscreen mode Exit fullscreen mode
  • Deploy the SAM App
$ sam deploy --guided Stack Name [sam-app]: sam-node-app AWS Region [us-east-1]: us-east-1 #Shows you resources changes to be deployed and require a 'Y' to initiate deploy Confirm changes before deploy [y/N]: y #SAM needs permission to be able to create roles to connect to the resources in your template Allow SAM CLI IAM role creation [Y/n]: Y #Preserves the state of previously provisioned resources when an operation fails Disable rollback [y/N]: N Save arguments to configuration file [Y/n]: Y SAM configuration file [samconfig.toml]: SAM configuration environment [default]: Deploy this changeset? [y/N]: y CloudFormation outputs from deployed stack --------------- Outputs --------------- Key ApiKey Description You can find your API Key in the AWS console: (Put in the request HEADER as 'x-api-key') Value https://console.aws.amazon.com/apigateway/home?region=us-east-1#/api-keys/w22y9chd81 Key ApiGateway Description The URL is: Value https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/ ----------------- Successfully created/updated stack - sam-node-app in us-east-1 
Enter fullscreen mode Exit fullscreen mode

12. Test the API

  • Test user registration
$ curl -X POST --header "Content-Type: application/json" --data '{"username":"bmacharia", "password":"pAssW0rd@s3cr3t"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/auth/user/register HTTP/1.1 200 OK {"message":"Registration successful. Proceed to login","username":"bmacharia"}% 
Enter fullscreen mode Exit fullscreen mode
  • Test user login
$ curl -X POST --header "Content-Type: application/json" --data '{"username":"bmacharia", "password":"pAssW0rd@s3cr3t"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/auth/user/login HTTP/1.1 200 OK {"message":"Login successful.","user":{"username":"bmacharia","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY"}}% 
Enter fullscreen mode Exit fullscreen mode
  • Test creating event
$ curl -X POST --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" --data '{"id":"2", "title":"Cybersecurity Summit", "description":"Downtown Hotel, 31st December 2023"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/event HTTP/1.1 200 OK {"id":"2","title":"Cybersecurity Summit","description":"Downtown Hotel, 31st December 2023"}% 
Enter fullscreen mode Exit fullscreen mode
  • Test getting all events
$ curl --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/events HTTP/1.1 200 OK [{"description":"Downtown Hotel, 31st December 2023","id":"2","title":"Cybersecurity Summit","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"},{"description":"Whitesands Hotel, 1st January 2024","id":"1","title":"GRC Conference","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"}]% 
Enter fullscreen mode Exit fullscreen mode
  • Get Event By ID
$ curl --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/event/1 HTTP/1.1 200 OK {"description":"Whitesands Hotel, 1st January 2024","id":"1","title":"GRC Conference","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"}% 
Enter fullscreen mode Exit fullscreen mode

13. Clean Up

  • Delete the SAM App
$ sam delete --stack-name sam-node-app Are you sure you want to delete the stack sam-node-app in the region us-east-1 ? [y/N]: y Are you sure you want to delete the folder sam-node-app in S3 which contains the artifacts? [y/N]: y Deleted successfully 
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
mean2me profile image
Emanuele Colonnelli

Much better and useful than mostly all official docs :)