You may think that once you configure Cognito you are done… but that’s not always the case. As the business requirements change, there is a chance that your configuration on Cognito may also change. Therefore some pieces of your app will need to be changed, as well.
We had a password validation on the frontend. As password validation is something that does not change at all (or at least not that frequently) it was hardcoded on the frontends and mobile apps.
One day, something unexpected happened… We received a requirement to expand the size of the password field from 6 chars to 8 chars. Sounds easy right? Well, it might be easy if you need to change it on one monolithic app only, but again, it’s still a hassle.
In our case, we had a serverless web app, a legacy monolithic web app and 2 mobile apps integrated with Cognito over SDK. To make all of these changes we needed a considerable amount of time. Also, different teams or even suppliers were responsible for the modifications. That required sync meetings, clarifications, lots of emails and discussions for every simple change. And finally, some more sync meetings, clarifications and emails to actually deploy the change.
It is what it is, but at least, we learned the lesson the hard way. Next, we needed to find a way to make the password configuration available for the client apps.
As I mentioned before, our architecture is based on a serverless model by utilizing managed services like Cognito, DynamoDB & and lambda functions. However, we do have a considerable amount of functionalities that remain on legacy applications and are integrated with the above mentioned serverless ones.
With a serverless approach, we have achieved to sustain unpredictable spikes in traffic without any sweat and take most of the burden off from the legacy applications. You can check below the simplified version of our architecture & infrastructure.
The mission to make password policy available to clients
As you are well aware, client applications usually access Cognito over App client id and App client secret. Cognito SDK over the above-mentioned credentials does not provide access to full SDK methods (which does make sense from a security perspective). The first idea was to get the password policy configuration over the SDK and use it from the client apps. That would make the password policy available to the clients beforehand. So by using a password policy, we eliminate the hardcoded password length validation.
The user pool configuration is available over describeUserPoolClient() for client apps, however, it does not provide password policy description.
Therefore we need to use describeUserPool(). Unfortunately, it is only available if it’s called an IAM user or any AWS (with a role that has a policy which allows calling this action).
Or, you can find more info on Cognito API reference: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_DescribeUserPool.html
As Cognito does not offer anything that will help solve our problem, we need to solve it our way.
So, we decided to create an API that will return just password policy configuration to the client apps. Furthermore, this API should be publicly available and therefore we need to have on mind security when building it.
We have analysed the idea from a security perspective and by default, proxying response from describeUserPool() would not be a smart idea as this function is publicly available. Quite few configuration details would be shared publicly, that (we think) would potentially jeopardize our security. Having that in mind, building an API that will return only password policy does not seem like a security problem.
A generic architectural approach to serverless REST APIs would be API Gateway to serve clients and invoking lambdas to execute the logic/transformation. That is usually the way to go when building serverless REST APIs. Our lambda would be responsible to call describeUserPool() function and reduce returned JSON to password policy only.
However, for this job, I didn’t like the limitation on the number of concurrent lambdas (and yes I know you can ask for an increase, but still…). Furthermore, no lambdas, no cold starts 🙂 So slicker approach would be to get rid of the lambda that serves only for transforming the request and do the transformation on API Gateway itself using VTL. VTL (Velocity Template Language) servers for manipulating request and response on API Gateway itself. This way we would create requests to Cognito API (instead of using Cognito SDK over Lambda) and modify the response of describeUserPool action so that it contains only password policy.
So, the idea is established and now let’s do it.
As all our infrastructure is handled in cloud formation or by using SAM (AWS Serverless Application Mode) templates, the natural approach would be to use it again. You can find more info about SAM on https://aws.amazon.com/serverless/sam.
To be able to understand the following code examples, you need a basic knowledge of AWS SAM, Swagger, API Gateway, and Cognito.
When you are defining REST API in SAM, you are defining paths and endpoints, as you do in Swagger. So we have defined resources as AWS::Serverless::Api. We have defined endpoint cognito/password/policy and method GET.
Now, to configure API Gateway, we need to configure x-amazon-apigateway-integration.
To integrate API Gateway with Cognito we need to know exactly which action we would like to call. As mentioned before we need to call DescribeUserPool Action over Cognito API. Therefore, we will need to configure API Integration Request like explained below.
First we need to set the uri, by adding the following to your SAM template.
Now, as we have set the uri, we need to set the request JSON Body in the requestTemplates object.
Have in mind that DescribeUserPool will return lots of information that you do not want to share over API. So you will need to present only what is necessary of that whole JSON returned by DescribeUserPool REST API action. Check it out in responseTemple below.
responseParameters & responseTemplate
Finally we need to give permission to API Gateway to consume Cognito API. So to x-amazon-apigateway-integration we need to add below credentials in other words IAM Role.
credentials: !GetAtt CognitoExecutionRole.Arn
As that role is not created yet, we need to create it as well. Good practice for creating a role is to give least needed permission. So, we will add only what is necessary and that is action cognito-idp:DescribeUserPool and only for the user pool we need password policy for. Role is shown below.
And you are done! You have your PasswordPolicy available over API. Full code is available in the linked repository below.
This article aimed to show you how to share password policy to the client apps and let the client apps use that info for validation before submitting to Cognito SDKs or API. Apart from that, it showed you basic integration process between API gateway and other services using just API Gateway and VTL, which can potentially remove the limitation on the number of concurrent lambdas and latency with a cold start. But have on mind that this solution is not infinitely Scalable. You have a limit on the number of request per second on API Gateway and Cognito APIs. So for prod use, you may need to use Cloudfront or some kind of caching in front of the API.