In security and access control, authentication and authorization are two distinct yet interconnected concepts. Authentication is the process of confirming the identity of a user or system, while authorization defines the actions that the authenticated user is permitted to perform within your system. Although API Gateway integrates directly with Cognito, it lacks built-in support for fine-grained authorization. In a previous article, we looked at implementing fine-grained authorization using a Lambda authorizer and Cognito groups. In that approach, authorization takes place on the API side: the caller sends its ID token to the API, and the Lambda authorizer generates a policy based on the user’s groups, which are embedded in the ID token by default. This time, we’ll look at a different approach – using access tokens with scopes. In this setup, the identity provider (Cognito, in our case) manages both authentication and authorization, offloading these responsibilities from the API. Customizing Cognito access tokensAs of December 2023, Cognito supports customizing access tokens [1]. Previously, you could only customize the ID tokens with the Pre-Token Generation trigger [2]. This new capability lets you customize the access tokens by adding specific scopes [3]. Here’s how: 1. Enable Advanced Security Features: Turn on this setting in the user pool. 2. Configure the Pre-Token Generation trigger: Choose “Basic features + access token customization” in the “Trigger event version”. Note: CloudFormation doesn’t support this setting and requires manual configuration. 3. Implement the pre-token generation Lambda function: Use this function to add custom scopes to the access token. Assuming you have two groups in the user pool – You want these groups to correspond to the Your pre-token generation Lambda function might look like the following, where we map the group names to an array of scopes. The Map scopes to API Gateway routesTo ensure API Gateway respects these scopes, configure your API Gateway methods with an These scopes are used with a Cognito authorizer to authorize a user request. A user request is authorized if any of the Here’s how you can configure the For a working demo, check out this repo [4]. ConsiderationsThis is an elegant solution. There is a lot to like about it. But it has one BIG downside – cost. The cost implicationEnabling Advanced Security Features significantly increases the cost of using Cognito: 1. 10x increase in cost: Advanced Security Features cost ten times more than the standard monthly active users (MAU) pricing. 2. No free tier: Unlike the standard MAU cost, which includes a generous free tier of 50,000 MAU, there is no free tier for Advanced Security Features. 3. Still have to pay the standard MAU cost: The Advanced Security Features cost is charged in addition to the standard MAU cost, although you benefit from the 50,000 MAU free tier. The cost overhead is substantial when you enable Advanced Security Features, as detailed in my last post [5]. At 50,000 MAU, you’d pay $2,500 per month, vs. $0 if you use ID tokens for authorization. At 100,000 MAU, it becomes $4525 (using access tokens) vs. $275 (ID tokens). Sadly, other vendors would charge you even more, as one of Cognito’s greatest strengths is its cost-efficiency. Are access tokens more secure than ID tokens?Proponents of this approach argue that access tokens should be used for authorization and ID tokens for authentication. While this might be technically accurate, it has little practical impact. As discussed in my last post [5], ID tokens are just as secure as access tokens. Is this approach faster than Lambda authorizers?Another argument is that this approach is more performant since it eliminates the need for Lambda authorizers and their associated cold starts. However, since you still need a Lambda function to customize the access token, the potential for cold starts remains on the critical path. ConclusionIt’s nice that this approach keeps your authorization logic within the Cognito User Pool setup. It centralizes both authentication and authorization concerns in one place. However, the cost implications are too significant. Unless you’re using Advanced Security Features already, or your application has a high value per user (e.g. a B2B enterprise application), this approach may be difficult to justify in terms of return on investment. Links[1] Cognito now support the ability to customize access tokens [2] Cognito’s Pre-Token Generation trigger [3] How to customize access tokens in Amazon Cognito user pools [4] Demo repo [5] Is it safe to use ID tokens with Cognito authorizers? Related articles
Get 10% off the AWS SA BootcampA friend of this newsletter, Raj Saha (Principal Solutions Architect at AWS), is running a bootcamp to help you land a Solutions Architect job, and has kindly offered us a 10% discount. AWS SA Bootcamp is the most direct and guided path to become a solutions architect and get a high-paying cloud job in 3 months
To learn more about the SA Bootcamp, go to www.sabootcamp.com. |
Join 12K readers and level up you AWS game with just 5 mins a week. Every Monday, I share practical tips, tutorials and best practices for building serverless architectures on AWS.
One of the most misunderstood aspects of Lambda is how throttling applies to async invocations. Or rather, how it doesn't! Every Lambda invocation has to go through its Invoke API [1], whether you're invoking the function directly or through an event source such as API Gateway or SNS. With the Invoke API, you can choose invocationType as either "RequestResponse" (i.e. synchronous) or "Event" (i.e. asynchronous). Synchronous invocations With synchronous invocations, throttling limits are...
When it comes to building event-driven architectures on AWS, EventBridge has become the de facto service for ingesting, filtering, transforming and distributing events to their desired destinations. It provides a standard envelope encapsulating each event, including metadata like the source, detail type, and timestamp. These fields are useful, but I'm gonna give you several reasons why you should wrap your event payload in its own envelope. For example, like this: 1. Clear separation between...
Years ago, I worked at a large e-commerce company that was one of the biggest food delivery services in the UK. They did something very interesting - they regularly ran load tests against production using fake orders. As a partial observer, here's what I think we can learn from this practice and how it partially caused the biggest outages they ever experienced (but not from the load test itself!). Load Testing in production As a food delivery service, they experienced large traffic spikes...