0039: Centralized Authentication Service using Amazon Cognito
STATUS
Accepted
CONTEXT
Authentication across Adgem services currently functions as separate entities, with each project managing its own users, access tokens, and client credentials. While this works for smaller architectures, maintainability decreases significantly as Adgem grows and new interconnected projects emerge.
This decentralized approach leads to growing complexity in token management. For example, the Targeted API (TA) currently retrieves offers via the Offer API (OA). This means the TA must store and manage its own access token as well as the OA token. If a new service is introduced, the number of tokens to manage increases, escalating complexity.
A diagram showing the current interaction between Adgem APIs is relevant here:
Since the introduction of further products will heavily increase authentication complexity, moving toward centralizing authentication is a necessary and reasonable step. The goal is to implement a centralized authentication service for AG products to reduce maintenance issues and complexity.
Considered Options
1. Continue Current Decentralized Authentication
Maintain the existing setup where each service handles its own authentication and stores credentials for every service it calls.
| Pros | Cons |
|---|---|
| Minimal immediate cost or refactoring required. | Complexity increases linearly with growth. |
| Teams retain full control over their service's authentication logic. | High maintenance cost due to token sprawl (Targeted API needing Offer API token, etc.). |
| Significantly increases future technical debt. |
2. Build an Internal Centralized Authentication Service
Develop and maintain a custom authentication service from scratch (e.g., using a framework like Laravel or dedicated auth library).
| Pros | Cons |
|---|---|
| Absolute control over all features, logic, and integration points. | High upfront development cost. |
| Significant, long-term maintenance and operations burden. | |
| Critical responsibility for user security, compliance, and handling security best practices (JWT issuance, key rotation, MFA). |
3. Implement Amazon Cognito as the Central Authentication Service (Preferred)
Utilize Amazon Cognito, an AWS-managed service, to handle token issuance (JWT), authorization, and potentially user management in the future.
What is Amazon Cognito?
Amazon Cognito is an AWS service that handles user authentication, authorization, and user management for web and mobile applications.
In simpler terms, it’s like a ready-made login system that doesn’t require building from scratch.
It provides:
-
User Sign-Up and Sign-In: Allows to create user pools with features like username/password login, password resets, and multi-factor authentication (MFA).
-
Social & Enterprise Identity Integration: Users can log in via Google, Facebook, Apple, Amazon, or enterprise identity providers using SAML and OIDC.
-
Secure Access Control: Issues JSON Web Tokens (JWT) to control access to APIs or AWS resources.
-
User Directory & Profile Management: Stores user attributes and preferences.
It’s commonly used together with API Gateway or custom backends to secure APIs and protect AWS resources.
Cognito Authentication Flow Options
Client Credentials Flow (Preferred option)
The Client Credentials flow is the simplest of the Amazon Cognito flows. It’s used when systems or services communicate without user interaction. The requesting system uses the client ID and client secret to obtain an access token. Since both systems operate without user involvement, no consent step is necessary.
Diagram showing Client Credentials flow
This flow authenticates through the following steps:
- Call the OAuth 2.0 token endpoint at
/oauth2/token. It will issue a JSON web token (JWT).
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token
Content-Type='application/x-www-form-urlencoded'&
Authorization=Basic ZGpjOTh1M2ppZWRtaTI4M2V1OTI4OmFiY2RlZjAxMjM0NTY3ODkw
grant_type=client_credentials&
client_id=1example23456789&
scope=resourceServerIdentifier1%2Fscope1%20resourceServerIdentifier2%2Fscope2&
&aws_client_metadata=%7B%22onBehalfOfToken%22%3A%22eyJra789ghiEXAMPLE%22,%20%22ClientIpAddress%22%3A%22192.0.2.252%22%7D
- This will return an access_token, which can be used to authorize further requests.
- There are 2 different (they’re not exclusive between them) ways to handle authorization once a user has been issued a token:
- Using an API Gateway Cognito Authorizer | Configure cross-account Amazon Cognito authorizer for a REST API using the API Gateway console - Amazon API Gateway
- Using the aws-jwt-verify library, there are several ways to implement this package; however, one option could be creating a lambda function, which can be used for authorizing requests.
Authorization Code flow
The Authorization Code flow is designed for web-based authentication. In this flow, the backend manages token exchange and storage.
This flow relies on redirection. The client interacts with a web browser or a similar client, gets redirected to an authentication server, and authenticates there. Upon successful authentication, the client is redirected back to the server.
Diagram showing Authorization Code flow
- Call the OAuth 2.0 authorize endpoint at /oauth2/authorize. It will return a redirection response with an authorization code that can be used for requesting a token.
GET https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/authorize?
response_type=code&
client_id=1example23456789&
redirect_uri=https://www.example.com&
state=abcdefg&
scope=openid+profile+aws.cognito.signin.user.admin
-
This will return a code,
code=a1b2c3d4-5678-90ab-cdef-EXAMPLE11111, which can be used to request anaccess_token. -
Call the OAuth 2.0 token endpoint at /oauth2/token. It will issue a JSON web token (JWT)
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token
Content-Type='application/x-www-form-urlencoded'&
Authorization=Basic ZGpjOTh1M2ppZWRtaTI4M2V1OTI4OmFiY2RlZjAxMjM0NTY3ODkw
grant_type=authorization_code&
client_id=1example23456789&
code=AUTHORIZATION_CODE&
redirect_uri=com.myclientapp://myclient/redirect
- This will return a few different tokens
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "eyJra1example",
"id_token": "eyJra2example", establish who you are
"refresh_token": "eyJj3example", grants permission to recreate access token
"token_type": "Bearer",
"expires_in": 3600
}
- Finally, the access_token should be used in order to authenticate requests.
Choosing the Right Flow
We must first choose the appropriate OAuth 2.0 flow for our primary goal: securing server-to-server (machine-to-machine) communication. The table below helps determine the best flow based on the application's nature:
| Do you need machine-to-machine authentication? | Is your app a web-based application where the frontend is rendered on the server? | Is your app a single-page application (SPA) or mobile-based frontend application? | Recommended Amazon Cognito flow |
|---|---|---|---|
| Yes | No | No | Client Credentials flow |
| No | Yes | No | Authorization Code flow |
| No | No | Yes | Authorization Code flow with PKCE |
| Pros | Cons |
|---|---|
| Managed Security: Eliminates the need to build and maintain a custom login/token system. | Requires initial configuration effort (User Pools, App Clients, etc.). |
| Scalability: Handles features like JWTs, MFA, and social/enterprise federation out-of-the-box. | Introduces a tight dependency on AWS and its limitations (e.g., non-customizable claims/scopes). |
| Native Integration: Integrates seamlessly with other AWS services (API Gateway Authorizers). | Adherence to AWS-defined limits and workflows. |
Extra features to consider
Resource Servers and Custom Scopes
A resource server in AWS Cognito hosts protected resources, such as APIs. When you register a resource server in Cognito, you define it within a user pool, grouping APIs or microservices under a single logical entity. Basically, a resource server is a remote server that authorizes access using OAuth 2.0 scopes in an access token.
Cognito can create OAuth 2.0 Resource servers and associate Custom scopes. These custom scopes in an access token authorize specific actions in an API. That means any app client in the user pool can issue a token using custom scopes in the resource servers.
Using the diagram as an example, if the client has been authenticated in order to retrieve offers from the Offer API, we could define a resource server called Offer API containing a custom scope called “read.” That means when verifying the access_token the API will check if it has the proper permissions to execute the requested task. This concept is similar to abilities in Laravel.
Pre-token generation Lambda trigger & Custom Claims
Access tokens could be customized through pre-token generation using a Lambda trigger for it, In simpler words, the Lambda will act as middleware between the token generation and the initial request, ensuring the token contains customized claims or suppressing undesired claims from the token. This topic is long enough to have its own document, because there are several options to customize token payload. For further information, take a look at Pre token generation Lambda trigger - Amazon Cognito.
DECISION
- Implement Amazon Cognito as the Central Authentication Service for Adgem products, starting with the Client Credentials flow for server-to-server communication.
- Transition to using Amazon Cognito for user authentication and authorization across all Adgem products.
- The initial implementation will focus on securing API-to-API communication between Targeted API and Offer API.
- Resource Servers and Custom Scopes will be defined for each API to manage access control effectively.
- Offer API Resource Server
- Scopes: read, write, update, delete
- Targeted API Resource Server
- Scopes: read
- We will deploy Cognito in the us-east-2 (Ohio) region to align with our existing AWS infrastructure.
- Token expiration policy can be set between 5 minutes to 1 day, we'll need to define the appropriate expiration times for each use case, and check for any potential impacts on user experience, further exploration may be required to optimize this aspect.
Future Considerations
- Define a policy for token expiration and refresh strategies.
- Determine how many tokens would be issued per service based on usage patterns and requirements.
- Analyze cost implications as usage scales based on the chosen token strategy.
CONSEQUENCES
- Reduced Maintenance Burden: By leveraging a managed service, we reduce the overhead of maintaining custom authentication logic.
- Improved Security Posture: Amazon Cognito provides built-in security features, reducing the risk of vulnerabilities in custom implementations.
- Scalability: As Adgem grows, Cognito can handle increased authentication demands without significant changes to our architecture.
- AWS Dependency: Relying on Amazon Cognito introduces a dependency on AWS services, which may have implications for cost and vendor lock-in.
- Initial Learning Curve: Teams will need to familiarize themselves with Amazon Cognito and its features, which may require training and documentation updates.
- Integration Effort: Existing services will need to be refactored to integrate with Cognito, which may require significant development effort.
Pricing
Amazon Cognito offers three pricing tiers for user pools: Lite, Essentials, and Plus.
Lite and Essentials Tiers
The Lite tier provides basic authentication capabilities including social identity integration and password-based authentication, designed for cost-conscious use cases. The Essentials tier builds upon Lite by offering more comprehensive authentication features, enabling secure and customizable sign-up and sign-in experiences for applications, with both tiers being priced based on the actual usage.
Amazon Cognito offers a perpetual free tier for both Essentials and Lite pricing tiers that doesn't expire after the standard 12-month AWS Free Tier period, making it available to all AWS customers indefinitely. This free tier includes 10,000 monthly active users (MAUs) per month for users who sign in directly through Amazon Cognito or via social identity providers when using either the Lite or Essentials tier configurations.
Lite Pricing Table
Amazon Cognito Lite pricing is based on monthly active users (MAUs) in the user pool with the Lite pricing tier, where a user counts as a Lite MAU if they're active at least once in a month with the Lite tier configuration and were never active under Essentials or Plus tiers.
| Pricing Tier (MAUs) | Price per MAU |
|---|---|
| First 10,000 (Free-tier) | $0.00 |
| 10,001-100,000 | $0.0055 |
| 100,001 - 1,000,000 | $0.0046 |
| 1,000,001 - 10,000,000 | $0.00325 |
| Greater than 10,000,000 | $0.0025 |
Amazon Cognito's advanced security features (ASF) incur additional costs per monthly active user beyond the base pricing, even when used in audit mode. These features include compromised credentials detection, adaptive authentication, advanced security metrics, and access token customization, with pricing remaining consistent with rates before November 22, 2024. It's important to note that these advanced security features are not available in the AWS GovCloud (US-West) region.
| Pricing Tier (MAUs) | Price per MAU |
|---|---|
| First 50,000 | $0.050 |
| Next 50,000 | $0.035 |
| Next 900,000 | $0.020 |
| Next 9,000,000 | $0.015 |
| Greater than 10,000,000 | $0.010 |
Essentials Pricing Table
Amazon Cognito Essentials pricing is based on Monthly Active Users (MAUs) in the user pool, with a user counted as an Essentials MAU if they're active at least once during a month with the Essentials tier configured and were never active when the pool was configured as Plus. Essentials is the default tier for new user pools, and customers can freely switch between Lite, Essentials, or Plus tiers at any time to match their application requirements. Additionally, there's a special eligibility for customers to upgrade existing user pools without Advanced Security Features to the Essentials tier while maintaining their current pricing until November 30, 2025.
| MAUs | Price per MAU |
|---|---|
| First 10,000 (Free-tier) | $0.00 |
| Greater than 10,000 | $0.015 |
Plus Tier
The Plus tier is an enhanced security offering that builds upon the Essentials tier by providing advanced threat protection capabilities. It specifically includes risk-based adaptive authentication, detection of compromised credentials, and the ability to export authentication event logs for security analysis. This tier is designed for customers who have higher security requirements and need more sophisticated tools to protect against suspicious login attempts and potential threats.
Plus Pricing
Amazon Cognito Plus pricing is based on Monthly Active Users (MAUs) in the user pool with the Plus pricing tier, where a user is counted as a Plus MAU if they're active at least once during the month. The pricing structure differentiates between users who sign in directly with their credentials (including via social identity providers) and those who sign in through enterprise directories using SAML federation. Notably, customers using Advanced Security Features (ASF) can achieve significant cost savings—up to 60% on their monthly bill—by configuring their user pool with the Plus pricing tier.
Amazon Cognito Plus costs $0.02 per MAU
Cognito Add-ons
Additionally, Cognito supports machine-to-machine (M2M) authorization and higher requests per second (RPS) as add-ons, each priced based on usage.
Tier comparison
| Features | Lite | Essentials | Plus |
|---|---|---|---|
| Basic capabilities for password-based authentication targeted for value-oriented use-cases. Additional capabilities requires customization | Core set of capabilities that enable seamless authentication for end-users such as passwordless login | Enhanced set of capabilities for applications with elevated security needs | |
| 40 million users or more | Yes | Yes | Yes |
| Sign-in with social, SAML, or OIDC providers | Yes | Yes | Yes |
| Sign-in with username and password | Yes | Yes | Yes |
| MFA with authenticator apps and SMS one-time codes | Yes | Yes | Yes |
| Custom runtime action with Lambda triggers | Yes | Yes | Yes |
| Customize managed login page with CSS | Yes | Yes | Yes |
| 99.9% service level agreement | Yes | Yes | Yes |
| Customize managed login page with visual editor | Yes | Yes | |
| MFA with email one-time codes | Yes | Yes | |
| Passwordless sign-in with one-time codes | Yes | Yes | |
| Passkeys sign-in with biometrics and hardware keys | Yes | Yes | |
| Prevent reuse of previous passwords | Yes | Yes | |
| Customize access token scopes and claims at runtime | Yes | Yes | |
| Support for refresh token rotation | Yes | Yes | |
| Protect against malicious sign-in attempts | Yes | ||
| Log and analyze threat profiles and user activity | Yes | ||
| Risk-based adaptive authentication | Yes | ||
| Compromised credentials detection to protect against unsafe passwords | Yes | ||
| Export threat profiles and user activity | Yes | ||
| Machine-to-machine authorization | Add-on | Add-on | Add-on |
| Higher API RPS quota | Add-on | Add-on | Add-on |
Machine-to-Machine Authorization Add-on
Amazon Cognito provides machine-to-machine (M2M) authorization through OAuth 2.0 client credentials flow, allowing to create app clients that represent the services or APIs. With this feature, access token can be issued in exchange for client credentials, configure token validity periods, and monitor token usage per app client. The pricing model is straightforward - it's charged monthly based on successful token responses, with no extra costs for the number of registered app clients in the account (though further contact with the account team is required if more than 2,500 app clients are needed).
| Tier | Number of token requests per month | Price |
|---|---|---|
| Tier 1 | 1 - 250,000 | $2.250 per 1000 token requests |
| Tier 2 | 250,001 - 5,000,000 | $1.500 per 1000 token requests |
| Tier 3 | 5,000,001 - And above | $1.125 per 1000 token requests |
Risks
- Service Limits: Amazon Cognito has service limits that may impact scalability. We will need to monitor usage and request limit increases as necessary.
- Feature Limitations: Cognito may not support all desired features or customizations, potentially requiring workarounds or additional services.
- Cost Management: While Cognito is cost-effective for many use cases, we will need to monitor costs as usage scales to ensure it remains within budget.
NOTES
References
- Adaptive authentication
- Advanced security metrics
- API Gateway Cognito Authorizer
- Amazon Cognito
- Amazon API Gateway
- aws-jwt-verify library
- Choose an Amazon Cognito authentication flow for enterprise applications - Amazon Cognito
- Cognito Pricing
- Configure cross-account Amazon Cognito authorizer for a REST API using the API Gateway console - Amazon API Gateway
- Compromised credentials detection
- Excluded claims and scopes
- /oauth2/token
- Pre token generation Lambda trigger - Amazon Cognito
- PR #82: docs: Starts drafting cognito implementation ADR :memo:
- PR #127: docs: backfill PR reference links for existing ADRs
Original Author
Daniel Ballesteros