Authentication and Authorization are tough problem almost. Doing it right is not easy. You need to balance usability and security. When you are building serverless application, like an API, you would want your APIs to be accessible by authorized users.
Let us try and understand this with an example. Our start-up Miztiik Corp
is a content publisher. Their platform leverage's serverless components like AWS Lambda & published their endpoints using AWS API Gateway. They want to authorize all access to their APIs serving premium content. They have adopted OAuth
for the authorization as that gives them the flexibility to integrate with multiple identity providers. The authorization is handled AWS Cognito using OAuth.
The appropriate authorization flow for Machine-To-Machine
or API-To-API
communication is called OAuth2 Client Credentials, there is no user, or identity associated with the access request. The calling service obtains an access token, and the target service asserts that token to be valid before granting access to the protected data. The process is fairly straightforward,
- The machine (i.e. script/Lambda/Requester) authenticates itself against a Cognito Endpoint with a list of desired scopes
- Cognito verifies the credentials and checks if the machine is allowed to get these scopes
- If the credentials are valid and the scopes can be granted, Cognito returns an Access Token to the machine
- The machine can use that Access Token to Authenticate itself against the API-Gateway or an Application Load Balancer
Scopes
are technically part of authorization. A scope more or less is a label that describes a capability such as READ_CONTENT
or WRITE_CONTENT
. You can create as many scopes as you like, but without further processing they are useless.
From the perspective of an App you get information about which scopes the current request has been granted and you as the app owner is responsible for implementing authorization measures based on that.
In this article, we will build the above architecture. using Cloudformation generated using AWS Cloud Development Kit (CDK). The architecture has been designed in a modular way so that we can build them individually and integrate them together. The prerequisites to build this architecture are listed below
-
This demo, instructions, scripts and cloudformation template is designed to be run in
us-east-1
. With few modifications you can try it out in other regions as well(Not covered here).- 🛠 AWS CLI Installed & Configured - Get help here
- 🛠 AWS CDK Installed & Configured - Get help here
- 🛠 Python Packages, Change the below commands to suit your OS, the following is written for amzn linux 2
- Python3 -
yum install -y python3
- Python Pip -
yum install -y python-pip
- Virtualenv -
pip3 install virtualenv
- Python3 -
-
-
Get the application code
git clone https://github.com/miztiik/serverless-api-authorizer.git cd serverless-api-authorizer
-
-
We will cdk to be installed to make our deployments easier. Lets go ahead and install the necessary components.
# If you DONT have cdk installed npm install -g aws-cdk # Make sure you in root directory python3 -m venv .env source .env/bin/activate pip3 install -r requirements.txt
The very first time you deploy an AWS CDK app into an environment (account/region), you’ll need to install a
bootstrap stack
, Otherwise just go ahead and deploy usingcdk deploy
.cdk bootstrap cdk ls # Follow on screen prompts
You should see an output of the available stacks,
cognito-identity-provider premium-content-provider content-consumers-stack
-
Let us walk through each of the stacks,
-
Stack: cognito-identity-provider In this scenario, Cognito’s User Pool is merely a placeholder, as we will have no users. The only user will be the app client. This stack creates the following resources,
- AWS Cognito Identity Pool to store identities
- Cognito domain - to host the OAuth2 endpoint,
/oauth2/token
- An app client. Our
content consumers
will be using this app client to access premium content - Resource Server with custom scopes for
read
andwrite
- We will be using the
client_credentials
OAuth flow, as we our scenario fits themachine-to-machine
communication - We will store the
app_secrets
information likeapp client id
,app client secret
andresource server identifier
andapp domain
in AWS Secrets Manager, which you can securely share with the downstream API consumer
Initiate the deployment with the following command,
cdk deploy cognito-identity-provider
-
Stack: premium-content-provider This stack creates the following resources,
- A Lambda function that generates content dynamically. Supports
read
scope forGET
requestswrite
scope forPOST
requests
- API Gateway Authorizer integrated with the
cognito-identity-provider
- API Gateway to validate the requests for
GET
&POST
methods using cognito. Upon successful validation trigger Lambda and return response to requester.
Initiate the deployment with the following command,
cdk deploy premium-content-provider
Check the
Outputs
section of the stack to access thePremiumApiUrl
- A Lambda function that generates content dynamically. Supports
-
Stack: content-consumers-stack
Our consumers will have two different flows
- Unauthorized Access
- Authorized Access
We will simulate them using AWS Lambda, triggered from API Gateway.
This stack creates the following resources,
- A Lambda function that can send different types of requests.
{API_URL}/unauthorized-read
{API_URL}/authorized-read
{API_URL}/authorized-write
- API Gateway to make it easier for us to trigger the lambda, simulating an end user
For each of the
authorized-{scope}
invocation, the lambda will make an request tocognito-identity-provider
get theaccess_token
. The request to cognito is faciliated by theapp_secrets
stored in AWS Secrets Manager by ourcognito-identity-provider
stack.Once we have the
access_token
we trigger the request topremium-content-provider
Initiate the deployment with the following command,
cdk deploy content-consumers-stack
Check the
Outputs
section of the stack to access theUnauthorizedUrl
,AuthorizedReadUrl
andAuthorizedWriteUrl
-
-
The Outputs section of the
content-consumers-stack
stack has the required information on the urls- Access the
UnauthorizedUrl
in your browser. This will trigger an request to thepremium_api
without any authorization. This request is rejected by the API Gateway and you get an unauthorized message as response.
Expected Output,
{ "message": "Unauthorized" }
- When you access the
AuthorizedReadUrl
, The following happens,- The consumer(lambda) retrieves the
app secrets
from AWS Secrets Manager - Send an
POST
request to cognito with secrets. Cognito validates theapp id
,app secret
and generates aauth token
for the given scope (read
in this case) - With this
auth token
make anGET
request topremium api
- API Gateway validates the
auth token
and on successful validation invokes thepremium content lambda
- Return the response back to requester.
- The consumer(lambda) retrieves the
Expected Output,
{ "message": "Premium Content: OAuth Scope: Read" }
- When you access the
AuthorizedWriteUrl
, The following happens,- The consumer(lambda) retrieves the
app secrets
from AWS Secrets Manager - Send an
POST
request to cognito with secrets. Cognito validates theapp id
,app secret
and generates aauth token
for the given scope (write
in this case) - With this
auth token
make anPOST
request topremium api
- API Gateway validates the
auth token
and on successful validation invokes thepremium content lambda
- Return the response back to requester.
- The consumer(lambda) retrieves the
Expected Output,
{ "message": "Premium Content: OAuth Scope: Write" }
You can check the logs in cloudwatch for more information or increase the logging level of the lambda functions by changing the environment variable from
INFO
toDEBUG
- Access the
-
If you want to destroy all the resources created by the stack, Execute the below command to delete the stack, or you can delete the stack from console as well
- Resources created during deployment
- Delete CloudWatch Lambda LogGroups
- Any other custom resources, you have created for this demo
# Delete from cdk cdk destroy cognito-identity-provider # Follow any on-screen prompts # Delete the CF Stack, If you used cloudformation to deploy the stack. aws cloudformation delete-stack \ --stack-name "MiztiikAutomationStack" \ --region "${AWS_REGION}"
This is not an exhaustive list, please carry out other necessary steps as maybe applicable to your needs.
This repository teaches developers, Solution Architects & Ops Engineers how to build complete architecture in AWS. Based on that knowledge these Udemy course #1, course #2 have been created to enhance your skills.
Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation or solutions, we greatly value feedback and contributions from our community. Start here
Buy me a coffee ☕.
-
Control access to a REST API using Amazon Cognito User Pools as authorizer
-
AWS API Gateway - using Access Token with Cognito User Pool authorizer?
Level: 300