Amazon Identity Center with Amazon Q Business: Simplified Authentication Architecture
Amazon Identity Center with Amazon Q Business: Simplified Authentication Architecture
As organizations adopt Amazon Q Business, understanding the authentication architecture is crucial for secure implementation. This guide presents a simplified view of authentication patterns combining Amazon Identity Center, Cognito, and custom authorizers.
Simplified Architecture: Two Main Approaches
The authentication system can be implemented using two distinct patterns depending on your authorization needs:
Approach 1: Cognito-Based Authorization
┌─────────────────────────────────────────────────────────────────────────────┐
│ IDENTITY LAYER │
│ (Where Users Authenticate) │
├─────────────────┬─────────────────┬─────────────────────────────────────────┤
│ External IdP │ Amazon Cognito │ IAM Identity Center │
│ (Okta/ADFS) │ User Pools │ (SSO) │
└─────────────────┴─────────┬───────┴─────────────────────────────────────────┘
│ (JWT Tokens with Groups/Roles)
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ AUTHORIZATION LAYER │
│ (Built-in Cognito Authorization) │
├─────────────────────────────┬───────────────────────────────────────────────┤
│ API Gateway │ Amazon Verified Permissions │
│ (Cognito Authorizer) │ (Policy Engine) │
└─────────────┬───────────────┴───────────────────────────────────────────────┘
│
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ (User Experience & Data) │
├─────────────────┬─────────────────┬─────────────────────────────────────────┤
│ Amazon Q Business│ Custom Plugins │ Data Sources │
│ Core Platform │ External APIs │ Enterprise Systems │
└─────────────────┴─────────────────┴─────────────────────────────────────────┘
Flow: Identity → Cognito JWT → API Gateway (Built-in Auth) → Applications
Approach 2: Custom Lambda Authorizer
┌─────────────────────────────────────────────────────────────────────────────┐
│ IDENTITY LAYER │
│ (Where Users Authenticate) │
├─────────────────┬─────────────────┬─────────────────────────────────────────┤
│ External IdP │ Amazon Cognito │ IAM Identity Center │
│ (Okta/ADFS) │ User Pools │ (SSO) │
└─────────┬───────┴─────────┬───────┴─────────┬───────────────────────────────┘
│ │ │ (Various Token Types)
└─────────────────┼─────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ AUTHORIZATION LAYER │
│ (Custom Business Logic) │
├──────────────┬──────────────────┬─────────────────────────────────────────────┤
│ API Gateway │ Lambda Authorizer│ Amazon Verified Permissions │
│ (Entry) │ (Custom Logic) │ (Policy Engine) │
└──────┬───────┴────────┬─────────┴─────────────────────┬───────────────────────┘
│ │ │
└────────────────┼───────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ (User Experience & Data) │
├─────────────────┬─────────────────┬─────────────────────────────────────────┤
│ Amazon Q Business│ Custom Plugins │ Data Sources │
│ Core Platform │ External APIs │ Enterprise Systems │
└─────────────────┴─────────────────┴─────────────────────────────────────────┘
Flow: Identity → Any Token → API Gateway → Lambda Authorizer → Applications
Understanding Amazon Cognito: The Authentication Bridge
Amazon Cognito is a fully managed identity service that provides user sign-up, sign-in, and access control capabilities for web and mobile applications. In the context of Amazon Q Business, Cognito serves as a crucial bridge between external identity providers and your custom plugins, enabling secure authentication and authorization for backend systems.
What is Amazon Cognito?
Amazon Cognito consists of two main components that work together to provide comprehensive identity management:
1. User Pools: A user directory service that handles user registration, authentication, and account recovery 2. Identity Pools: A service that provides temporary AWS credentials to users for accessing AWS services
How Amazon Cognito Works
Core Authentication Flow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User/Client │ │ Cognito User │ │ Your Backend │
│ Application │ │ Pool │ │ Application │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ 1. Sign In Request │ │
├─────────────────────►│ │
│ │ │
│ 2. JWT Tokens │ │
│◄─────────────────────┤ │
│ │ │
│ 3. API Request + JWT │ │
├──────────────────────┼─────────────────────►│
│ │ │
│ │ 4. Token Validation │
│ │◄─────────────────────┤
│ │ │
│ 5. API Response │ │
│◄─────────────────────┼──────────────────────┤
Detailed Process Breakdown
Step 1: User Authentication
- User provides credentials (username/password, social login, or SAML/OIDC)
- Cognito User Pool validates credentials against configured identity sources
- Multi-factor authentication (MFA) can be enforced at this stage
Step 2: Token Generation
- Upon successful authentication, Cognito issues three types of JWT tokens:
- ID Token: Contains user identity information and claims
- Access Token: Used for accessing Cognito-secured APIs
- Refresh Token: Used to obtain new access and ID tokens
Step 3: API Authorization
- Client includes JWT tokens in API requests to your backend
- API Gateway or Lambda authorizer validates token signature and claims
- Request proceeds to backend application if authorization succeeds
Cognito User Pool Features
Identity Federation
Cognito can federate with external identity providers:
External IdP ┌─── SAML 2.0 (Active Directory, Okta)
Sources ────┼─── OIDC (Google, Facebook, Apple)
└─── Social Providers (Amazon, GitHub)
│
▼
┌─────────────────┐
│ Cognito User │ ────► Unified JWT Tokens
│ Pool │
└─────────────────┘
User Management Capabilities
- User Registration: Self-service sign-up with email/phone verification
- Password Policies: Configurable complexity requirements
- Account Recovery: Password reset via email or SMS
- User Attributes: Custom and standard attributes (email, name, custom claims)
- Groups and Roles: Organize users and assign permissions
Security Features
- Multi-Factor Authentication: SMS, TOTP, hardware tokens
- Advanced Security: Risk-based authentication and adaptive security
- Token Management: Configurable token expiration and refresh policies
- Audit Logging: CloudTrail integration for compliance
Integration with Amazon Q Business
Custom Plugin Authentication
When setting up custom plugins for Amazon Q Business, Cognito provides several key benefits:
1. Seamless User Experience
# OpenAPI 3.x specification example
securitySchemes:
CognitoAuth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://your-domain.auth.region.amazoncognito.com/oauth2/authorize
tokenUrl: https://your-domain.auth.region.amazoncognito.com/oauth2/token
scopes:
read: Read access to resources
write: Write access to resources
API Gateway Custom Authorizer vs Built-in Cognito Authorizer
Important Note: URL Configuration Difference
The above example shows Cognito URLs, but the configuration differs depending on your authorizer type:
Built-in Cognito Authorizer
# Uses Cognito URLs - Cognito acts as the OAuth2 provider
securitySchemes:
CognitoAuth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://your-domain.auth.region.amazoncognito.com/oauth2/authorize
tokenUrl: https://your-domain.auth.region.amazoncognito.com/oauth2/token
Custom Lambda Authorizer (Direct IdP Integration)
# Uses direct IdP URLs - bypasses Cognito OAuth2 flow
securitySchemes:
DirectIdPAuth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://login.microsoftonline.com/tenant-id/oauth2/v2.0/authorize
tokenUrl: https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token
# OR for Google
# authorizationUrl: https://accounts.google.com/o/oauth2/v2/auth
# tokenUrl: https://oauth2.googleapis.com/token
Key Differences:
Aspect | Built-in Cognito Authorizer | Custom Lambda Authorizer |
---|---|---|
URLs | Cognito OAuth2 endpoints | Direct IdP endpoints |
Token Validation | API Gateway handles automatically | Your Lambda code validates |
Public Key Management | Cognito manages IdP keys | You fetch and cache IdP keys |
Configuration Complexity | Simple - just reference User Pool | Complex - implement validation logic |
Performance | Optimized by AWS | Depends on your implementation |
Maintenance | Zero - fully managed | High - you handle key rotation |
Why Use Each Approach:
Choose Built-in Cognito Authorizer when:
- Want zero-maintenance authentication
- Need enterprise-grade performance and reliability
- Cognito User Pool federation meets your requirements
Choose Custom Lambda Authorizer when:
- Require custom authorization logic beyond token validation
- Have specific compliance requirements for token handling
- Need to validate non-JWT tokens or custom token formats
2. Token-Based Authorization
- Q Business automatically handles OAuth 2.0 flow with Cognito
- JWT tokens contain user identity and group memberships
- Custom claims can encode business-specific permissions
3. Session Management
- Cognito handles token refresh automatically
- Q Business caches authentication state for smooth user experience
- Session timeout and security policies enforced consistently
Backend API Protection
Your custom plugins can leverage Cognito tokens for fine-grained access control:
# Example Lambda authorizer using Cognito JWT
import jwt
import json
def lambda_handler(event, context):
token = event['authorizationToken']
try:
# Validate JWT signature and claims
decoded_token = jwt.decode(
token,
cognito_public_key,
algorithms=['RS256'],
audience=client_id
)
# Extract user groups and permissions
user_groups = decoded_token.get('cognito:groups', [])
user_id = decoded_token.get('sub')
# Apply business logic for authorization
if 'ProductAdmin' in user_groups:
return generate_policy('Allow', event['methodArn'])
else:
return generate_policy('Deny', event['methodArn'])
except jwt.InvalidTokenError:
return generate_policy('Deny', event['methodArn'])
Cognito vs Other Identity Solutions
When to Use Cognito
Ideal for:
- External user authentication (customers, partners)
- Mobile and web application user management
- Social identity federation
- Rapid development and deployment
- Fine-grained user attribute management
Consider Alternatives When:
- Primary users are internal employees (use IAM Identity Center)
- Existing enterprise identity infrastructure is sufficient
- Simple AWS service-to-service authentication (use IAM roles)
Cognito in Multi-Identity Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Internal │ │ External │ │ Customer │
│ Employees │ │ Partners │ │ Users │
│ │ │ │ │ │
│ IAM Identity │ │ Cognito + │ │ Cognito │
│ Center │ │ External IdP │ │ User Pools │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌─────────────────┐
│ Amazon Q │
│ Business │
│ (Unified Auth) │
└─────────────────┘
Layer Breakdown
1. Identity Layer
Purpose: Authenticate users and establish their identity
Components:
- External Identity Providers: Corporate SSO systems like Okta, ADFS
- Amazon Cognito: Flexible user management for external users
- IAM Identity Center: AWS-native SSO for internal applications
Key Function: Verify “who the user is” through credentials validation
2. Authorization Layer
Purpose: Determine what authenticated users can access
Components:
- API Gateway: Entry point for all requests with built-in security
- Lambda Authorizer: Custom business logic for access decisions
- Amazon Verified Permissions: Policy-based access control engine
Key Function: Decide “what the user can do” based on policies and context
3. Application Layer
Purpose: Deliver business functionality to authorized users
Components:
- Amazon Q Business: Core AI platform providing chat and insights
- Custom Plugins: External APIs and integrations
- Data Sources: Enterprise systems and databases
Key Function: Provide “business value” through secure access to data and AI capabilities
Authentication Flow Principles
Basic Flow Pattern
User Request → Identity Verification → Authorization Check → Application Access
Detailed Process
- User Authentication: User proves identity through chosen provider
- Token Generation: Identity provider issues signed token with user claims
- Request Authorization: Each API call validated against policies
- Resource Access: Authorized requests reach Q Business and plugins
- Audit Trail: All actions logged for security and compliance
Key Security Principles
Defense in Depth
- Identity verification at multiple points
- Token validation for every request
- Policy enforcement before resource access
- Audit logging for all activities
Zero Trust Model
- Never trust based on network location
- Always verify user identity and permissions
- Continuously monitor for anomalous behavior
- Apply least privilege access principles
Simplified Token Flow
[User] → [Identity Provider] → [Signed Token] → [API Gateway]
→ [Lambda Authorizer] → [Policy Check] → [Q Business/Plugins]
Implementation Approaches
Option 1: Start Simple (Identity Center Only)
- Use IAM Identity Center for all authentication
- Leverage built-in Q Business integration
- Add custom policies as needed
- Scale to other providers later
Option 2: Hybrid Approach (Multiple Providers)
- External IdP for corporate users
- Cognito for external/customer users
- Identity Center for AWS-native applications
- Unified authorization layer
Option 3: Custom Plugin Focus
- Standard authentication for Q Business core
- Custom authorizers for plugin APIs
- Fine-grained permissions per plugin
- Business-specific authorization logic
Benefits of Simplified Architecture
Easier Understanding
- Three clear layers with distinct purposes
- Straightforward data flow between layers
- Reduced complexity for implementation teams
Flexible Implementation
- Start with one identity provider
- Add authorization complexity gradually
- Scale application integrations over time
Maintainable Security
- Clear separation of authentication and authorization
- Centralized policy management
- Consistent audit and monitoring
Common Patterns
Pattern 1: Corporate SSO Integration
Corporate Directory → External IdP → API Gateway → Q Business
Pattern 2: Customer Access
Customer Portal → Cognito → Lambda Authorizer → Custom Plugins
Pattern 3: Mixed Environment
Multiple Identity Sources → Unified Authorization → Various Applications
Security Monitoring
Essential Metrics
- Authentication Success/Failure Rates
- Authorization Decision Latency
- Unusual Access Patterns
- Policy Violations
Audit Requirements
- Complete request tracing through all layers
- Policy evaluation details for compliance
- User activity patterns for security analysis
- System health metrics for reliability
Getting Started
Phase 1: Foundation
- Set up IAM Identity Center
- Configure basic Q Business access
- Establish monitoring and logging
- Test with pilot user group
Phase 2: Expansion
- Add external identity providers
- Implement custom plugin authentication
- Create fine-grained policies
- Expand to production users
Phase 3: Optimization
- Performance tuning and caching
- Advanced threat detection
- Automated policy management
- Compliance reporting
Conclusion
This simplified three-layer architecture provides a clear framework for implementing secure Amazon Q Business authentication. By focusing on identity, authorization, and application layers, organizations can:
- Build incrementally from simple to complex implementations
- Maintain security through clear separation of concerns
- Scale effectively as requirements grow
- Ensure compliance through comprehensive audit trails
The key to success is starting simple and adding complexity only as needed, while maintaining strong security principles throughout the implementation journey.
Automatic Key Discovery
- Cognito queries the OIDC discovery endpoint:
{issuer}/.well-known/openid_configuration
- Retrieves the JWKS URI from the discovery document
- Fetches public keys from the JWKS endpoint (e.g.,
https://www.googleapis.com/oauth2/v3/certs
) - Caches keys and refreshes them periodically
Detailed OIDC Discovery Process
Step 1: OpenID Connect Discovery
When you configure an OIDC provider in Cognito, the discovery process begins automatically:
// Example OIDC Provider Configuration
{
"ProviderName": "GoogleOIDC",
"ProviderType": "OIDC",
"ProviderDetails": {
"client_id": "123456789.apps.googleusercontent.com",
"client_secret": "your-secret",
"authorize_scopes": "openid email profile",
"oidc_issuer": "https://accounts.google.com"
}
}
Cognito constructs the discovery URL by appending the well-known path:
Discovery URL = {oidc_issuer}/.well-known/openid_configuration
Example: https://accounts.google.com/.well-known/openid_configuration
Step 2: Discovery Document Retrieval
Cognito makes an HTTP GET request to the discovery endpoint and receives a comprehensive configuration document:
{
"issuer": "https://accounts.google.com",
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
"token_endpoint": "https://oauth2.googleapis.com/token",
"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
"revocation_endpoint": "https://oauth2.googleapis.com/revoke",
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token",
"none"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"scopes_supported": [
"openid",
"email",
"profile"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic"
],
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"family_name",
"given_name",
"iat",
"iss",
"locale",
"name",
"picture",
"sub"
],
"code_challenge_methods_supported": [
"plain",
"S256"
]
}
Step 3: JWKS Endpoint Extraction
From the discovery document, Cognito extracts the jwks_uri
field:
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs"
This URI points to the JSON Web Key Set containing the public keys used to verify JWT signatures.
Step 4: JWKS Fetching Process
Cognito makes a GET request to the JWKS endpoint and receives the public keys:
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "1234567890abcdef1234567890abcdef12345678",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtV....",
"e": "AQAB"
},
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "abcdef1234567890abcdef1234567890abcdef12",
"n": "xf7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtV....",
"e": "AQAB"
}
]
}
Step 5: Key Processing and Storage
Cognito processes each key in the JWKS:
# Simplified representation of Cognito's key processing
class CognitoOIDCKeyProcessor:
def process_jwks(self, jwks_response):
processed_keys = {}
for key in jwks_response['keys']:
# Extract key identifier
kid = key['kid']
# Validate key parameters
if key['kty'] != 'RSA':
continue # Only RSA keys supported
if key.get('use') not in ['sig', None]:
continue # Only signing keys
if key.get('alg') not in ['RS256', 'RS384', 'RS512']:
continue # Only supported algorithms
# Convert JWK to internal format
public_key = self.jwk_to_rsa_key(key)
# Store with metadata
processed_keys[kid] = {
'public_key': public_key,
'algorithm': key.get('alg', 'RS256'),
'use': key.get('use', 'sig'),
'expires_at': self.calculate_expiry(),
'last_updated': time.time()
}
return processed_keys
def jwk_to_rsa_key(self, jwk):
# Convert JWK parameters to RSA public key
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
# Decode base64url encoded parameters
n = self.base64url_decode(jwk['n'])
e = self.base64url_decode(jwk['e'])
# Create RSA public key
public_numbers = rsa.RSAPublicNumbers(
e=int.from_bytes(e, 'big'),
n=int.from_bytes(n, 'big')
)
public_key = public_numbers.public_key()
return public_key
Caching and Automatic Refresh Mechanism
Caching Strategy
Cognito implements a multi-level caching strategy for OIDC keys:
┌─────────────────────────────────────────────────────────────┐
│ Cognito Key Cache │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ L1 Cache (Memory) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Provider A │ │ Provider B │ │ Provider C │ │ │
│ │ │ JWKS Keys │ │ JWKS Keys │ │ JWKS Keys │ │ │
│ │ │ TTL: 1 hour │ │ TTL: 1 hour │ │ TTL: 1 hour │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ L2 Cache (Persistent) │ │
│ │ • Backup key storage for failover │ │
│ │ • Extended TTL for emergency use │ │
│ │ • Historical key versions │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Refresh Triggers
Multiple events can trigger a key refresh:
class CognitoKeyRefreshManager:
def __init__(self):
self.refresh_triggers = {
'scheduled': 3600, # Every hour
'cache_miss': True, # Key not found for kid
'validation_failure': True, # JWT validation fails
'manual': True, # Admin-triggered refresh
'provider_notification': True # Webhook from provider
}
def should_refresh_keys(self, provider_id, context):
last_refresh = self.get_last_refresh_time(provider_id)
current_time = time.time()
# Scheduled refresh
if current_time - last_refresh > self.refresh_triggers['scheduled']:
return True, 'scheduled_refresh'
# Cache miss - unknown kid in token
if context.get('unknown_kid'):
return True, 'cache_miss'
# Recent validation failures
failure_rate = self.get_recent_failure_rate(provider_id)
if failure_rate > 0.1: # 10% failure threshold
return True, 'high_failure_rate'
# Emergency refresh
if context.get('force_refresh'):
return True, 'manual_refresh'
return False, None
Refresh Process
The refresh process is designed for high availability:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Refresh │ │ Background │ │ Validation │
│ Trigger │ │ Worker │ │ Engine │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ 1. Refresh Request │ │
├─────────────────────►│ │
│ │ │
│ │ 2. Fetch New Keys │
│ │ (Non-blocking) │
│ │ │
│ │ 3. Validate Keys │
│ ├─────────────────────►│
│ │ │
│ │ 4. Keys Valid │
│ │◄─────────────────────┤
│ │ │
│ │ 5. Atomic Update │
│ │ Cache │
│ │ │
│ 6. Refresh Complete │ │
│◄─────────────────────┤ │
Error Handling During Refresh
class CognitoKeyRefreshHandler:
def refresh_keys_with_fallback(self, provider_config):
try:
# Primary refresh attempt
new_keys = self.fetch_fresh_keys(provider_config)
# Validate new keys before replacing
if self.validate_key_set(new_keys):
self.atomic_cache_update(provider_config['id'], new_keys)
return new_keys
else:
raise KeyValidationError("Invalid key set received")
except (NetworkError, TimeoutError) as e:
# Network issues - extend current cache TTL
self.extend_cache_ttl(provider_config['id'], extension=1800) # 30 min
raise TemporaryRefreshFailure(f"Network error: {e}")
except KeyValidationError as e:
# Invalid keys - keep current cache, alert admins
self.alert_administrators(provider_config['id'], str(e))
raise PermanentRefreshFailure(f"Key validation failed: {e}")
except Exception as e:
# Unexpected error - fallback to backup cache
backup_keys = self.get_backup_keys(provider_config['id'])
if backup_keys:
return backup_keys
raise CriticalRefreshFailure(f"All refresh mechanisms failed: {e}")
Performance Optimization
To ensure minimal latency impact:
# Asynchronous refresh to avoid blocking token validation
async def background_key_refresh(provider_id):
"""Non-blocking key refresh that doesn't impact active validation"""
try:
# Fetch new keys in background
new_keys = await fetch_keys_async(provider_id)
# Pre-validate keys
if validate_keys(new_keys):
# Atomic cache swap - no downtime
await atomic_cache_update(provider_id, new_keys)
# Update metrics
update_refresh_metrics(provider_id, success=True)
except Exception as e:
# Log error but don't disrupt service
log_refresh_error(provider_id, e)
update_refresh_metrics(provider_id, success=False)
This comprehensive OIDC discovery and key management process ensures that Cognito can reliably authenticate users from external OIDC providers while maintaining high availability and security standards through automated key rotation and robust error handling.
Performance Deep Dive: Key Fetching and Caching
The performance differences between built-in Cognito authorizers and custom Lambda authorizers are significant, particularly around key management:
Built-in Cognito Authorizer Performance
Optimized Key Management Infrastructure
┌─────────────────────────────────────────────────────────────┐
│ AWS-Managed Infrastructure │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ API Gateway │ │ Cognito │ │
│ │ Key Cache │ │ Key Cache │ │
│ │ │ │ │ │
│ │ • Regional │ │ • Global │ │
│ │ • Sub-ms access │ │ • Multi-region │ │
│ │ • Auto-refresh │ │ • Predictive │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ └───────────────────────┼───────────────────────┘
│ │
│ ┌─────────────────────────────────────────────────────────┐
│ │ Performance Characteristics │
│ │ │
│ │ • Token validation: < 5ms average │
│ │ • Key cache hit ratio: > 99.9% │
│ │ • Zero cold starts │
│ │ • Automatic scaling │
│ │ • Built-in redundancy │
│ └─────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────┘
Key Advantages:
- No Cold Starts: Always warm, dedicated infrastructure
- Predictive Caching: AWS pre-fetches keys before expiration
- Regional Optimization: Keys cached closer to API Gateway instances
- Automatic Scaling: Handles traffic spikes without degradation
Custom Lambda Authorizer Performance
Developer-Implemented Caching
Lambda custom authorizers do not automatically cache keys - you must implement caching yourself:
API Gateway’s Built-in Authorization Result Caching
Important Discovery: API Gateway has its own authorization cache layer that can explain why your Lambda authorizer logs don’t always appear!
┌─────────────────────────────────────────────────────────────┐
│ API Gateway Request Flow │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Incoming │ │ Authorization │ │
│ │ Request │ │ Cache │ │
│ │ │ │ │ │
│ │ • Token: ABC... │───►│ • Token: ABC... │ │
│ │ • Method: GET │ │ • Policy: Allow │ │
│ │ • Resource: /api│ │ • TTL: 300s │ │
│ └─────────────────┘ └─────────┬───────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Cache Hit? │ │
│ └────────┬────────┘ │
│ │ │
│ Yes ◄────────────────┼────────────────► No │
│ │ │ │ │
│ ▼ │ ▼ │
│ ┌─────────────────┐ │ ┌─────────────────┐ │
│ │ Use Cached │ │ │ Invoke Lambda │ │
│ │ Policy │ │ │ Authorizer │ │
│ │ │ │ │ │ │
│ │ • No Lambda │ │ │ • Full auth │ │
│ │ invocation │ │ │ logic runs │ │
│ │ • No logs │ │ │ • CloudWatch │ │
│ │ • Sub-ms │ │ │ logs created │ │
│ └─────────────────┘ │ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Why Your Lambda Authorizer Logs Don’t Always Appear:
When API Gateway caches an authorization decision, subsequent requests with the same token bypass the Lambda authorizer entirely:
# Lambda authorizer function
def lambda_handler(event, context):
print(f"Processing token: {event['authorizationToken'][:10]}...")
# This log will ONLY appear on cache misses!
# If API Gateway has cached the auth decision,
# this function won't be invoked at all
return {
'principalId': 'user123',
'policyDocument': {
'Version': '2012-10-17',
'Statement': [{
'Action': 'execute-api:Invoke',
'Effect': 'Allow',
'Resource': event['methodArn']
}]
},
'context': {
'ttl': 300 # Cache this result for 5 minutes
}
}
Caching Behavior Examples:
# Request sequence showing caching behavior:
# Request 1: Token "xyz123..."
POST /api/products
Authorization: Bearer xyz123...
→ Lambda authorizer invoked (logs appear)
→ Policy cached for 300 seconds
→ Request allowed
# Request 2: Same token, within 300 seconds
GET /api/products
Authorization: Bearer xyz123...
→ Cache hit - Lambda authorizer NOT invoked
→ NO logs in CloudWatch
→ Request allowed immediately
# Request 3: Same token, different resource
PUT /api/products/1
Authorization: Bearer xyz123...
→ Cache hit - Lambda authorizer NOT invoked
→ NO logs in CloudWatch
→ Request allowed (if policy covers this resource)
# Request 4: Same token, after 300 seconds
GET /api/products
Authorization: Bearer xyz123...
→ Cache expired - Lambda authorizer invoked again
→ Logs appear in CloudWatch
→ Policy cached again
Configuring Authorization Caching:
1. TTL Configuration in Lambda Response:
def lambda_handler(event, context):
return {
'principalId': 'user123',
'policyDocument': policy_doc,
'context': {
'ttl': 3600, # Cache for 1 hour
'userId': 'user123',
'permissions': 'read,write'
}
}
2. CloudFormation Configuration:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: LambdaAuthorizer
Type: TOKEN
AuthorizerCredentials: !GetAtt LambdaAuthorizerRole.Arn
AuthorizerUri: !Sub
- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaArn}/invocations
- LambdaArn: !GetAtt AuthorizerFunction.Arn
AuthorizerResultTtlInSeconds: 300 # 5 minutes default
IdentitySource: method.request.header.Authorization
3. Serverless Framework Configuration:
authorizer:
name: customAuthorizer
type: TOKEN
authorizerCredentials: arn:aws:iam::123456789:role/LambdaAuthRole
resultTtlInSeconds: 300
identitySource: method.request.header.Authorization
Cache Key Composition:
API Gateway creates cache keys based on:
cache_key = {
'authorization_token': 'Bearer xyz123...', # Full token
'method_arn': 'arn:aws:execute-api:region:account:api-id/stage/method/resource'
}
# Examples of different cache keys:
# Key 1: Token A + GET /products → Separate cache entry
# Key 2: Token A + POST /products → Separate cache entry
# Key 3: Token B + GET /products → Separate cache entry
# Key 4: Token A + GET /products → Same as Key 1 (cache hit)
Performance Impact of API Gateway Caching:
# Measurement results from production systems:
auth_performance = {
'cache_miss': {
'latency': '50-200ms', # Lambda cold start + execution
'lambda_invocations': 1,
'cloudwatch_logs': 'Yes',
'cost_per_request': '$0.0000002' # Lambda execution cost
},
'cache_hit': {
'latency': '1-5ms', # Sub-millisecond auth check
'lambda_invocations': 0,
'cloudwatch_logs': 'No',
'cost_per_request': '$0.0000000' # No additional cost
}
}
# With 95% cache hit ratio:
# Average latency: (0.05 * 125ms) + (0.95 * 3ms) = 9.1ms
# vs No caching: 125ms per request
Monitoring Cache Effectiveness:
# CloudWatch metrics to track:
metrics_to_monitor = {
'custom_metrics': [
'LambdaAuthorizerInvocations', # Track actual Lambda calls
'TotalAPIRequests', # Track all API requests
'CacheHitRatio' # Calculate hit ratio
],
'calculated_cache_hit_ratio': 'TotalAPIRequests - LambdaAuthorizerInvocations / TotalAPIRequests'
}
# Example CloudWatch Logs Insights query:
query = """
fields @timestamp, @message
| filter @message like /Processing token/
| stats count() by bin(5m)
"""
Security Considerations for Caching:
1. Token Revocation Delay:
# Problem: Revoked tokens may still work during cache TTL
security_implications = {
'risk': 'Revoked tokens remain valid until cache expires',
'mitigation': [
'Use shorter TTL (30-300 seconds)',
'Implement token blacklisting',
'Use context for additional validation'
]
}
2. Optimal TTL Selection:
ttl_recommendations = {
'high_security': 60, # 1 minute - frequent re-validation
'balanced': 300, # 5 minutes - good performance/security
'performance_focused': 3600 # 1 hour - maximum caching
}
3. Cache Invalidation Strategies:
# Force cache invalidation by changing token structure
def invalidate_user_cache(user_id):
"""Force new auth by incrementing token version"""
new_token = generate_token(user_id, version=get_next_version(user_id))
return new_token
# Or use context-based validation
def lambda_handler(event, context):
# Always check critical flags even with caching
user_status = check_user_status_in_realtime()
if user_status == 'suspended':
return deny_policy()
# Normal caching for active users
return allow_policy_with_ttl(300)
This API Gateway caching behavior explains why you don’t always see Lambda authorizer logs - it’s actually a performance feature that reduces latency and costs by avoiding unnecessary Lambda invocations for recently authorized tokens.
Cost Analysis: Authentication Architecture Economics
Understanding the cost implications of different authentication approaches is crucial for making informed architectural decisions, especially when scaling Amazon Q Business implementations.
Cost Breakdown by Authentication Method
Built-in Cognito Authorizer Costs
Service Components:
# Monthly costs for 1M API requests (us-east-1 pricing)
cognito_authorizer_costs = {
'api_gateway': {
'cost': 3.50,
'description': '$3.50 per million API calls',
'notes': 'Standard API Gateway pricing'
},
'cognito_user_pool': {
'cost': 0.00,
'description': 'Token validation included',
'notes': 'No additional cost for JWT validation'
},
'cognito_mau': {
'cost': 0.0055, # per MAU above free tier
'description': '$0.0055 per MAU above 50,000 free',
'notes': 'Monthly Active Users pricing'
},
'total_base': 3.50,
'total_with_users': '3.50 + (MAU_above_50k * 0.0055)'
}
Cost Examples:
Scenario 1: Small Application (10,000 MAU, 1M API calls/month)
├── API Gateway: $3.50
├── Cognito MAU: $0.00 (within free tier)
└── Total: $3.50/month
Scenario 2: Medium Application (100,000 MAU, 10M API calls/month)
├── API Gateway: $35.00
├── Cognito MAU: $275.00 (50,000 users above free tier)
└── Total: $310.00/month
Scenario 3: Large Application (500,000 MAU, 50M API calls/month)
├── API Gateway: $175.00
├── Cognito MAU: $2,475.00 (450,000 users above free tier)
└── Total: $2,650.00/month
Custom Lambda Authorizer Costs
Service Components:
# Monthly costs for 1M API requests
lambda_authorizer_costs = {
'api_gateway': {
'cost': 3.50,
'description': '$3.50 per million API calls',
'notes': 'Same as built-in authorizer'
},
'lambda_invocations': {
'cost': 0.20,
'description': '$0.20 per million invocations',
'notes': 'Reduced by caching effectiveness'
},
'lambda_compute': {
'cost_range': [1.50, 8.00],
'description': '$1.50-$8.00 based on execution time',
'notes': 'Depends on key fetching and validation logic'
},
'external_api_calls': {
'cost_range': [0.50, 25.00],
'description': 'IdP API call costs',
'notes': 'Varies by provider and caching strategy'
}
}
Cost Scenarios with Different Cache Hit Ratios:
# Cost calculation based on cache effectiveness
def calculate_lambda_auth_costs(api_calls_millions, cache_hit_ratio):
lambda_invocations = api_calls_millions * (1 - cache_hit_ratio)
costs = {
'api_gateway': api_calls_millions * 3.50,
'lambda_invocations': lambda_invocations * 0.20,
'lambda_compute': lambda_invocations * 3.00, # Average
'external_calls': lambda_invocations * 5.00, # Average IdP cost
}
return costs
# Examples:
scenarios = {
'poor_caching_50%': calculate_lambda_auth_costs(1, 0.50),
'good_caching_90%': calculate_lambda_auth_costs(1, 0.90),
'excellent_caching_95%': calculate_lambda_auth_costs(1, 0.95),
'no_caching_0%': calculate_lambda_auth_costs(1, 0.00)
}
Results:
1M API Calls with Different Cache Hit Ratios:
No Caching (0% hit ratio):
├── API Gateway: $3.50
├── Lambda Invocations: $0.20
├── Lambda Compute: $3.00
├── External API Calls: $5.00
└── Total: $11.70/month
Poor Caching (50% hit ratio):
├── API Gateway: $3.50
├── Lambda Invocations: $0.10
├── Lambda Compute: $1.50
├── External API Calls: $2.50
└── Total: $7.60/month
Good Caching (90% hit ratio):
├── API Gateway: $3.50
├── Lambda Invocations: $0.02
├── Lambda Compute: $0.30
├── External API Calls: $0.50
└── Total: $4.32/month
Excellent Caching (95% hit ratio):
├── API Gateway: $3.50
├── Lambda Invocations: $0.01
├── Lambda Compute: $0.15
├── External API Calls: $0.25
└── Total: $3.91/month
Scale-Based Cost Comparison
Small Scale (1M API calls/month)
Authentication Method | Monthly Cost | Cache Dependency | Maintenance Cost |
---|---|---|---|
Built-in Cognito | $3.50 | No caching needed | $0 |
Lambda (No Cache) | $11.70 | Critical for cost control | High |
Lambda (95% Cache) | $3.91 | Critical for cost control | High |
Medium Scale (10M API calls/month)
Authentication Method | Monthly Cost | Annual Cost | Notes |
---|---|---|---|
Built-in Cognito | $35.00 + MAU costs | $420 + MAU costs | Predictable scaling |
Lambda (No Cache) | $117.00 | $1,404 | Unsustainable |
Lambda (95% Cache) | $39.10 | $469 | Requires excellent implementation |
Large Scale (100M API calls/month)
Authentication Method | Monthly Cost | Annual Cost | Engineering Cost |
---|---|---|---|
Built-in Cognito | $350.00 + MAU costs | $4,200 + MAU costs | Minimal |
Lambda (No Cache) | $1,170.00 | $14,040 | High + engineering |
Lambda (95% Cache) | $391.00 | $4,692 | Ongoing optimization |
Hidden Costs Analysis
Built-in Cognito Authorizer
hidden_costs_cognito = {
'engineering_time': {
'setup': '2-5 days',
'maintenance': '0.5 days/month',
'annual_engineering_cost': '$2,000-$5,000'
},
'operational_overhead': {
'monitoring': 'Built-in CloudWatch',
'troubleshooting': 'AWS Support included',
'cost': '$0'
},
'scaling_concerns': {
'automatic': True,
'performance_degradation': 'None',
'additional_config': 'None required'
}
}
Custom Lambda Authorizer
hidden_costs_lambda = {
'engineering_time': {
'initial_development': '10-20 days',
'caching_implementation': '5-10 days',
'ongoing_maintenance': '2-4 days/month',
'annual_engineering_cost': '$25,000-$50,000'
},
'operational_overhead': {
'monitoring_setup': '3-5 days',
'custom_metrics': 'Required',
'troubleshooting': 'Custom implementation',
'annual_ops_cost': '$5,000-$10,000'
},
'scaling_challenges': {
'cache_tuning': 'Ongoing',
'key_rotation_handling': 'Manual',
'performance_optimization': 'Continuous'
}
}
Cost Optimization Strategies
For Built-in Cognito Authorizer
1. User Pool Optimization:
cognito_optimization = {
'mau_management': [
'Implement user cleanup for inactive accounts',
'Use federated identities for external users',
'Monitor MAU usage patterns'
],
'feature_optimization': [
'Disable unused Cognito features',
'Use basic authentication for internal APIs',
'Leverage existing corporate identity providers'
]
}
2. API Gateway Optimization:
api_gateway_optimization = {
'request_reduction': [
'Implement client-side caching',
'Use WebSocket for real-time features',
'Batch API requests where possible'
],
'regional_optimization': [
'Deploy in regions with lower costs',
'Use CloudFront for static content',
'Optimize data transfer'
]
}
For Custom Lambda Authorizer
1. Caching Optimization:
lambda_caching_optimization = {
'cache_hit_ratio_improvement': [
'Implement multi-layer caching',
'Use Redis/ElastiCache for shared cache',
'Optimize TTL based on usage patterns',
'Pre-warm cache for popular keys'
],
'cost_per_cache_miss': [
'Minimize IdP API calls',
'Batch key fetching operations',
'Use connection pooling',
'Implement exponential backoff'
]
}
2. Lambda Function Optimization:
lambda_function_optimization = {
'execution_time': [
'Minimize cold start impact',
'Use provisioned concurrency for high-traffic',
'Optimize memory allocation',
'Minimize external dependencies'
],
'invocation_reduction': [
'Maximize cache TTL safely',
'Implement intelligent cache invalidation',
'Use API Gateway caching effectively'
]
}
Total Cost of Ownership (3-Year Analysis)
# 3-year TCO for medium-scale application (10M API calls/month)
tco_analysis = {
'cognito_authorizer': {
'infrastructure': '$1,260', # API Gateway costs
'mau_costs': '$9,900', # Assuming 100k MAU average
'engineering': '$15,000', # Minimal maintenance
'total_3_year': '$26,160'
},
'lambda_authorizer_optimized': {
'infrastructure': '$1,404', # API Gateway + Lambda
'external_costs': '$360', # IdP API calls
'engineering': '$150,000', # Development + maintenance
'monitoring': '$15,000', # Custom monitoring setup
'total_3_year': '$166,764'
},
'savings_with_cognito': '$140,604'
}
Cost Decision Matrix
Choose Built-in Cognito Authorizer when:
✅ Total API calls < 100M/month
✅ Standard authentication requirements
✅ Limited engineering resources
✅ Need predictable costs
✅ Want zero operational overhead
Choose Custom Lambda Authorizer when:
✅ Complex custom authorization logic required
✅ Existing investment in custom auth systems
✅ High-volume, cost-sensitive applications (>95% cache hit ratio)
✅ Dedicated engineering team for optimization
✅ Specific compliance requirements not met by Cognito
Best Practices for Cost Management
- Monitor Key Metrics:
- Cache hit ratios for Lambda authorizers
- MAU growth for Cognito
- API call patterns and optimization opportunities
- Implement Cost Controls:
- Set up billing alerts for unexpected costs
- Use AWS Cost Explorer for trend analysis
- Regular cost optimization reviews
- Plan for Scale:
- Project MAU growth for Cognito pricing
- Test cache effectiveness at higher loads
- Consider regional pricing differences
The cost analysis clearly shows that while built-in Cognito authorizers have higher per-user costs at scale, they offer significant savings in engineering and operational overhead, making them cost-effective for most Amazon Q Business implementations.
OAuth2 Validation Flow Diagrams
Understanding how these URLs are used in the validation process is crucial for implementing secure authentication:
Built-in Cognito Authorizer Flow
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Built-in Cognito Authorizer Flow │
│ (Uses Cognito OAuth2 URLs) │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Amazon Q │ │ Cognito │ │ API Gateway │ │ Backend │
│ Business │ │ User Pool │ │ (Built-in Auth) │ │ Application │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │ │
│ 1. Redirect to Auth │ │ │
├─────────────────────►│ │ │
│ GET: /oauth2/authorize│ │ │
│ │ │ │
│ 2. User Login │ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 3. Authorization Code│ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 4. Exchange Code │ │ │
├─────────────────────►│ │ │
│ POST: /oauth2/token │ │ │
│ │ │ │
│ 5. JWT Tokens │ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 6. API Request + JWT │ │ │
├──────────────────────┼─────────────────────►│ │
│ │ │ │
│ │ 7. Automatic │ │
│ │ Validation │ │
│ │◄─────────────────────┤ │
│ │ • JWKS fetch │ │
│ │ • Signature verify │ │
│ │ • Claims check │ │
│ │ │ │
│ │ 8. Valid Token │ │
│ │─────────────────────►│ │
│ │ │ │
│ │ │ 9. Forward Request │
│ │ ├─────────────────────►│
│ │ │ │
│ 10. API Response │ │ 10. Response │
│◄─────────────────────┼──────────────────────┼──────────────────────┤
Key Validation Points:
- Step 7: API Gateway automatically validates JWT using Cognito’s JWKS endpoint
- Cognito URLs Used:
- Authorization:
https://your-domain.auth.region.amazoncognito.com/oauth2/authorize
- Token Exchange:
https://your-domain.auth.region.amazoncognito.com/oauth2/token
- JWKS:
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
- Authorization:
Custom Lambda Authorizer Flow
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Custom Lambda Authorizer Flow │
│ (Uses Direct IdP URLs) │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Amazon Q │ │ External IdP │ │ API Gateway │ │ Lambda │
│ Business │ │ (Azure/Google) │ │ + Lambda Auth │ │ Authorizer │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │ │
│ 1. Redirect to Auth │ │ │
├─────────────────────►│ │ │
│ GET: /oauth2/authorize│ │ │
│ │ │ │
│ 2. User Login │ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 3. Authorization Code│ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 4. Exchange Code │ │ │
├─────────────────────►│ │ │
│ POST: /oauth2/token │ │ │
│ │ │ │
│ 5. JWT Tokens │ │ │
│◄─────────────────────┤ │ │
│ │ │ │
│ 6. API Request + JWT │ │ │
├──────────────────────┼─────────────────────►│ │
│ │ │ │
│ │ │ 7. Invoke Lambda │
│ │ ├─────────────────────►│
│ │ │ event.authToken │
│ │ │ │
│ │ 8. Fetch JWKS │ │
│ │◄─────────────────────┼──────────────────────┤
│ │ GET: /.well-known/ │ │
│ │ jwks.json │ │
│ │ │ │
│ │ 9. Public Keys │ │
│ │─────────────────────►┼─────────────────────►│
│ │ │ │
│ │ │ │ 10. Validate JWT
│ │ │ │ • Verify signature
│ │ │ │ • Check claims
│ │ │ │
│ │ │ 11. IAM Policy │
│ │ │◄─────────────────────┤
│ │ │ Allow/Deny │
│ │ │ │
│ │ │ 12. Forward Request │
│ │ ├─────────────────────►│
│ │ │ (if allowed) │ Backend
│ │ │ │ Lambda
│ 13. API Response │ │ 13. Response │
│◄─────────────────────┼──────────────────────┼──────────────────────┤
Key Validation Points:
- Step 8-9: Lambda authorizer fetches JWKS from external IdP
- Step 10: Custom validation logic in Lambda function
- Direct IdP URLs Used:
- Authorization:
https://login.microsoftonline.com/tenant-id/oauth2/v2.0/authorize
- Token Exchange:
https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token
- JWKS:
https://login.microsoftonline.com/tenant-id/discovery/v2.0/keys
- Authorization:
This flow visualization shows how the OAuth2 URLs are used in practice - with built-in Cognito authorizers handling validation automatically behind the scenes, while custom Lambda authorizers must implement the complete validation workflow manually using the external IdP URLs.
URL Validation Details
Cognito OAuth2 Endpoints
# Cognito OAuth2 endpoint structure
cognito_endpoints = {
'authorization_url': {
'pattern': 'https://{domain}.auth.{region}.amazoncognito.com/oauth2/authorize',
'purpose': 'User authentication and authorization code generation',
'parameters': [
'client_id',
'response_type=code',
'scope=openid email profile',
'redirect_uri',
'state'
]
},
'token_url': {
'pattern': 'https://{domain}.auth.{region}.amazoncognito.com/oauth2/token',
'purpose': 'Exchange authorization code for JWT tokens',
'method': 'POST',
'content_type': 'application/x-www-form-urlencoded'
},
'jwks_url': {
'pattern': 'https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json',
'purpose': 'Public keys for JWT signature validation',
'method': 'GET',
'auto_discovery': True
}
}
External IdP Endpoints
# External IdP endpoint examples
external_idp_endpoints = {
'microsoft_azure': {
'authorization_url': 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize',
'token_url': 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token',
'jwks_url': 'https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys',
'discovery_url': 'https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid_configuration'
},
'google': {
'authorization_url': 'https://accounts.google.com/o/oauth2/v2/auth',
'token_url': 'https://oauth2.googleapis.com/token',
'jwks_url': 'https://www.googleapis.com/oauth2/v3/certs',
'discovery_url': 'https://accounts.google.com/.well-known/openid_configuration'
},
'okta': {
'authorization_url': 'https://{domain}.okta.com/oauth2/v1/authorize',
'token_url': 'https://{domain}.okta.com/oauth2/v1/token',
'jwks_url': 'https://{domain}.okta.com/oauth2/v1/keys',
'discovery_url': 'https://{domain}.okta.com/.well-known/openid_configuration'
}
}
Validation Implementation Examples
Built-in Cognito Validation (Automatic)
# API Gateway automatically handles this validation
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref CognitoAuthorizer
# API Gateway automatically:
# 1. Fetches JWKS from Cognito
# 2. Validates JWT signature
# 3. Checks token claims (iss, aud, exp)
# 4. Allows/denies request
Security Considerations for URL Validation
security_checklist = {
'url_validation': [
'Verify HTTPS endpoints only',
'Validate SSL certificates',
'Check endpoint availability',
'Monitor for endpoint changes'
],
'token_validation': [
'Verify issuer claim matches expected IdP',
'Check audience claim matches your application',
'Validate token expiration time',
'Verify signature using current public keys'
],
'endpoint_monitoring': [
'Monitor JWKS endpoint availability',
'Track key rotation events',
'Alert on validation failures',
'Implement circuit breaker patterns'
]
}
This flow visualization shows how the OAuth2 URLs are used in practice - with built-in Cognito authorizers handling validation automatically behind the scenes, while custom Lambda authorizers must implement the complete validation workflow manually using the external IdP URLs.
Cognito Runtime Interaction with External IdP Authorize Endpoints
Understanding how Cognito interacts with external Identity Provider (IdP) authorize endpoints at runtime is crucial for troubleshooting and optimizing the authentication flow in Amazon Q Business implementations.
What Happens When Cognito Receives an Authorize Call
When a user initiates authentication through Cognito User Pool with an external IdP, Cognito does not make direct server-to-server calls to the IdP’s authorize endpoint. Instead, it orchestrates the flow through browser redirects.
Runtime Flow Breakdown
Step 1: Initial Authorize Request to Cognito
GET https://your-domain.auth.region.amazoncognito.com/oauth2/authorize
?client_id=your-app-client-id
&response_type=code
&scope=openid+profile+email
&redirect_uri=https://your-app.com/callback
&identity_provider=AzureAD
Step 2: Cognito Processes Request Cognito performs these actions immediately:
- Validates the incoming request parameters
- Identifies the specified external IdP (
identity_provider=AzureAD
) - Retrieves the configured IdP authorize URL from its stored configuration
- Generates a secure state parameter for CSRF protection
Step 3: Dynamic IdP URL Construction Cognito dynamically builds the external IdP authorize URL at runtime:
Original IdP Authorize URL (configured in Cognito):
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
Runtime-constructed URL with parameters:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
?client_id={cognito-configured-app-id}
&response_type=code
&scope=openid+profile+email
&redirect_uri=https://your-domain.auth.region.amazoncognito.com/oauth2/idpresponse
&state={cognito-generated-state}
&nonce={cognito-generated-nonce}
Step 4: Browser Redirect Response Cognito sends an HTTP redirect response (not a direct API call):
HTTP/1.1 302 Found
Location: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?...
Set-Cookie: cognito-fl="..."; Secure; HttpOnly
Detailed Runtime Sequence Diagram
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Browser │ │ Cognito User │ │ External IdP │
│ │ │ Pool │ │ (Azure/Google) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ 1. GET /oauth2/authorize │
├─────────────────────►│ │
│ ?identity_provider=AzureAD │
│ │ │
│ │ 2. Process Request │
│ │ • Validate params │
│ │ • Lookup IdP config │
│ │ • Generate state │
│ │ │
│ 3. 302 Redirect │ │
│◄─────────────────────┤ │
│ Location: IdP Auth URL │
│ │ │
│ 4. GET /oauth2/authorize │
├──────────────────────┼─────────────────────►│
│ (follows redirect) │ │
│ │ │
│ 5. User Authentication │
│◄─────────────────────┼──────────────────────┤
│ (login form/SSO) │ │
Key Runtime Behaviors
Parameter Mapping and Transformation
Cognito Input Parameters:
Original Request:
?client_id=your-app-client-id
&redirect_uri=https://your-app.com/callback
&scope=openid+profile+email
&identity_provider=AzureAD
Cognito Transforms to IdP Parameters:
Transformed Request to IdP:
?client_id={cognito-registered-app-id} // Different from original
&redirect_uri=https://cognito-domain.../oauth2/idpresponse // Cognito endpoint
&scope=openid+profile+email // Passed through
&state={cognito-generated-state} // Generated by Cognito
&response_type=code // Always authorization code flow
State Management
Cognito generates and manages state parameters for security:
State Parameter Components:
├── Original request state (if provided)
├── Cognito session identifier
├── Security token for CSRF protection
└── Return URL for post-authentication redirect
When Cognito DOES Make Direct Server Calls
While Cognito uses redirects for the authorize endpoint, it makes direct HTTP calls to other IdP endpoints:
1. Token Exchange (POST to Token Endpoint)
Timing: After user completes IdP authentication
Method: Direct HTTP POST from Cognito servers
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code={received-auth-code}
&client_id={cognito-configured-app-id}
&client_secret={cognito-configured-secret}
&redirect_uri=https://your-domain.auth.region.amazoncognito.com/oauth2/idpresponse
2. JWKS Key Fetching (GET to JWKS Endpoint)
Timing: During token validation
Method: Direct HTTP GET from Cognito servers
GET https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys
Accept: application/json
3. OIDC Discovery (GET to Discovery Endpoint)
Timing: During initial configuration and periodic refresh
Method: Direct HTTP GET from Cognito servers
GET https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid_configuration
Accept: application/json
Comments