Cognito
AWS Cognito user authentication and authorization for web and mobile applications
You are an expert in Amazon Cognito for implementing authentication, authorization, and user management in web and mobile applications. ## Key Points - **Skipping token verification on the backend** -- Trusting tokens without signature validation allows forged JWTs to access protected resources. Always verify against the Cognito JWKS. - **Adding too many custom claims via Lambda triggers** -- ID tokens have a 10,000-character limit. Excessive claims or large group lists overflow the token, causing silent authentication failures. - **Treating the user pool schema as flexible** -- Custom attributes cannot be removed or have their types changed after creation. Plan thoroughly before creating the pool. - **Use SRP (Secure Remote Password)** authentication flow for client-side apps. Never send passwords in plain text; let Amplify or the SDK handle SRP. - **Enable MFA** at minimum as optional, require it for sensitive applications. TOTP is preferred over SMS. - **Validate tokens on your backend**: Always verify the JWT signature, issuer, audience, and expiration. Cache the JWKS. - **Use Lambda triggers** for custom logic (validation, enrichment) rather than building it outside the auth flow. - **Set short access token expiration** (15-60 minutes) and use refresh tokens for long sessions. - **Use Cognito groups** for role-based access control. Groups appear in the `cognito:groups` claim in the ID token. - **Configure account recovery** to use verified email or phone, not admin-only recovery. - **Use a custom domain** for the hosted UI to maintain brand consistency. - **Client secret with public clients**: SPAs and mobile apps cannot securely store a client secret. Create the app client without `--generate-secret` for public clients.
skilldb get aws-services-skills/CognitoFull skill: 286 linesAWS Cognito — Cloud Services
You are an expert in Amazon Cognito for implementing authentication, authorization, and user management in web and mobile applications.
Core Philosophy
Authentication is the foundation of trust in any application. Cognito should handle the undifferentiated heavy lifting -- password hashing, token lifecycle, MFA, and social federation -- so your team can focus on business logic. Never roll your own password storage or JWT signing when Cognito provides a standards-compliant, managed alternative that is audited and maintained by AWS.
Design your user pool schema carefully because it cannot be changed later. Custom attributes, case sensitivity settings, and username configuration are immutable after pool creation. Invest time upfront to define what attributes you need, which ones are required versus optional, and whether usernames are emails, phone numbers, or arbitrary strings. Getting this wrong means creating a new user pool and migrating users.
Tokens are the perimeter of your API security. Always verify JWTs on your backend -- check the signature against the JWKS endpoint, validate the issuer, audience, and expiration claims. Cache the JWKS to avoid per-request network calls. Use short-lived access tokens (15-60 minutes) with refresh tokens for long sessions, and leverage Cognito groups for role-based access control rather than building a parallel permission system.
Anti-Patterns
- Storing or transmitting passwords in application code -- Use SRP (Secure Remote Password) via the Amplify SDK. The password should never cross the wire in plain text or be accessible to your backend.
- Skipping token verification on the backend -- Trusting tokens without signature validation allows forged JWTs to access protected resources. Always verify against the Cognito JWKS.
- Using a client secret with public clients -- SPAs and mobile apps cannot securely store secrets. Creating an app client with
--generate-secretfor a browser app leaks the secret to anyone who inspects the bundle. - Adding too many custom claims via Lambda triggers -- ID tokens have a 10,000-character limit. Excessive claims or large group lists overflow the token, causing silent authentication failures.
- Treating the user pool schema as flexible -- Custom attributes cannot be removed or have their types changed after creation. Plan thoroughly before creating the pool.
Overview
Cognito provides two main components: User Pools for authentication (sign-up, sign-in, token management, MFA) and Identity Pools for authorization (exchanging tokens for temporary AWS credentials). User Pools issue JWTs (ID token, access token, refresh token). Cognito supports OAuth 2.0/OIDC flows, social identity providers (Google, Apple, Facebook), SAML, and custom authentication challenges via Lambda triggers.
Setup & Configuration
Create a User Pool (AWS CLI)
aws cognito-idp create-user-pool \
--pool-name my-app-users \
--auto-verified-attributes email \
--username-attributes email \
--mfa-configuration OPTIONAL \
--policies '{
"PasswordPolicy": {
"MinimumLength": 12,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": false
}
}' \
--schema '[
{"Name": "email", "Required": true, "Mutable": true},
{"Name": "name", "Required": true, "Mutable": true}
]'
Create an App Client
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_abc123 \
--client-name my-app-web \
--explicit-auth-flows ALLOW_USER_SRP_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--supported-identity-providers COGNITO \
--callback-urls '["https://myapp.com/callback"]' \
--logout-urls '["https://myapp.com/logout"]' \
--allowed-o-auth-flows code \
--allowed-o-auth-scopes openid email profile \
--allowed-o-auth-flows-user-pool-client \
--generate-secret # Omit for public clients (SPAs, mobile)
SDK Setup (JavaScript - Amplify Auth)
import { Amplify } from "aws-amplify";
import { signUp, signIn, signOut, getCurrentUser, fetchAuthSession } from "aws-amplify/auth";
Amplify.configure({
Auth: {
Cognito: {
userPoolId: "us-east-1_abc123",
userPoolClientId: "1a2b3c4d5e6f7g8h9i0j",
loginWith: {
oauth: {
domain: "myapp.auth.us-east-1.amazoncognito.com",
scopes: ["openid", "email", "profile"],
redirectSignIn: ["https://myapp.com/callback"],
redirectSignOut: ["https://myapp.com/logout"],
responseType: "code",
},
},
},
},
});
SDK Setup (Python boto3 - Server-Side)
import boto3
import hmac, hashlib, base64
cognito = boto3.client("cognito-idp", region_name="us-east-1")
USER_POOL_ID = "us-east-1_abc123"
CLIENT_ID = "1a2b3c4d5e6f7g8h9i0j"
Core Patterns
Sign Up and Confirm
// Client-side sign up
const { userId } = await signUp({
username: "alice@example.com",
password: "SecureP@ss123",
options: {
userAttributes: {
email: "alice@example.com",
name: "Alice",
},
},
});
// Confirm with verification code sent to email
import { confirmSignUp } from "aws-amplify/auth";
await confirmSignUp({
username: "alice@example.com",
confirmationCode: "123456",
});
# Server-side sign up (admin)
cognito.admin_create_user(
UserPoolId=USER_POOL_ID,
Username="alice@example.com",
UserAttributes=[
{"Name": "email", "Value": "alice@example.com"},
{"Name": "email_verified", "Value": "true"},
{"Name": "name", "Value": "Alice"},
],
TemporaryPassword="TempP@ss123",
)
Sign In
const { isSignedIn, nextStep } = await signIn({
username: "alice@example.com",
password: "SecureP@ss123",
});
if (nextStep.signInStep === "CONFIRM_SIGN_IN_WITH_TOTP_CODE") {
// MFA required
import { confirmSignIn } from "aws-amplify/auth";
await confirmSignIn({ challengeResponse: "123456" });
}
# Server-side authentication
response = cognito.admin_initiate_auth(
UserPoolId=USER_POOL_ID,
ClientId=CLIENT_ID,
AuthFlow="ADMIN_USER_PASSWORD_AUTH",
AuthParameters={
"USERNAME": "alice@example.com",
"PASSWORD": "SecureP@ss123",
},
)
id_token = response["AuthenticationResult"]["IdToken"]
access_token = response["AuthenticationResult"]["AccessToken"]
refresh_token = response["AuthenticationResult"]["RefreshToken"]
Verify JWT Tokens (Backend API)
import jwt
import requests
# Fetch JWKS (cache this)
JWKS_URL = f"https://cognito-idp.us-east-1.amazonaws.com/{USER_POOL_ID}/.well-known/jwks.json"
jwks = requests.get(JWKS_URL).json()
def verify_token(token):
# Decode header to get key ID
header = jwt.get_unverified_header(token)
key = next(k for k in jwks["keys"] if k["kid"] == header["kid"])
# Verify and decode
payload = jwt.decode(
token,
jwt.algorithms.RSAAlgorithm.from_jwk(key),
algorithms=["RS256"],
audience=CLIENT_ID, # Use "aud" for id_token, "client_id" claim for access_token
issuer=f"https://cognito-idp.us-east-1.amazonaws.com/{USER_POOL_ID}",
)
return payload
Lambda Triggers
# Pre-sign-up trigger: auto-confirm users from a specific domain
aws cognito-idp update-user-pool \
--user-pool-id us-east-1_abc123 \
--lambda-config '{
"PreSignUp": "arn:aws:lambda:us-east-1:123456789012:function:pre-signup-validate",
"PostConfirmation": "arn:aws:lambda:us-east-1:123456789012:function:post-confirm-welcome",
"PreTokenGeneration": "arn:aws:lambda:us-east-1:123456789012:function:pre-token-add-claims"
}'
# Pre-sign-up Lambda: auto-confirm corporate emails
def handler(event, context):
email = event["request"]["userAttributes"].get("email", "")
if email.endswith("@mycompany.com"):
event["response"]["autoConfirmUser"] = True
event["response"]["autoVerifyEmail"] = True
return event
# Pre-token-generation Lambda: add custom claims
def handler(event, context):
event["response"]["claimsOverrideDetails"] = {
"claimsToAddOrOverride": {
"custom:role": "admin",
"custom:tenant_id": "tenant-001",
}
}
return event
Cognito with API Gateway Authorizer
# CloudFormation
CognitoAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoAuth
Type: COGNITO_USER_POOLS
RestApiId: !Ref Api
IdentitySource: method.request.header.Authorization
ProviderARNs:
- !GetAtt UserPool.Arn
Groups and Role-Based Access
aws cognito-idp create-group \
--user-pool-id us-east-1_abc123 \
--group-name admins \
--description "Administrator users" \
--role-arn arn:aws:iam::123456789012:role/AdminRole
aws cognito-idp admin-add-user-to-group \
--user-pool-id us-east-1_abc123 \
--username alice@example.com \
--group-name admins
Best Practices
- Use SRP (Secure Remote Password) authentication flow for client-side apps. Never send passwords in plain text; let Amplify or the SDK handle SRP.
- Enable MFA at minimum as optional, require it for sensitive applications. TOTP is preferred over SMS.
- Validate tokens on your backend: Always verify the JWT signature, issuer, audience, and expiration. Cache the JWKS.
- Use Lambda triggers for custom logic (validation, enrichment) rather than building it outside the auth flow.
- Set short access token expiration (15-60 minutes) and use refresh tokens for long sessions.
- Use Cognito groups for role-based access control. Groups appear in the
cognito:groupsclaim in the ID token. - Configure account recovery to use verified email or phone, not admin-only recovery.
- Use a custom domain for the hosted UI to maintain brand consistency.
Common Pitfalls
- Client secret with public clients: SPAs and mobile apps cannot securely store a client secret. Create the app client without
--generate-secretfor public clients. - User pool schema is immutable: Custom attributes cannot be removed or have their types changed after creation. Plan the schema carefully upfront.
- Token size limits: ID tokens have a 10,000-character limit. Adding too many custom claims or groups can exceed this.
- Refresh token rotation: Cognito does not rotate refresh tokens by default. Enable refresh token rotation for better security.
- Case sensitivity: By default, usernames are case-sensitive. Use
UsernameConfiguration: CaseSensitive: falseat pool creation time (cannot change later). - Rate limits: Cognito has request rate limits (e.g., 50 RPS for
AdminInitiateAuthby default). Request quota increases for production traffic. - Hosted UI limitations: The Cognito hosted UI has limited customization. For full control, build a custom UI using Amplify Auth SDK and handle the flows yourself.
Install this skill directly: skilldb add aws-services-skills
Related Skills
API Gateway
AWS API Gateway for building, deploying, and managing RESTful and WebSocket APIs
Cloudformation
AWS CloudFormation infrastructure-as-code for provisioning and managing AWS resources declaratively
Dynamodb
AWS DynamoDB NoSQL database for high-performance key-value and document workloads
Ecs Fargate
AWS ECS and Fargate for running containerized applications without managing servers
Rds Aurora
AWS RDS and Aurora managed relational databases for production SQL workloads
S3
AWS S3 object storage service for scalable, durable file and data storage