Authentication and Authorization (Auth) using AWS Cognito

Priyam Sarma
YipitData Engineering
5 min readJul 29, 2021

--

Enabling sector-based auth using Google IdP and Cognito

Photo by FLY:D on Unsplash

Background

At YipitData, we provide data products covering companies in a multitude of industries. Accordingly, we have aligned our Product organization to have teams that cover specific sectors within those industries. For us, a sector provides a logical grouping of companies. As an example, the Food sector covers companies ranging from delivery services (e.g. GrubHub) to restaurants (e.g. Chipotle). An analyst belongs to a sector team and owns different data products within that sector. Analysts manage their data products using an internal tool, Orange, which allows them to categorize a data product as belonging to a given sector.

Requirements for Auth

For Orange, we recently wanted to add authentication and authorization (auth). Before exploring solutions, we came up with the following requirements to guide our discovery process for potential solutions:

  • We wanted only certain organizations (i.e. Product and Engineering) to use this system, but not other departments, such as Revenue
  • We wanted authentication to use our current Identity Provider (IdP), which is Google. We wanted to avoid having to set up and manage new username and passwords.
  • We needed sector-based authorization. We wanted to limit the data products that are visible based on what sector(s) an analyst belonged to. This would limit the blast radius of any undesired actions, like accidentally archiving a data product.
  • We also wanted authorization management to be the responsibility of the Analysts. There needs to be a place where they can manage who belongs to what sectors, and who has access to which products.

Constraints

In addition to the above criteria, we also had some technical constraints when exploring potential solutions:

  • Google will continue to be the Identity Provider (IdP) for YipitData
  • React is the preferred organizational Frontend Framework
  • Django is the preferred organizational Backend Framework

Solutions for Auth

In evaluating auth solutions, we looked at Auth0, Okta, and AWS Cognito. Okta and Auth0 are the market leaders when it comes to auth; they meet all of the above requirements and work with our constraints. However, we have the vast majority of our infrastructure in AWS and it made sense to take a closer look at Cognito.

We ultimately went with Cognito for the following reasons:

  1. Cognito integrates better with AWS. As an example, a group in Cognito can be assigned to a role, authorizing users in that group to access AWS resources, with temporary access keys.
  2. It would be a significant amount of work to implement another solution like Okta or Auth0. Although it could be argued that this ignores the long-term benefits of Okta or Auth0, those requirements were less concrete at that point in time. We did not have an organizational mandate for auth, simply for this one internal application.
  3. Cognito currently does not cost us anything. There could be potential costs due to implementing Okta or Auth0, and going down this path seemed unnecessary without the proper requirements to push us in that direction.
  4. Cognito provides the same authentication and authorization functionalities that we would get from Auth0 or Okta.
  • Cognito can easily create users, groups and their associations.
  • Cognito can use Google as our IdP and create federated users in Cognito using an Out of Box (OOB) integration.
  • Cognito is good at keeping track of tokens and refreshing them behind the scenes for us. We configure, we do not manage.
  • Cognito has Lambda triggers that allow for more sophisticated auth flows.

Implementation

The following diagram highlights our auth implementation using Cognito. Details for this implementation can be found in the AWS docs linked here. Orange was built using a React Front End (FE) and Django API Backend (BE), with Cognito serving as the middleware that facilitates authentication and authorization.

  1. React FE makes a request to Amazon Cognito for authenticating Google login using the aws-amplify library
  2. Amazon Cognito User Pool makes a request to Google IdP OAuth 2.0 application for authentication
  3. After successful authentication, Google returns jwt token(s) to the configured cognito callback URL
  4. Amazon Cognito creates its own jwt accessToken, refreshToken, and idToken based on the jwt token(s) provided by Google and sends back to the React FE
  5. React FE wraps and submits all subsequent requests to the Django BE with the idToken.jwtToken in the Authorization header as a Bearer token
  6. Django backend validates that the idToken.jwtToken using the django-cognito-jwt library and its authentication middleware

For end users of Orange, they are able to authenticate (log in) using their Google credentials. Once authentication is successful, Cognito passes personal information from Google such as name, email, etc. in the idToken. This is enough information to create a user in the Django backend if one does not exist. The default state for this user is to be inactive, and must be activated manually in the Django admin console of Orange. This administration is performed by assigned admins from the Product organization.

To meet our sector-based authorization criteria, we leveraged Cognito Lambda triggers. We did consider using Cognito groups, and having groups in Cognito represent sectors. However, we decided that the AWS console was not the best place for administering user-to-sector mappings for non-engineers. Therefore, we created a separate Django API, the Authorization API, where product, user and sector mapping information is stored. We did this in a separate API so that it is a single-purpose microservice and can be leveraged by more than one internal application for authorization.

Next, we created a custom Pre Token Generation Lambda Trigger that augments the idToken that Cognito generates with a user’s sectors. This enhanced idToken is delivered to our React FE post-authentication. The idToken is submitted to the backend API and is deciphered to pull out the user to sectors mapping. As a result, the Orange application has the necessary sector metadata about a user to filter what data products should be visible to that end user.

Conclusion

In conclusion, we met all of the requirements that we set out to achieve for an authentication and authorization solution using Cognito. This solution also happened to be the cheapest and fastest to implement. The flexibility of the Cognito Lambda Triggers leaves room to expand this solution and does not make it a one-way door. Most importantly, we have a tailored solution that activates our customers, i.e. the Product organization, to administer their own authorization, without Engineering as a bottleneck.

--

--