We’re design-thinking product development experts, enabling entrepreneurs to convert ideas...
Here’s how we used AWS Cognito’s authorizer to enable users to have developer API access restricted to their user data using Client Id/Secret.
For a SaaS product, we needed to allow users to have API access to our product’s services. This enabled users to build custom capabilities by accessing and leveraging our APIs via an authenticated, authorized, and controlled access mechanism.
This requirement is not uncommon. Many services, such as Spotify, or Trello, or Digital Ocean (among many others) allow API-based access for developers to build custom services.
Our product needed to enable the following capability:
To understand these requirements in technical terms, we first need to understand our tech stack. We were using the following services:
Cognito provides a User Pool to manage users. Users logging in via a UI would be authenticated by Cognito and all requests to the API would now have a bearer token. The bearer token contains the Cognito username or the user’s email. Our code thereby authorizes the call to operate only within that user scope.
The AWS API gateway allows access to authenticated APIs via the use of app client id/secret. However, these id/secret pairs only determine whether a call to the API passes through to the code or results in an Unauthorized or Forbidden HTTP error. Cognito does not enable any way to link an app to a Cognito user. Hence, no user-level information is associated with successful calls using the client id/secret. Thus, any additional authorization such as access to a specific user scope by our code is directly not possible when using these API client id/secret.
So, what we needed to do is to programmatically allow the creation, update, delete, and query of a two-way mapping of users and client id. Given a client id, we need to determine which user this client id authorizes. Similarly, given a Cognito Username, we need to create, obtain, reset the client id/secret associated with that user.
Usually, such a use-case would require writing a custom Lambda authorizer. But can also be done using AWS Cognito Authorizer as outlined below.
A mapping database
We start by creating a database mapping a three-way database mapping in our codebase. Conceptually it is a simple table (let’s call it ID_Mapping_Table) with the following schema and indexes on each of the fields
Internal User_id Cognito Username Cognito Client_Id
In this table,
When users are added/provisioned in our system, we ensure that their Internal User_id and their Cognito Username are stored in this table.
To support app client id/secret, we provide the following API endpoints:
We have a Python decorator that is associated with all authenticated calls. The decorator performs the following tasks:
With this approach, we have enabled user-based access control via direct authenticated use of app client ids and secrets.
Also published on: https://www.ignitesol.com/aws-cognito-api-client-key/