This sample is based on https://github.com/mcguinness/node-lambda-oauth2-jwt-authorizer by Karl McGuinness. Karl's original README can be found on his github repo at https://github.com/mcguinness/node-lambda-oauth2-jwt-authorizer. A modified version, including changes made for this sample, is included below.
This project is sample implementation of an AWS Lambda custom authorizer for AWS API Gateway that works with a JWT bearer token (id_token
or access_token
) issued by an OAuth 2.0 Authorization Server. It can be used to secure access to APIs managed by AWS API Gateway.
This authorize was built as a demo tool to show how to secure an API resource on AWS API Gateway using OAuth 2.0. The authorizer is specifically designed to work with mock_api_lambda, a Lambda Function that serves as a mock API endpoint. The authorizer adds data about the policy decision (success and failure) to the context object of it's response to the API Gateway. The mock_api_lambda function, in turn, returns that contextual information in it's response.
In addition to
The authorizer uses a simple json mapping object to define which scopes are required for each API resource/HTTP method. A scope is mapped to an HTTP method/resource pair (an endpoint). This can be a 1:1 mapping, or several scopes could be required for a single method/resource as shown below.
The authorizer will loop through the scopes in the mapping json object, comparing them with the scopes present in the bearer token. When a match is found, the method/resource is added to the policy document as an explicit allow. If a scope in the scope mapping JSON is not present in the bearer token, the access policy explicitly deny access to the method/resource, and add an error message to the authorizerMessage
attribute of policy document's context indicating which scope(s) were missing. The authorizerMessage
is used to provide more informative (demo purposes only) error message from the API Gateway. Here's a sample scope->method/resource mapping, where the scope fab:read is required to access the /banks resource via GET.
const scpMapping = {
'fab:read': {
method: 'GET',
resource: '/banks'
},
'banks:read': {
method: 'GET',
resource: '/banks'
}
};
To use the messages returned in the authorizerMessage
attribute, you'll need to modify the API's Gateway Response messages. I modified Default 4XX, and the 403 responses like this:
{
"[Default 4XX] message": $context.error.messageString,
"Authorizer Message": "$context.authorizer.authorizerMessage"
}
Where $context.authorizer.authorizerMessage
is the authorizerMessage
attribute returned on the policy document context object.
Update the ISSUER
and AUDIENCE
variables in the .env
file
ISSUER=https://example.oktapreview.com/oauth2/aus8o56xh1qncrlwT0h7
AUDIENCE=https://api.example.com
It is critical that the issuer
and audience
claims for JWT bearer tokens are properly validated using best practices. You can obtain these values from your OAuth 2.0 Authorization Server configuration.
The audience
value should uniquely identify your AWS API Gateway deployment. You should assign unique audiences for each API Gateway authorizer instance so that a token intended for one gateway is not valid for another.
Update keys.json
with the JSON Web Key Set (JWKS) format for your issuer. You can usually obtain the JWKS for your issuer by fetching the jwks_uri
published in your issuer's metadata such as ${issuer}/.well-known/openid-configuration
.
The authorizer only supports RSA signature keys
Ensure that your issuer uses a pinned key for token signatures and does not automatically rotate signing keys. The authorizer currently does not support persistence of cached keys (e.g. dynamo) obtained via metadata discovery.
Run npm install
to download all of the authorizer's dependent modules. This is a prerequisite for deployment as AWS Lambda requires these files to be included in the uploaded bundle.
There are several ways to deploy this lambda to AWS. This document won't go into those details, but you will need a bundle, so:
Run npm run bundle
. This will create custom-authorizer.zip with all the source, configuration and node modules AWS Lambda needs.
You can use Postman to test the REST API
- Method: < matching the Method in API Gateway >
- URL
https://<api-id>.execute-api.<region>.amazonaws.com/<stage>/<resource>
- The base URL you can see in the Stages section of the API
- Append the Resource name to get the full URL
- Header - add an Authorization key
- Authorization : Bearer
$ curl -X POST <url> -H 'Authorization: Bearer <token>'
fetch( '<url>', { method: 'POST', headers: { Authorization : 'Bearer <token>' }}).then(response => { console.log( response );});