X.509 certificate-based authorization for REST APIs

Adrin Mukherjee
Nerd For Tech
Published in
7 min readJul 9, 2021

Simple yet powerful API authorization scheme leveraging transport layer trust

X.509 certificates are at the core of Mutual TLS (MTLS) based authentication. Essentially a certificate represents the identity of clients/partners and is used to authenticate a trusted party. This post will attempt to describe an API authorization scheme that leverages this transport layer trust and authorizes a client/partner to access APIs deployed on Amazon API Gateway. However, this scheme does not consider any user context. For user context-related API authorization, one has to resort to strategies like access tokens which generally carry a set of scopes based on which API authorization could be processed on a per user basis.

At a high level, the solution has two parts, the first part involves a couple of steps to be taken during the partner onboarding process (preferably automated). These steps are explained in the section named ‘On-boarding a partner’. The second part (described in a subsequent section- ‘Configuration & creation of runtime components’) involves the configuration and creation of solution components to support an API request made by a trusted partner over MTLS at runtime.

Onboarding a partner

The authorization scheme requires a set of activities to be performed during partner onboarding. Once the public certificate has been shared by the partner, a set of specific fields needs to be extracted (preferably using an automated process) and a unique identifier needs to be created for the partner. Additionally, API permissions need to be mapped with this unique identifier.

Generation of certificates

Partners can choose to generate their own certificate (trusted by a CA) or a custom certificate-vending portal provided by the API provider, which can help the partners generate certificates (along with private keys) as a self-service. Self-signed certificates are also an option but not recommended for production. Finally, the generated certificate is shared with the API gateway.

Extract Certificate Details

As part of the partner onboarding process, there have to be some means to extract specific fields from the shared certificate to create a unique identifier for the trusted partner. The strategy could be as simple as extracting Issuer Common Name (CN), Subject Common Name (CN), and Certificate Serial Number from the certificate. These values could then be concatenated to create a unique identifier:

<Issuer CN>:<Subject CN>:<Serial Number>

While concatenation of the certificate fields will work, it’d be better to abstract these values by using a strong hashing algorithm (like SHA-256) to generate a hash value of this concatenated string. This opaque hash value could now in practice, uniquely identify a partner within the system.

Typically, Issuer CN and Certificate Serial Number combination is unique, since a Certificate Issuer must ensure that no two distinct certificates with the same Certificate Issuer CN contain the same serial number. However, Subject CN has been thrown to the mix, just to be sure

Authorize the partner

With the opaque unique identifier created for the partner, the next decision is around which APIs and corresponding resources/methods, the partner can access, based on business requirements. This decision is then translated into a map, where each entry maps a partner’s unique hash value to the list of resources that the partner can access (Refer: Figure-1)

Figure-1: Map of partner-specific hashes with API permissions

This map is then stored in persistent storage (like DynamoDB). Later, we will see how this map is leveraged by a Lambda authorizer to authorize the partner’s request to access APIs. A sample map entry might look like this:

"19637284c1506405a9ed6ba699a5dacc7bc2567cf355207397528189acea71d5":   
[{
arn: "arn:aws:execute-api:<region-code>:<acct-id>:<api-id>",
resource: "customer/*",
stage: "DEV",
method: "GET",
effect: "Allow"
},
{
arn: "arn:aws:execute-api:<region-code>:<acct-id>:<api-id>",
resource: "products*",
stage: "DEV",
method: "GET",
effect: "Allow"
},
...
]

The above structure attempts to map the generated hash value with a list of objects that carry the API ARN, stage, method, resource and effect (Allow or Deny). Essentially, this structure supports creation of an authorization scheme where a certain partner has access to a specific set of API resources and methods, whereas another partner has access to another set of API resources and methods. There can also be overlaps of authorized API resources across partners

Configuration & creation of runtime components

Here’s a schematic view of how the various components of the solution work in tandem and the high-level steps involved:

