| Voiced by Amazon Polly | 
Introduction
Serverless architectures have transformed how developers build APIs. By offloading server management to cloud providers, developers can focus purely on business logic. However, building secure serverless APIs requires careful planning, especially around authentication and access control. In this blog, we will build a secure serverless API using Amazon API Gateway, AWS Lambda, and Amazon Cognito, along with Python Lambda code, Terraform infrastructure setup, and JWT token validation.
Pioneers in Cloud Consulting & Migration Services
- Reduced infrastructural costs
- Accelerated application deployment
Why Serverless APIs?
Serverless APIs provide several advantages:
- No server management: AWS handles the underlying infrastructure.
- Automatic scaling: AWS Lambda functions scale automatically based on demand.
- Cost-efficient: Pay only for the compute time you use.
- Integrated security: Use services like Amazon Cognito for authentication.
Architecture Overview
Our architecture will include:
- Amazon Cognito User Pool – for managing users and authentication.
- Amazon API Gateway – to expose RESTful endpoints.
- AWS Lambda – backend business logic.
- JWT token validation – to secure API endpoints.
Step-by-Step Guide
Step 1: Set up Amazon Cognito User Pool
Amazon Cognito provides user management, sign-up/sign-in capabilities, and JWT tokens.
Terraform Example:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | resource "aws_cognito_user_pool" "my_user_pool" {   name = "my-user-pool"   password_policy {     minimum_length    = 8     require_uppercase = true     require_numbers   = true     require_symbols   = false   } } resource "aws_cognito_user_pool_client" "my_app_client" {   name         = "my-app-client"   user_pool_id = aws_cognito_user_pool.my_user_pool.id   generate_secret = false } | 
After creating the user pool and app client, users can register and authenticate via Amazon Cognito, which issues JWT access and ID tokens.
Step 2: Create the AWS Lambda Function
We will create a simple Python Lambda function to respond to authenticated API requests.
Python Lambda Code (lambda_function.py):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import json import jwt import os from jwt import PyJWKClient COGNITO_POOL_ID = os.environ.get("COGNITO_POOL_ID") REGION = os.environ.get("AWS_REGION") USER_POOL_URL = f"https://cognito-idp.{REGION}.amazonaws.com/{COGNITO_POOL_ID}" jwks_client = PyJWKClient(f"{USER_POOL_URL}/.well-known/jwks.json") def lambda_handler(event, context):     token = event['headers'].get('Authorization', '').split(' ')[-1]     if not token:         return {             "statusCode": 401,             "body": json.dumps({"message": "Unauthorized"})         }     try:         signing_key = jwks_client.get_signing_key_from_jwt(token)         decoded_token = jwt.decode(token, signing_key.key, algorithms=["RS256"], audience=os.environ.get("COGNITO_CLIENT_ID"))         return {             "statusCode": 200,             "body": json.dumps({                 "message": "Hello, authenticated user!",                 "user": decoded_token["username"]             })         }     except Exception as e:         return {             "statusCode": 401,             "body": json.dumps({"message": "Unauthorized", "error": str(e)})         } | 
- Uses PyJWT to validate JWT tokens issued by Amazon Cognito.
- Verifies audience and signature.
- Responds with 401 Unauthorized for invalid tokens.
Step 3: Deploy AWS Lambda with Terraform
Terraform Example for AWS Lambda and AWS IAM Role:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | resource "aws_iam_role" "lambda_exec" {   name = "lambda-exec-role"   assume_role_policy = jsonencode({     Version = "2012-10-17"     Statement = [       {         Action    = "sts:AssumeRole"         Effect    = "Allow"         Principal = { Service = "lambda.amazonaws.com" }       }     ]   }) } resource "aws_iam_role_policy_attachment" "lambda_basic" {   role       = aws_iam_role.lambda_exec.name   policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } resource "aws_lambda_function" "api_lambda" {   function_name = "api-lambda"   runtime       = "python3.11"   handler       = "lambda_function.lambda_handler"   role          = aws_iam_role.lambda_exec.arn   filename      = "lambda_function.zip"   environment {     variables = {       COGNITO_POOL_ID   = aws_cognito_user_pool.my_user_pool.id       COGNITO_CLIENT_ID = aws_cognito_user_pool_client.my_app_client.id       AWS_REGION        = "us-east-1"     }   } } | 
| 1 2 3 4 5 6 7 8 9 10 | # Zip your Lambda function zip lambda_function.zip lambda_function.py # Initialize Terraform terraform init # Validate Terraform config terraform validate # Plan the deployment terraform plan -out plan.out # Apply the Terraform plan terraform apply plan.out | 
Step 4: Set up Amazon API Gateway
Terraform Example for Amazon API Gateway:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | resource "aws_apigatewayv2_api" "api" {   name          = "serverless-api"   protocol_type = "HTTP" } resource "aws_apigatewayv2_integration" "lambda_integration" {   api_id           = aws_apigatewayv2_api.api.id   integration_type = "AWS_PROXY"   integration_uri  = aws_lambda_function.api_lambda.invoke_arn   payload_format_version = "2.0" } resource "aws_apigatewayv2_route" "get_route" {   api_id    = aws_apigatewayv2_api.api.id   route_key = "GET /hello"   target    = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" } resource "aws_apigatewayv2_stage" "default" {   api_id      = aws_apigatewayv2_api.api.id   name        = "$default"   auto_deploy = true } | 
| 1 2 3 4 5 6 7 8 9 | resource "aws_apigatewayv2_authorizer" "cognito_auth" {   api_id      = aws_apigatewayv2_api.api.id   authorizer_type = "JWT"   identity_sources = ["$request.header.Authorization"]   jwt_configuration {     audience = [aws_cognito_user_pool_client.my_app_client.id]     issuer   = "https://cognito-idp.${var.region}.amazonaws.com/${aws_cognito_user_pool.my_user_pool.id}"   } } | 
Attach authorizer to route:
| 1 2 3 4 5 6 | resource "aws_apigatewayv2_route" "get_route" {   api_id    = aws_apigatewayv2_api.api.id   route_key = "GET /hello"   target    = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}"   authorizer_id = aws_apigatewayv2_authorizer.cognito_auth.id } | 
Step 5: Testing the API
- Sign up a user in Amazon Cognito and confirm their account.
- Authenticate and retrieve a JWT token.
- Make an API call with curl:
| 1 | curl -H "Authorization: Bearer <JWT_TOKEN>" https://<api-id>.execute-api.<region>.amazonaws.com/hello | 
Expected output:
| 1 2 3 4 | {   "message": "Hello, authenticated user!",   "user": "username" } | 
Best Practices
- Use environment variables: Store configuration outside code.
- Enable logging: Both AWS Lambda and Amazon API Gateway logging.
- Enable throttling: Protect APIs from abuse.
- Use least privilege: Limit AWS Lambda execution role permissions.
- Secure endpoints: Always use Amazon Cognito JWT validation.
Conclusion
By combining Amazon Cognito, Amazon API Gateway, and AWS Lambda, you can build fully secure, serverless APIs with minimal operational overhead.
Drop a query if you have any questions regarding Amazon Cognito, Amazon API Gateway, or AWS Lambda and we will get back to you quickly.
Empowering organizations to become ‘data driven’ enterprises with our Cloud experts.
- Reduced infrastructure costs
- Timely data-driven decisions
About CloudThat
CloudThat is an award-winning company and the first in India to offer cloud training and consulting services worldwide. As a Microsoft Solutions Partner, AWS Advanced Tier Training Partner, and Google Cloud Platform Partner, CloudThat has empowered over 850,000 professionals through 600+ cloud certifications winning global recognition for its training excellence including 20 MCT Trainers in Microsoft’s Global Top 100 and an impressive 12 awards in the last 8 years. CloudThat specializes in Cloud Migration, Data Platforms, DevOps, IoT, and cutting-edge technologies like Gen AI & AI/ML. It has delivered over 500 consulting projects for 250+ organizations in 30+ countries as it continues to empower professionals and enterprises to thrive in the digital-first world.
FAQs
1. Can I use Amazon API Gateway Lambda Proxy integration for non-Python Lambda functions?
ANS: – Yes. AWS Lambda Proxy integration supports Node.js, Java, Go, and any other runtime supported by AWS Lambda.
2. What is the difference between Amazon Cognito User Pool and Identity Pool?
ANS: –
- User Pool: Manages users and authentication (JWT tokens).
- Identity Pool: Provides temporary AWS credentials to access AWS services.
3. How do I rotate AWS Lambda environment variables securely?
ANS: – Use AWS Secrets Manager or Parameter Store for sensitive variables, such as Amazon Cognito client secrets.
 
            WRITTEN BY Rajveer Singh Chouhan
Rajveer works as a Cloud Engineer at CloudThat, specializing in designing, deploying, and managing scalable cloud infrastructure on AWS. He is skilled in various AWS services as well as automation tools like Terraform and CI/CD pipelines. With a strong understanding of cloud architecture best practices, Rajveer focuses on building secure, cost-effective, and highly available solutions. In his free time, he keeps up with the latest advancements in cloud technologies and enjoys exploring infrastructure automation and DevOps tools.
 
  
  Login
 Login
 
        
 October 31, 2025
 October 31, 2025




 PREV
 PREV 
                                   
                                   
                                   
                                   
                                   
                                   
                                   
                                   
                                   
                                  
Comments