AWS API Gateway Endpoint with Multiple Authorisers.
API Gateway provides multiple ways for controlling and managing access to the APIs. The main responsibility of API Gateway is to authorise all API requests and block any unauthorised ones at API Gateway level before sending to backend integrations. It allows to define authorisation per method level and per path and supports the parsing and handling of any bearer token, and supports native parsing of standardised OpenID Connect (OIDC) and OAuth 2.0 JWTs. It also seamlessly integrates with identity providers and you can enable these capabilities using the following authorisers.
- JWT and Amazon Cognito User Pools authorisers
It seamlessly validates the Amazon Cognito user pools tokens, or any standards compliant OpenID Connect (OIDC) and OAuth 2.0 tokens by checking the issuer, client ID, timestamp, signature, and authorisation scopes if specified. You don’t need to write any custom code. - AWS Lambda custom authorisers
It allows to write custom business logic according to the specifications, do external lookups, generate per-user fine-grained AWS IAM policies and and cache the resulting user’s policy. - IAM-based authorisation
It allows to validate a unique canonical request signature which is generated and sent by the API client with each request. This signature contains the time of request, resource requested and action.
Although API Gateway provides different mechanism to handle authorisation at different level, it does have a limitation. API Gateway doesn’t allow to set multiple authoriser for a single API gateway endpoint.
There are many use cases where you need to use both Amazon Cognito authoriser and AWS Lambda custom authorisers on a single API Gateway endpoint. Amazon Cognito authoriser is to validate access token and AWS Lambda custom authorisers is to implement custom business logic to validate the request header values with downstream systems. Since API Gateway dose allow to set only one authoriser for a single API gateway endpoint, it is impossible to use two authorisers. Only way to overcome this limitation is to implement both access token validation and custom business logic in a single AWS Lambda custom authorisers and set it as authoriser in API Gateway endpoint. In this post, let’s see how we can implement it using Python script.
First, we need to create a function to parse and extract access token from event data. There are two types of custom authorisers: TOKEN and REQUEST. We are using REQUEST authorisers here and access token is set on the header, Authorization. The following function receives event data and extracts access token from header. Then it splits and removes “Bearer” part from the token.
Next, we need a function to validate the token. Token will have three sections: Header, payload and signature. These sections are encoded as base64url strings and are separated by dot (.) characters. First, we have to extract key ID (kid) from the header and compare it with public kid. You have to download and store the corresponding public JSON Web Key (JWK) for your user pool. It is available as part of a JSON Web Key Set (JWKS). You can use the following code snippet for downloading and storing public JWK. CognitoUserPoolId is environment variable. You need to set correct value for it.
Here is the function to validate the token. It gets corresponding public key from the store and verifies the decoded signature. Additionally, it validates whether token is expired or not.
Now we have done the standard access token validation. Next step is to add custom business logic to validate the values set in the request headers using internal validation service which is deployed in AWS fragate. The following function contains custom logics to validate header values. You can modify it as per your needs. Here, internal validation service requires AWS Cognito access token for authorisation.
As you can see, we can implement both AWS Cognito access token verification and custom validation logic in a single AWS Lambda custom authoriser and use it with AWS API Gateway endpoint. Here is the complete python script. You can modify it as per your requirement. Besides, Below I have provided Dockerfile to create container and AWS CloudFormation template to create lambda function.
Python Script.
You need to set the following environment variables with correct values.
CognitoUserPoolId
ValidationServiceClientIdValue
ValidationServiceClientSecretValue
Dockerfile
The following Python script dependencies are defined in requirements.txt file.
python-jose==3.3.0
requests==2.25.0
You can use the following bitbucket pipeline to create image and publish to Amazon ECR. Make sure you have set the environment variable with correct values.
AWS CloudFormation Template
You can deploy lambda function using this template.
This post explains the solutions to define multiple authorisers to a single AWS API Gateway endpoint and also provides sample scripts and template to create AWS Lambda custom authorisers which will do both standard AWS Cognito access token verification and custom logic validation.