Figure-2: Certificate-based authorization of APIs with Amazon API Gateway
  1. Partners trying to access a set of APIs must own a trusted client certificate and establish an MTLS connection with AWS API Gateway. The API Gateway, in turn, must be configured with the custom domain name and MTLS authentication support and the partner certificate must be placed in trust store beforehand
  2. Once API Gateway receives an API request from a partner, it authenticates the partner by looking up Amazon S3 based trust store and checks if the certificate used by the partner is trusted. At this point, if the certificate is not trusted, the connection is blocked by API Gateway
  3. API gateway triggers Lambda authorizer associated with the requested API resource/method and passes the request object. This object carries the client certificate content (in PEM format)
  4. Lambda authorizer extracts details (Issuer CN, Subject CN, and Serial Number) from the certificate content in the request and uses these to look up corresponding API permissions from a persistent store (like DynamoDB). These permissions are essentially placed in the persistent store against the extracted certificate details, during the partner on-boarding process. They represent which APIs/resources/methods could be invoked by the trusted partner
  5. Lambda authorizer generates an IAM policy based on the retrieved API permissions and responds to API Gateway
  6. API Gateway subsequently uses this IAM policy to allow or deny access to the requested API resource/method by the trusted partner. If access is denied, an HTTP 401 (Unauthorized) response is sent back.
  7. If access is allowed, the API request goes through to the back-end service and the response is passed back to the caller

While this post does not talk about API key, that still becomes relevant when the ask is to monitor the usage of APIs by each partner. API key also helps in implementing rate limiting, throttling, assigning monthly quota, etc.

Configuring API Gateway custom domain name

Creation and subsequent configuration of a regional custom domain name that supports MTLS authentication for REST APIs is a significant part of the solution and the same has been discussed in the following post:

Coding the Lambda authorizer

Finally, a ‘request’ type Lambda authorizer has to be created. This authorizer is meant to perform the following:

  • Check if the request has an associated client certificate. The PEM contents of the certificate is available under ‘event/requestContext/identity/clientCert/clientCertPem’
  • Parse the contents of the incoming X.509 certificate and extract the following fields: Issuer Common Name (Issuer CN), Subject Common Name (Subject CN), and Certificate Serial Number
  • Use the concatenation technique followed in the ‘Extract Certificate Details’ section to create a unique partner identifier: <Issuer CN>:<Subject CN>:<Certificate Serial Number>
  • Use the same hashing algorithm as used in the ‘Extract Certificate Details’ section (SHA-256) to generate a hash value of partner identifier
  • Look up this hash value in a persistent store (described under the “Authorize the Partner’ section) that contains the mapping of these generated hash values for each trusted partner with corresponding authorized APIs and resources
  • Generate an IAM policy based on the retrieved list of authorized APIs and corresponding resources/methods and return this to API Gateway. API Gateway reads this IAM policy and decides whether or not to allow access to the requested API resource

Check the following GitHub repository for a sample implementation of an X.509 certificate-based Lambda authorizer:

https://github.com/adrin-mukherjee/x509_lambda_authorizer

The sample authorizer loads a file from Amazon S3 that contains all API permission mappings in JSON format to keep things simple. However, in practice, persistent storage would be an ideal choice to store these dynamic mappings as part of the partner onboarding process.

The Lambda function will have to be deployed with appropriate execution role that allows it to access a persistent store like S3 or DynamoDB

Finally, this Lambda authorizer will have to be associated with methods of relevant APIs which need to be protected.

A note on certificate rotation

When a partner’s certificate gets rotated, three things need to happen:

  • Extraction of details from new certificate and subsequent generation of a hash value
  • API gateway trust-store update with the new certificate
  • Lastly, hash value update in persistent store against API permissions for the partner.

Concluding remarks

The API authorization scheme described in this post deserves deliberation around the caveats before adoption. Such a strategy may vary widely based on the application ecosystem, security requirements, compliance, and other factors. This scheme leverages transport layer properties in the application layer, which may not be the best choice in certain scenarios. Moreover, in terms of granularity of authorization, this strategy does not take into account individual user context. However, it still provides a relatively easy option to implement a secure API authorization solution.

NB: This post is partly the outcome of some hearty and healthy debates with my friend Arijit Mazumdar

--

--

Adrin Mukherjee
Nerd For Tech

Cosmic inhabitant from a pale blue dot in the Milky Way galaxy | Arik's father | Coldplay & Imagine Dragons fan | Solution Architect | Author | Shutterbug