|
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
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

October 31, 2025
PREV
Comments