OAuth2 Token Limits and Best Practices for AWS Cognito
When adding information to AWS Cognito ID and Access tokens, it’s crucial to understand the constraints and best practices to ensure efficient and secure authorization.
Token Size Limits¶
The primary limitation is the JWT (JSON Web Token) size. While there isn’t a single, hard-coded, universally published “maximum byte limit” for Cognito tokens that’s visible to the user, practical limits are imposed by:
HTTP Header Size: Tokens are typically passed in the Authorization HTTP header (Bearer
Browser Limits: While less common for server-to-server token usage, if a token is ever exposed client-side or stored in local storage/cookies, browsers also have limits on cookie size (~4KB per cookie, total ~50 cookies per domain) and local storage.
Performance: Larger tokens mean more data transferred over the network, more CPU cycles for signing/verification, and more memory consumption. This can negatively impact performance, especially at scale or on low-bandwidth connections.
Cognito’s Internal Limits: While not explicitly documented as a hard limit for your custom claims, Cognito itself has internal limits on the total size of claims it will process and include in a token. Very large custom claims could potentially hit these internal thresholds.
Recommendation: Aim to keep your tokens as lean as possible. A common practical guideline for the token itself (before Base64 encoding) might be to stay well under 4KB - 8KB to ensure broad compatibility across different network infrastructures. Smaller is always better for performance and reliability.
Types of Information to Include¶
The types of information you include in a token should align with its purpose: identity (for ID tokens) and authorization (for access tokens).
For ID Tokens (Identity Information):¶
Purpose: To inform the client application about the authenticated user’s identity.
- Good Candidates:
- User ID (sub claim): This is inherent.
- Username (cognito:username): Inherent.
- Email (email), Phone Number (phone_number), Name (name): Basic user profile information directly from Cognito attributes or federated identity providers.
- email_verified, phone_number_verified: Confirmation status.
- Cognito Groups (cognito:groups): User’s membership in Cognito groups.
- User Roles (if simple): If you have a few, stable, high-level roles (e.g., “admin”, “user”, “editor”), these can be suitable if they directly inform UI rendering or initial authorization checks.
- Avoid:
- Sensitive data: Anything that should never be exposed client-side or to unauthorized parties.
- Large arrays or complex objects: Keep the data flat and concise.
- Highly dynamic or frequently changing data: Tokens are static once issued. If data changes often, it’s better to fetch it on demand.
- Large lists of permissions: Use a separate authorization service for granular permissions.
For Access Tokens (Authorization Information):¶
Purpose: To inform the resource server about what the authenticated user is authorized to do. This is where most augmentation for backend authorization happens.
-
Good Candidates:
- User ID (sub claim): Essential for identifying the principal.
- Scopes (scope claim): These are crucial for OAuth 2.0. Custom scopes (e.g., api.example.com/read, resource:action) are ideal for defining access to your backend APIs.
- High-level roles or tiers: Similar to ID tokens, but specifically for access control (e.g., premium_user, corporate_client).
- Tenant ID: In a multi-tenant application, this is critical for ensuring data isolation.
- Subscription status/tier: If this directly dictates API access.
- Simplified authorization flags: Boolean flags or simple enums indicating access to broad features (e.g., can_manage_products: true).
-
Avoid:
- PII (Personally Identifiable Information): Access tokens are for authorization, not identity. While sub is typically present, avoid adding excessive PII unless absolutely necessary for authorization logic.
- Fine-grained permissions: Do not include a giant list of every single permission a user has (e.g., “can_edit_document_123”, “can_view_report_abc”). This leads to bloated tokens and poor maintainability.
- Session-specific or volatile data: Data that changes frequently during a user’s session should be stored and managed by the backend resource server, not in a token.
- Large embedded data structures: Keep claims concise.
General Recommendations¶
- Keep it Lean: Only include information that is absolutely necessary for the immediate authorization decision at the point of token consumption. If the data is rarely used or requires complex logic, retrieve it on demand from your backend.
- Prioritize Scopes for Authorization: For API access, define clear and concise custom scopes in Cognito Resource Servers. These scopes should represent actions or resources (e.g., orders/read, products/manage). Your Lambda can then conditionally add these scopes to the access token based on backend data.
- Use the Pre Token Generation Lambda Trigger: This is the designated and most secure way to inject custom claims from a backend.
- Validate Tokens on Your Backend: Always validate the JWT signature, issuer, audience, and expiration on your backend when receiving a token. This ensures the token’s integrity and authenticity.
-
Consider a Separate Authorization Service for Granular Access: For very complex or dynamic authorization requirements (e.g., attribute-based access control, resource-level permissions), tokens are not the ideal place to store all that logic. Instead:
-
Include a high-level role or group claim in the token.
Your backend service (or an external authorization service like AWS Verified Permissions) then uses this high-level claim, combined with real-time data from your backend database, to make fine-grained authorization decisions.
-
- Be Mindful of Token Lifetime: The longer a token’s lifetime, the longer potentially stale information within it persists. Balance security with user experience.
- Test Token Size: Actively monitor and test the size of your generated tokens, especially after adding new claims or updating the Lambda logic, to ensure they don’t exceed practical HTTP header limits.
- Don’t Rely Solely on Token Claims for Sensitive Data: While claims provide convenience, for highly sensitive operations, your backend should always perform a direct lookup in its own authoritative database to confirm permissions and data validity, rather than solely relying on potentially stale token claims.
By following these guidelines, you can effectively use Cognito’s token augmentation capabilities to streamline your authentication and authorization flows while maintaining performance and security.