This document provides a comprehensive overview of the security architecture, authentication flow, and token management for the AgentCore MCP Proxy.
- Overview
- Authentication Flow
- Hardware-Bound Token Encryption
- Token Storage and Lifecycle
- Security Guarantees
- Threat Model
- Security Best Practices
- Troubleshooting Security Issues
The AgentCore MCP Proxy implements a multi-layered security architecture that combines:
- Dual Authentication Methods: Amazon Corporate SSO (Federate/Midway) or Cognito User Pool (username/password)
- OAuth 2.0 Authorization Code Flow with AWS Cognito
- Hardware-bound token encryption using machine-specific identifiers
- Automatic token refresh with 30-day validity
- Intelligent authentication fallback with user choice UI
- Bearer token authentication for API calls
- Defense in Depth: Multiple security layers protect credentials
- Non-Portability: Tokens are bound to specific hardware and cannot be transferred
- Least Privilege: Tokens have limited scope and expiration
- Secure by Default: All tokens are encrypted at rest
- Zero Trust: Every request requires valid authentication
The MCP server supports two authentication methods:
- Amazon Corporate SSO (Federate) - Uses Midway cookies and Amazon identity provider
- Cognito User Pool - Direct username/password authentication
The server intelligently selects the authentication method:
- If Midway cookies are available → Uses Federate (Amazon SSO)
- If Midway unavailable → Shows authentication choice page
- User can manually trigger either method via MCP tools
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌──────────────┐
│ Kiro │────────▶│ MCP │────────▶│ Browser │────────▶│ Cognito │────────▶│ AWS Federate │
│ IDE │ │ Server │ │ (User) │ │ OAuth │ │ (Midway) │
└─────────┘ └──────────┘ └──────────┘ └─────────┘ └──────────────┘
│ │ │ │ │
│ │ │ │ │
│ 1. Tool Call │ │ │ │
│───────────────────▶│ │ │ │
│ │ │ │ │
│ │ 2. Check Midway │ │ │
│ │ Cookies │ │ │
│ │─────────────────────┘ │ │
│ │ │ │
│ │ 3. Check Cached Tokens │ │
│ │ (Hardware-Encrypted) │ │
│ │──────────────────────┘ │ │
│ │ │ │
│ │ 4. If No Valid Token: │ │
│ │ Start OAuth Flow │ │
│ │ (identity_provider=Amazon) │ │
│ │──────────────────────────────────────────▶ │
│ │ │ │
│ │ 5. Open Browser │ │
│ │─────────────────────────────────────────▶│ │
│ │ │ │
│ │ │ 6. Redirect to │
│ │ │ Amazon Login │
│ │ │─────────────────────▶│
│ │ │ │
│ │ │ 7. User Authenticates
│ │ │ with Amazon │
│ │ │◀─────────────────────│
│ │ │ │
│ │ │ 8. Authorization │
│ │ 9. Auth Code │ Code │
│ │◀──────────────────────────────────────────│◀─────────────────────│
│ │ (via localhost:8080/callback) │ │
│ │ │ │
│ │ 10. Exchange Code for Tokens │ │
│ │──────────────────────────────────────────▶│ │
│ │ │ │
│ │ 11. Access Token + Refresh Token │ │
│ │◀──────────────────────────────────────────│ │
│ │ │ │
│ │ 12. Encrypt & Cache Tokens │ │
│ │ (Hardware-Bound AES-256) │ │
│ │ with auth_method=federate │ │
│ │──────────────────────┘ │ │
│ │ │ │
│ │ 13. Call AgentCore Gateway │ │
│ │ with Bearer Token │ │
│ 14. Tool Result │──────────────────────────────────────────────────────────────────▶
│◀───────────────────│ │ │
│ │ │ │
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ Kiro │────────▶│ MCP │────────▶│ Browser │────────▶│ Cognito │
│ IDE │ │ Server │ │ (User) │ │ OAuth │
└─────────┘ └──────────┘ └──────────┘ └─────────┘
│ │ │ │
│ 1. Tool Call │ │ │
│───────────────────▶│ │ │
│ │ │ │
│ │ 2. No Midway │ │
│ │ Cookies Found │ │
│ │─────────────────────┘ │
│ │ │ │
│ │ 3. Show Auth │ │
│ │ Choice Page │ │
│ │─────────────────────▶ │
│ │ │ │
│ │ 4. User Selects │ │
│ │ "Cognito User" │ │
│ │◀───────────────────── │
│ │ │ │
│ │ 5. Start OAuth │ │
│ │ (no identity_ │ │
│ │ provider) │ │
│ │─────────────────────────────────────────▶│
│ │ │ │
│ │ 6. Cognito Login │ │
│ │ Page │ │
│ │─────────────────────▶ │
│ │ │ │
│ │ 7. User Enters │ │
│ │ Username/Pass │ │
│ │─────────────────────────────────────────▶│
│ │ │ │
│ │ 8. Auth Code │ │
│ │◀─────────────────────────────────────────│
│ │ │ │
│ │ 9. Exchange Code │ │
│ │─────────────────────────────────────────▶│
│ │ │ │
│ │ 10. Tokens │ │
│ │◀─────────────────────────────────────────│
│ │ │ │
│ │ 11. Extract User │ │
│ │ from ID Token │ │
│ │──────────────────────┘ │
│ │ │ │
│ │ 12. Cache Tokens │ │
│ │ with auth_ │ │
│ │ method=cognito │ │
│ │──────────────────────┘ │
When Midway is unavailable, users see an HTML page with two options:
┌─────────────────────────────────────────────┐
│ AgentCore MCP Proxy Authentication │
├─────────────────────────────────────────────┤
│ │
│ Choose your authentication method: │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Amazon Corporate Single Sign-On │ │
│ │ (Federate/Midway) │ │
│ └─────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Sign in with Cognito User │ │
│ │ (Username/Password) │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────┘- Kiro IDE sends a tool call request to the MCP server via stdio
- MCP Server checks for Midway cookies at
~/.midway/cookie - If Midway cookies exist:
- Extracts user alias from cookies (e.g.,
sbattoo) - Uses Federate authentication method
- Extracts user alias from cookies (e.g.,
- If no Midway session exists:
- Shows authentication choice page in browser
- User selects authentication method (Federate or Cognito User)
- For Cognito User: extracts username from ID token after authentication
- Server attempts to load cached tokens from
~/.agentcore_mcp_proxy/tokens.enc - Decrypts tokens using hardware-bound encryption key
- Checks if access token is still valid (not expired)
- If valid, proceeds directly to Step 13
-
MCP Server starts a temporary HTTP server on
localhost:8080- Automatically kills any process blocking port 8080
- Enables
SO_REUSEADDRfor socket reuse
-
Constructs Cognito authorization URL with parameters:
For Federate (Amazon SSO):
https://{COGNITO_DOMAIN}/oauth2/authorize? identity_provider=Amazon& redirect_uri=http://localhost:8080/callback& response_type=code& client_id={CLIENT_ID}& scope=openid+email+profileFor Cognito User Pool:
https://{COGNITO_DOMAIN}/oauth2/authorize? redirect_uri=http://localhost:8080/callback& response_type=code& client_id={CLIENT_ID}& scope=openid+email+profile -
Opens user's default browser to the authorization URL
-
Browser redirects to AWS Cognito
-
Authentication Path:
- Federate: Cognito redirects to Amazon Federate for corporate SSO (Midway-backed)
- Cognito User: Cognito shows username/password login page
-
User authenticates with their credentials
-
Cognito returns authorization to callback
-
Cognito redirects browser to
http://localhost:8080/callback?code=AUTH_CODE
- MCP Server receives authorization code via callback handler
- Displays success page in browser (auto-closes after 3 seconds)
- Exchanges authorization code for tokens via POST request:
POST https://{COGNITO_DOMAIN}/oauth2/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& client_id={CLIENT_ID}& code={AUTH_CODE}& redirect_uri=http://localhost:8080/callback
- Cognito returns token response:
{ "access_token": "eyJraWQiOiI...", "refresh_token": "eyJjdHkiOiJ...", "id_token": "eyJraWQiOiJ...", "token_type": "Bearer", "expires_in": 3600 }
- Server derives hardware-bound encryption key (see Hardware-Bound Token Encryption)
- For Federate authentication, extracts username from Midway cookies
- For Cognito User authentication, extracts username from ID token JWT
- Creates token data structure with authentication method:
or for Cognito User:
{ "sbattoo": { "access_token": "eyJraWQiOiI...", "refresh_token": "eyJjdHkiOiJ...", "id_token": "eyJraWQiOiI...", "expires_at": 1709123456.789, "auth_method": "federate" } }{ "username123": { "access_token": "eyJraWQiOiI...", "refresh_token": "eyJjdHkiOiJ...", "id_token": "eyJraWQiOiI...", "expires_at": 1709123456.789, "auth_method": "cognito_user" } } - Encrypts entire structure with AES-256 (Fernet)
- Saves to
~/.agentcore_mcp_proxy/tokens.encwith0600permissions - Shuts down temporary callback server
- MCP Server initializes Strands
MCPClientwith Bearer authentication - Calls AgentCore Gateway via HTTP with:
Note: The server uses the ID Token (not the Access Token) for gateway calls. The ID Token contains user identity claims (
POST {AGENTCORE_GATEWAY_URL} Authorization: Bearer {ID_TOKEN} Content-Type: application/json {tool_call_payload}
cognito:username,email) that the AgentCore Gateway uses for user identification and territory-based access control. - AgentCore Gateway validates token and executes tool
- Returns result to MCP Server
- MCP Server forwards result to Kiro IDE
When an access token expires (after ~1 hour):
┌──────────┐ ┌─────────┐
│ MCP │────────▶│ Cognito │
│ Server │ │ OAuth │
└──────────┘ └─────────┘
│ │
│ 1. Check Token │
│ Expiration │
│────────────────────┘
│ │
│ 2. POST /token │
│ grant_type= │
│ refresh_token │
│───────────────────▶│
│ │
│ 3. New Access │
│ Token │
│◀───────────────────│
│ │
│ 4. Update Cache │
│────────────────────┘
Refresh tokens are valid for 30 days. After 30 days, full OAuth flow is required.
Tokens are encrypted using a key derived from machine-specific hardware identifiers, making them non-portable across machines. This provides defense-in-depth security even if token files are stolen.
┌──────────────────┐
│ Hardware UUID │ ← Platform-specific, immutable identifier
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Username │ ← Additional entropy from OS user
└────────┬─────────┘
│
▼
┌──────────────────┐
│ PBKDF2-SHA256 │ ← 100,000 iterations
│ (100k rounds) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ 32-byte Key │ ← AES-256 encryption key
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Fernet Cipher │ ← AES-128 CBC + HMAC-SHA256
└──────────────────┘
- Source: IOPlatformUUID from IOPlatformExpertDevice
- Command:
ioreg -rd1 -c IOPlatformExpertDevice - Example:
12345678-ABCD-EFGH-IJKL-MNOPQRSTUVWX - Characteristics:
- Unique per Mac hardware
- Survives OS reinstalls
- Changes only with motherboard replacement
- Cannot be easily modified
- Primary Source:
/sys/class/dmi/id/product_uuid - Fallback Sources:
/etc/machine-id/var/lib/dbus/machine-id
- Characteristics:
- Unique per physical machine
- Survives OS reinstalls (product_uuid)
- Requires root for product_uuid access
- machine-id regenerates on OS reinstall
- Source: Machine GUID from WMI
- Command:
wmic csproduct get UUID - Characteristics:
- Unique per Windows installation
- Stored in registry
- Survives most system changes
- Changes with motherboard replacement
Fernet (Symmetric Encryption)
- Cipher: AES-128 in CBC mode
- Authentication: HMAC-SHA256
- Key Derivation: PBKDF2-SHA256 with 100,000 iterations
- IV: Randomly generated per encryption
- Format: Base64-encoded token with version, timestamp, IV, ciphertext, and HMAC
-
Encryption Key:
~/.agentcore_mcp_proxy/.key- Permissions:
0600(read/write for owner only) - Content: 32-byte base64-encoded key
- Verification: Checked against hardware UUID on every startup
- Permissions:
-
Encrypted Tokens:
~/.agentcore_mcp_proxy/tokens.enc- Permissions:
0600 - Content: Fernet-encrypted JSON
- Format: Multi-user token storage
- Permissions:
On every startup, the server:
- Derives key from current hardware UUID + username
- Compares with stored key in
.keyfile - If mismatch detected:
- Raises
Exceptionwith clear error message - Suggests deleting
~/.agentcore_mcp_proxy/and re-authenticating - Prevents token decryption
- Raises
This prevents:
- Using tokens copied from another machine
- Using tokens after hardware changes
- Using tokens after username changes
~/.agentcore_mcp_proxy/
├── .key # Hardware-derived encryption key (32 bytes)
└── tokens.enc # Encrypted token storage (Fernet format)
Decrypted Format (in memory only):
{
"user1": {
"access_token": "eyJraWQiOiI...",
"refresh_token": "eyJjdHkiOiJ...",
"id_token": "eyJraWQiOiJ...",
"expires_at": 1709123456.789
},
"user2": {
"access_token": "eyJraWQiOiI...",
"refresh_token": "eyJjdHkiOiJ...",
"id_token": "eyJraWQiOiJ...",
"expires_at": 1709127890.123
}
}┌─────────────────┐
│ Initial Auth │ ← OAuth flow with browser
└────────┬────────┘
│
▼
┌─────────────────┐
│ Access Token │ ← Valid for ~1 hour
│ (1 hour TTL) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Token Refresh │ ← Automatic refresh before expiration
│ (30 day TTL) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Re-Auth │ ← Full OAuth flow after 30 days
│ (30+ days) │
└─────────────────┘
-
Access Token Expiration (~1 hour):
- Server checks
expires_atbefore each API call - If expired, automatically refreshes using refresh token
- Updates cache with new access token
- Transparent to user
- Server checks
-
Refresh Token Expiration (30 days):
- Refresh attempt fails with 400/401 error
- Server initiates full OAuth flow
- Opens browser for re-authentication
- User must complete login again
-
Expiration Buffer:
- Tokens are considered expired 60 seconds before actual expiration
- Prevents race conditions and API call failures
- Ensures smooth user experience
Manual Cleanup:
# Delete all tokens
rm -rf ~/.agentcore_mcp_proxy/
# Delete specific user tokens (requires Python)
python -c "from agentcore_mcp_proxy.token_encryption import TokenEncryption; TokenEncryption().delete_tokens('username')"Automatic Cleanup:
- No automatic cleanup implemented
- Tokens remain until manually deleted or overwritten
- Expired tokens are automatically refreshed or re-authenticated
-
Token Theft from Disk
- ✅ Tokens are encrypted with AES-256
- ✅ Encryption key is hardware-bound
- ✅ Stolen files are useless on different machines
-
Token Theft from Memory
⚠️ Tokens are decrypted in memory during use⚠️ Memory dumps could expose tokens- ✅ Tokens expire after 1 hour (limited window)
-
Man-in-the-Middle (MITM) Attacks
- ✅ All communication uses HTTPS/TLS
- ✅ OAuth callback uses localhost (not exposed to network)
- ✅ Authorization code is single-use
-
Replay Attacks
- ✅ Access tokens expire after 1 hour
- ✅ Authorization codes are single-use
- ✅ Refresh tokens are rotated on use (Cognito behavior)
-
Credential Stuffing
- ✅ Uses Amazon SSO (Midway) with MFA
- ✅ No password storage in MCP server
- ✅ OAuth flow prevents password exposure
-
Unauthorized Access
- ✅ Tokens are user-specific
- ✅ AgentCore Gateway validates tokens
- ✅ Territory-based access control at gateway level
-
Compromised User Account
- ❌ If Amazon account is compromised, attacker can authenticate
- Mitigation: Use strong passwords and MFA on Amazon account
-
Malicious Code Execution
- ❌ If attacker has code execution, they can read tokens from memory
- Mitigation: Keep system and dependencies updated
-
Physical Access to Running System
- ❌ Attacker with physical access can extract tokens from memory
- Mitigation: Lock screen when away, use full-disk encryption
-
Social Engineering
- ❌ Attacker could trick user into authenticating on their behalf
- Mitigation: User education, verify OAuth URLs
-
Trusted Execution Environment
- User's machine is not compromised
- Operating system is trustworthy
- No malware or keyloggers present
-
Secure Network
- TLS/HTTPS connections are secure
- No MITM attacks on network layer
- DNS is not poisoned
-
Trusted Identity Provider
- AWS Cognito is secure
- Amazon SSO (Midway) is secure
- OAuth implementation is correct
Attack: Attacker copies ~/.agentcore_mcp_proxy/ directory to their machine
Defense:
- Encryption key is hardware-bound
- Key verification fails on different machine
- Token decryption raises exception
- Attacker cannot use tokens
Result: ✅ Attack prevented
Attack: Attacker intercepts network traffic
Defense:
- All OAuth communication uses HTTPS
- Callback server uses localhost (not exposed)
- Authorization codes are single-use
- TLS prevents eavesdropping
Result: ✅ Attack prevented
Attack: Malicious Kiro extension tries to steal tokens
Defense:
- Tokens are never exposed via stdio
- MCP server only returns tool results
- Token encryption key is not accessible
- Tokens are stored outside workspace
Result: ✅ Attack prevented (tokens not exposed)
Attack: Malicious package in dependency chain
Defense:
⚠️ Dependencies have access to memory⚠️ Could potentially extract tokens during use- ✅ Tokens expire after 1 hour (limited window)
- ✅ Regular dependency audits recommended
Result:
Attack: Malicious user with valid credentials
Defense:
- ✅ Territory-based access control at gateway
- ✅ Audit logs at AgentCore Gateway
- ✅ Token is tied to specific user identity
- ❌ Cannot prevent authorized user from accessing their data
Result:
-
Protect Your Authentication Credentials
- For Federate (Amazon SSO):
- Enable MFA on Amazon account
- Use strong, unique password
- Keep Midway session active with
mwinit - Don't share Midway cookies
- For Cognito User Pool:
- Use strong, unique password
- Enable MFA if available
- Don't share credentials
- Regularly review account activity
- For Federate (Amazon SSO):
-
Choose Appropriate Authentication Method
- Use Federate (Amazon SSO) when on corporate network with Midway access
- Use Cognito User Pool when Midway is unavailable or for non-corporate access
- Both methods provide equivalent access to resources
-
Secure Your Machine
- Use full-disk encryption
- Lock screen when away
- Keep OS and software updated
- Use antivirus/antimalware
-
Monitor Token Usage
- Check
~/.agentcore_mcp_proxy/permissions - Delete tokens when no longer needed
- Re-authenticate if suspicious activity
- Check
-
Network Security
- Use trusted networks
- Avoid public Wi-Fi for authentication
- Use VPN when on untrusted networks
-
Dependency Management
- Regularly audit dependencies
- Use
pip-auditorsafetyto check for vulnerabilities - Pin dependency versions
- Review dependency changes before updating
-
Secret Management
- Never log tokens or sensitive data
- Use environment variables for configuration
- Don't commit secrets to version control
-
Error Handling
- Don't expose tokens in error messages
- Sanitize logs before sharing
- Use generic error messages for auth failures
-
Code Review
- Review all changes to auth code
- Test token encryption/decryption
- Verify hardware UUID extraction
-
Security Testing
- Test token portability (should fail)
- Test token expiration handling
- Test refresh token rotation
- Test error scenarios
The server provides two MCP tools for manual authentication control:
Initiates Amazon Corporate SSO (Federate/Midway) authentication flow.
When to use:
- When you want to explicitly use corporate SSO
- When switching from Cognito User to Federate authentication
- When re-authenticating after token expiration
Requirements:
- Active Midway session (run
mwinitfirst) - Midway cookies at
~/.midway/cookie
Behavior:
- Opens browser to Cognito with
identity_provider=Amazon - Redirects to Amazon Federate for SSO
- Caches tokens with
auth_method=federate
Initiates Cognito User Pool (username/password) authentication flow.
When to use:
- When Midway is unavailable
- When you want to use username/password authentication
- When switching from Federate to Cognito User authentication
- When re-authenticating after token expiration
Requirements:
- Valid Cognito User Pool credentials
- No Midway session required
Behavior:
- Opens browser to Cognito login page
- User enters username and password
- Extracts username from ID token JWT
- Caches tokens with
auth_method=cognito_user
- Once authenticated, the method is stored in token metadata
- Tokens remain valid for 30 days regardless of method
- Users can switch methods by calling the respective tool
- Both methods provide equivalent access to resources
Cause: Tokens were copied from another machine, hardware changed, or username changed
Solution:
rm -rf ~/.agentcore_mcp_proxy/
# Re-authenticate on next MCP server startCause: Midway cookies expired or not present when using Federate authentication
Solution:
# Option 1: Authenticate with Midway
mwinit
# Then restart MCP server or call authenticate_with_federate tool
# Option 2: Use Cognito User authentication instead
# Call authenticate_with_cognito_user tool from KiroCause: Midway session not found, server offering authentication options
Solution:
- This is normal behavior when Midway is unavailable
- Choose "Amazon Corporate Single Sign-On" if you have Midway access
- Choose "Sign in with Cognito User" for username/password authentication
- Or use the MCP authentication tools directly
Cause: Another process is using port 8080
Solution:
- Server automatically kills blocking process
- If automatic cleanup fails, manually kill process:
# macOS/Linux lsof -ti :8080 | xargs kill -9 # Windows netstat -ano | findstr :8080 taskkill /F /PID <PID>
Cause: Refresh token expired (30 days)
Solution:
- Server automatically initiates OAuth flow
- Complete authentication in browser
- Tokens will be cached for another 30 days
Cause: Insufficient permissions or unsupported platform
Solution:
- macOS: Ensure
ioregcommand is available - Linux: Check permissions on
/sys/class/dmi/id/product_uuidsudo chmod +r /sys/class/dmi/id/product_uuid
- Windows: Ensure
wmiccommand is available
Cause: Token is invalid or expired
Solution:
- Delete cached tokens:
rm -rf ~/.agentcore_mcp_proxy/ - Restart MCP server
- Complete OAuth flow
- If issue persists, check AgentCore Gateway configuration
If you discover a security vulnerability, please report it to:
- Email: [security-contact-email]
- Internal: Create a security ticket in [internal-system]
Please do not:
- Open public GitHub issues for security vulnerabilities
- Share vulnerabilities publicly before they are fixed
- Attempt to exploit vulnerabilities in production systems
- Security patches are released as soon as possible
- Users are notified via [notification-channel]
- Update to latest version immediately when security patches are released
- MCP Server: Logs authentication events to stderr
- AgentCore Gateway: Logs all API calls with user identity
- Cognito: Logs all OAuth events in CloudWatch
- Tokens: Stored until manually deleted or overwritten
- Logs: Retained per organizational policy
- Audit Trails: Available at AgentCore Gateway level
- GDPR: User can delete tokens at any time
- SOC 2: Encryption at rest, audit logging, access control
- ISO 27001: Secure authentication, token lifecycle management
- Version: Fernet v1
- Cipher: AES-128-CBC
- MAC: HMAC-SHA256
- Key Derivation: PBKDF2-HMAC-SHA256
- IV: 128-bit random (per encryption)
- Timestamp: 64-bit (for TTL support)
base64(version || timestamp || iv || ciphertext || hmac)
- version: 1 byte (0x80)
- timestamp: 8 bytes (big-endian)
- iv: 16 bytes (random)
- ciphertext: variable length (AES-128-CBC)
- hmac: 32 bytes (HMAC-SHA256)
- Algorithm: PBKDF2-HMAC-SHA256
- Iterations: 100,000
- Salt: Hardware UUID (platform-specific)
- Key Length: 32 bytes (256 bits)
- Additional Entropy: Username
Document Version: 1.0
Last Updated: 2024
Maintained By: AgentCore MCP Proxy Team