AWS Startups Blog

STACS: Implementing a Scalable Access Management Solution using Amazon Cognito and API Gateway

Guest post by Aaron Soh, Jin Ser, and Severin Gassauer-Fleissner, STACS

About STACS

Hashstacs Pte Ltd (“STACS”) is a blockchain development company and technological solutions provider for the financial world. STACS enables financial institutions to realize new revenue generating and operational efficiency use cases. The flagship product, STACS Blockchain, enables financial institutions to be at the forefront of global adoption of digital assets. STACS Blockchain empowers financial institutions to issue, trade, clear and settle digital assets, all while remaining fully compliant with the strictest regulatory standards.

Security Requirements

The Hashstacs Solutions Engineering team set out to implement an authentication and authorization module that would secure all application logins and API access in a secured, scalable and cost-effective manner. We identified the following two security requirements:

·       Security Requirement 1: The solution also needed to support integration with Enterprise Identity Providers (IDP) via SAML. [SR1]

·       Security Requirement 2: The solution needs to support fine grained access control and user entitlement management. [SR2]

·       Security Requirement 3: The developed APIs should not directly be exposed to the public [SR3]

Solution Overview

Security access management is commonly split into two concerns: Authentication (AuthN) which identifies who an entity (principal) is, and then Authorization (AuthZ) which is concerned with what level of access or permissions a principal has. The combination of the two is also sometimes referred to as “AuthX”.

Our security module consists of three parts. For one, Authentication which leverages Amazon Cognito.  We also implement Authorization making use of Amazon API Gateway Custom authorizers in concert with Amazon RDS to provide a structured relational store to record access permissions in. Finally, we have created a custom micro-service which enables admin users to manage the entitlements stored in the database.

This diagram shows the high-level architecture, and we will be diving deeper into it throughout this post. Note that the scope of this blog post is the security module, and all other components are out of scope of the following explanations. We use the annotations to reference the relevant resources throughout the remainder of the post.

Figure 1 – Security Module Architecture

Authentication using Amazon Cognito User Pools

Amazon Cognito is a service that controls user authentication, authorization and management for web and mobile applications. It also supports integration with third-party Security Assertion Markup Language (SAML), simplifying the onboarding process for users. Cognito also natively handles the time-out of the refresh token which can be set from the Cognito console.

In our solution, Authentication is based on the aforementioned third party IDP using SAML federation. We chose to use an Amazon Cognito User Pool (B) as they support this feature.

On successful authentication, Cognito issues an id token, access token, and a refresh token to the client. These two tokens are stored as a httpOnly cookie on the client browser, and every subsequent authenticated request from the client will carry the access token in the request header.

Authorization using Amazon API Gateway

The APIs are deployed to Amazon API Gateway (C), which is a fully managed scalable service that is able to handle concurrent API calls and manages traffic to and from our backend services (H). API Gateway also acts as a reverse proxy by terminating the user request and then routing it to the private APIs. We make use of API Gateway’s private integration feature to make the resources in our VPC accessible via it.

API Gateway authorization supports, and integrates, with Amazon Cognito User Pools. In its basic form the Cognito authorizer is able to look at either an id or access token and to check for the presence of certain claims or scopes.

As per the aforementioned requirement [SR2] we require fine grained access control on a per user basis, and it wasn’t sufficient to just rely on scopes or claims in the access tokens. Instead, we wanted to keep an up-to-date record of various users and their entitlements.

For more sophisticated authorization use cases, such as this, API gateway authorization supports custom authorizers backed by a Lambda function. Within this Lambda function we are able to evaluate the access request drawing on the power of high-level programming language in concert with the associated AWS SDKs. Once we extract the token from the request, this enables us to do a few things: 1) invoke the Cognito API to obtain the user ID associated with the presented access token, and then 2) use that information to look up the user’s entitlement in our Amazon Relational Database Service (RDS) database cluster, and finally 3) compare the request to the entitlement to make a decision whether to grant access or not.

Only if the request passes the access evaluation logic in our lambda function is it accepted and routed from the API gateway to the backing microservice.

Because Lambda is event-driven this approach has the benefit that we only execute, and more importantly pay for, the evaluation whenever a resource is requested by our customers.

Another advantage is the reduced undifferentiated heavy lifting for authentication and authorization we need to perform within our microservices. By virtue of Amazon API Gateway Private APIs we are able to whitelist the Private API Gateway endpoint in our microservices’ security groups. This gives us confidence that any request that reaches the microservice has come via the API gateway and therefore has been through Authentication and Authorization. Therefore, we are able to trust the incoming request rather than having to implement authentication and authorization logic in each of our microservices.

This is what the creation of the custom authorizer looks like in the AWS console:

Figure 2 – Custom Authorizer Creation

Figure 2 – Custom Authorizer Creation

Managing roles and permissions

The roles and permissions for each user are defined and stored persistently in an RDS database (E). The permissions defined in the database schema refers to the API resource that the user can access. The Lambda authorizer (D) queries this database to get a list of permissions for the user that is sending the request

To manage the permissions, we have developed a custom microservice (G). Like the other microservices (H), this microservice is a private resource, meaning that users will require an access token from Cognito to have access to it.

The permissions microservice is capable of the following tasks:

·       Fetching a list of permissions for user

·       Fetch list of roles for user

·       Create role / permission

·       Assign or remove role to user

·       Assign or remove permission to role

The following sequence diagram shows what occurs as part of the happy path

Figure 3 – Admin microservice flow

Summary

The solution presented fulfills all our initially stated goals. 3rd party IDP integration, SR1, is solved by virtue of Cognito’s support for SAML Identity providers. Fine grained access, SR2, is achieved using Amazon API Gateway powerful custom authorizer feature, and SR3, private APIs, is enabled through private integration. An upside for us here at STACS is that by implementing this leveraging using Amazon managed serverless services we save a lot of work and effort that we would otherwise need to invest in deploying and managing a SAML capable IDP or micro-service side Authentication and Authorization.

After the positive experience with serverless technologies, we are also interested in making our admin microservice AWS Lambda based to gain further efficiencies and reducing our EC2 instance footprint.

Learn More!

Tutorial: Build a REST API with API Gateway private integration

Use API Gateway Lambda authorizers

Building ADFS Federation for your Web App using Amazon Cognito User Pools

 

About the Authors

 

Severin “Sev” Gassauer-Fleissner is a Senior Solutions Architect at Amazon Web Services. He is passionate about distributed systems and understanding how things work. In his spare time, he enjoys tinkering with technology, and reading non-fiction.

 

 

 

Jin was formerly a software engineer from Oracle where he designed and built the full stack of the Analytics data warehouse for one of the Oracle Cloud Products that provided actionable Business Intelligence. He was also a solutions engineer from Transperfect, a global leader in translation solutions, where he led and delivered on-time customized tech integrations with clients’ Enterprise cloud systems across the APAC region. He also co-founded an AI mobile app startup where he contributed to the proprietary algorithms and built the final product. He was awarded an IMDA National Infocomm (SG:Digital) Overseas Scholarship and graduated with a Masters of Engineering from the University of Pennsylvania.