Amazon Cognito Guide
Implement authentication with Amazon Cognito: user pools, identity pools, hosted UI, social login, MFA, Lambda triggers, and API Gateway.
Prerequisites
- Basic understanding of authentication and OAuth 2.0
- AWS account with Cognito permissions
Introduction to Amazon Cognito
Amazon Cognito provides authentication, authorization, and user management for web and mobile applications. Instead of building your own user registration, login, password reset, and session management systems, Cognito handles all of this as a managed service. It supports millions of users, integrates with social identity providers (Google, Apple, Facebook, Amazon), enterprise identity providers (SAML 2.0, OIDC), and provides a hosted UI for login flows out of the box.
Cognito consists of two main components: User Pools for authentication (managing user accounts, passwords, MFA, and tokens) and Identity Pools (Federated Identities) for authorization (granting authenticated and unauthenticated users temporary AWS credentials to access AWS services like S3, DynamoDB, and API Gateway). These components can be used together or independently based on your application's needs.
This guide covers both User Pools and Identity Pools in depth: creating and configuring user pools, implementing social and enterprise login, customizing the hosted UI, configuring MFA and advanced security features, using Lambda triggers for custom workflows, setting up identity pools for AWS resource access, and integrating Cognito with API Gateway and other AWS services.
Cognito Pricing
Cognito User Pools pricing is based on monthly active users (MAUs). The first 50,000 MAUs are free when users sign in directly with a username and password. Beyond that, pricing starts at $0.0055 per MAU. Users who sign in through SAML or OIDC federation cost $0.015 per MAU. Advanced security features (adaptive authentication, compromised credential detection) add $0.050 per MAU. Identity Pools pricing is based on identity federation requests and Sync operations.
Creating a User Pool
A User Pool is a user directory that provides sign-up and sign-in functionality. When you create a User Pool, you define the sign-in options (email, phone, username), password policy, MFA requirements, user attributes (name, email, custom fields), and email/SMS messaging settings. These choices affect the user experience and cannot all be changed after creation, so plan carefully.
# Create a User Pool with email sign-in and strong password policy
aws cognito-idp create-user-pool \
--pool-name production-app-pool \
--auto-verified-attributes email \
--username-attributes email \
--mfa-configuration OPTIONAL \
--software-token-mfa-configuration '{"Enabled": true}' \
--policies '{
"PasswordPolicy": {
"MinimumLength": 12,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true,
"TemporaryPasswordValidityDays": 7
}
}' \
--schema '[
{"Name": "email", "Required": true, "Mutable": true, "AttributeDataType": "String"},
{"Name": "name", "Required": true, "Mutable": true, "AttributeDataType": "String"},
{"Name": "custom:company", "Mutable": true, "AttributeDataType": "String", "StringAttributeConstraints": {"MaxLength": "100"}},
{"Name": "custom:role", "Mutable": true, "AttributeDataType": "String", "StringAttributeConstraints": {"MaxLength": "50"}}
]' \
--email-configuration '{
"EmailSendingAccount": "DEVELOPER",
"SourceArn": "arn:aws:ses:us-east-1:123456789:identity/noreply@example.com",
"From": "MyApp <noreply@example.com>"
}' \
--account-recovery-setting '{
"RecoveryMechanisms": [
{"Priority": 1, "Name": "verified_email"},
{"Priority": 2, "Name": "verified_phone_number"}
]
}' \
--user-pool-add-ons '{"AdvancedSecurityMode": "ENFORCED"}' \
--admin-create-user-config '{
"AllowAdminCreateUserOnly": false
}'
# Get the User Pool ID
aws cognito-idp list-user-pools \
--max-results 10 \
--query 'UserPools[?Name==`production-app-pool`].{ID: Id, Name: Name, Created: CreationDate}' \
--output tableCreating an App Client
An app client is the entry point for your application to interact with the User Pool. Each app client has its own client ID and optional client secret, supported auth flows, token validity settings, and OAuth scopes. Create separate app clients for different application types (web app, mobile app, backend service).
# Create an app client for a web application (public client, no secret)
aws cognito-idp create-user-pool-client \
--user-pool-id <user-pool-id> \
--client-name web-app \
--generate-secret false \
--explicit-auth-flows '["ALLOW_USER_SRP_AUTH", "ALLOW_REFRESH_TOKEN_AUTH"]' \
--supported-identity-providers '["COGNITO", "Google", "SignInWithApple"]' \
--callback-urls '["https://app.example.com/auth/callback", "http://localhost:3000/auth/callback"]' \
--logout-urls '["https://app.example.com/logout", "http://localhost:3000/logout"]' \
--allowed-o-auth-flows '["code"]' \
--allowed-o-auth-scopes '["openid", "email", "profile"]' \
--allowed-o-auth-flows-user-pool-client \
--id-token-validity 1 \
--access-token-validity 1 \
--refresh-token-validity 30 \
--token-validity-units '{
"IdToken": "hours",
"AccessToken": "hours",
"RefreshToken": "days"
}' \
--prevent-user-existence-errors ENABLED
# Create an app client for a backend service (confidential client, with secret)
aws cognito-idp create-user-pool-client \
--user-pool-id <user-pool-id> \
--client-name backend-api \
--generate-secret \
--explicit-auth-flows '["ALLOW_ADMIN_USER_PASSWORD_AUTH", "ALLOW_REFRESH_TOKEN_AUTH"]' \
--allowed-o-auth-flows '["client_credentials"]' \
--allowed-o-auth-scopes '["api/read", "api/write"]' \
--allowed-o-auth-flows-user-pool-client
# Get the client ID and secret
aws cognito-idp describe-user-pool-client \
--user-pool-id <user-pool-id> \
--client-id <client-id> \
--query 'UserPoolClient.{ClientId: ClientId, ClientSecret: ClientSecret, Name: ClientName}' \
--output tableHosted UI and OAuth Configuration
Cognito provides a hosted UI for sign-up, sign-in, password reset, and MFA verification. The hosted UI is a pre-built, customizable web interface that handles all authentication flows without you writing any frontend authentication code. It supports OAuth 2.0 and OpenID Connect (OIDC) protocols, making it compatible with any standard OIDC client library.
# Create a domain for the hosted UI
aws cognito-idp create-user-pool-domain \
--user-pool-id <user-pool-id> \
--domain "myapp-production"
# Hosted UI URL: https://myapp-production.auth.us-east-1.amazoncognito.com
# Or use a custom domain
aws cognito-idp create-user-pool-domain \
--user-pool-id <user-pool-id> \
--domain "auth.example.com" \
--custom-domain-config '{
"CertificateArn": "arn:aws:acm:us-east-1:123456789:certificate/xxx"
}'
# Hosted UI login URL construction:
# https://myapp-production.auth.us-east-1.amazoncognito.com/login
# ?response_type=code
# &client_id=<client-id>
# &redirect_uri=https://app.example.com/auth/callback
# &scope=openid+email+profile
# Exchange authorization code for tokens
curl -X POST "https://myapp-production.auth.us-east-1.amazoncognito.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=<client-id>" \
-d "code=<authorization-code>" \
-d "redirect_uri=https://app.example.com/auth/callback"Social and Enterprise Identity Providers
Cognito supports federating with social identity providers (Google, Facebook, Apple, Amazon) and enterprise identity providers (any SAML 2.0 or OIDC-compliant provider like Okta, Azure AD, Auth0, or OneLogin). Federated users are automatically created in the User Pool on first sign-in, with their attributes mapped from the identity provider's token claims.
# Add Google as an identity provider
aws cognito-idp create-identity-provider \
--user-pool-id <user-pool-id> \
--provider-name Google \
--provider-type Google \
--provider-details '{
"client_id": "<google-client-id>",
"client_secret": "<google-client-secret>",
"authorize_scopes": "openid email profile"
}' \
--attribute-mapping '{
"email": "email",
"name": "name",
"picture": "picture",
"username": "sub"
}'
# Add Sign in with Apple
aws cognito-idp create-identity-provider \
--user-pool-id <user-pool-id> \
--provider-name SignInWithApple \
--provider-type SignInWithApple \
--provider-details '{
"client_id": "<apple-services-id>",
"team_id": "<apple-team-id>",
"key_id": "<apple-key-id>",
"private_key": "<apple-private-key>",
"authorize_scopes": "email name"
}' \
--attribute-mapping '{
"email": "email",
"name": "name",
"username": "sub"
}'
# Add a SAML enterprise identity provider (e.g., Okta)
aws cognito-idp create-identity-provider \
--user-pool-id <user-pool-id> \
--provider-name OktaSSO \
--provider-type SAML \
--provider-details '{
"MetadataURL": "https://mycompany.okta.com/app/xxx/sso/saml/metadata",
"IDPSignout": "true"
}' \
--attribute-mapping '{
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"custom:role": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
}'Attribute Mapping Strategy
Map identity provider attributes to Cognito user attributes carefully. The email attribute should always be mapped for account linking and recovery. Custom attributes (prefixed with custom:) are useful for storing provider-specific data like roles or department. If multiple identity providers might return different attribute names for the same concept, use Lambda triggers to normalize attributes during the sign-in flow.
Multi-Factor Authentication
Cognito supports MFA with TOTP-based software tokens (authenticator apps like Google Authenticator, Authy, or Microsoft Authenticator) and SMS text messages. You can require MFA for all users, make it optional (users choose to enable it), or require it only when adaptive authentication detects a risky sign-in.
# Enable MFA on the User Pool
aws cognito-idp set-user-pool-mfa-config \
--user-pool-id <user-pool-id> \
--mfa-configuration ON \
--software-token-mfa-configuration '{"Enabled": true}' \
--sms-mfa-configuration '{
"SmsAuthenticationMessage": "Your verification code is {####}",
"SmsConfiguration": {
"SnsCallerArn": "arn:aws:iam::123456789:role/cognito-sms-role",
"ExternalId": "cognito-sms-external-id"
}
}'
# Associate a TOTP software token for a user (admin flow)
aws cognito-idp admin-set-user-mfa-preference \
--user-pool-id <user-pool-id> \
--username user@example.com \
--software-token-mfa-settings '{
"Enabled": true,
"PreferredMfa": true
}'
# Configure adaptive authentication (risk-based MFA)
aws cognito-idp set-risk-configuration \
--user-pool-id <user-pool-id> \
--risk-exception-configuration '{
"BlockedIPRangeList": ["198.51.100.0/24"],
"SkippedIPRangeList": ["10.0.0.0/8"]
}' \
--account-takeover-risk-configuration '{
"NotifyConfiguration": {
"SourceArn": "arn:aws:ses:us-east-1:123456789:identity/noreply@example.com",
"From": "security@example.com",
"ReplyTo": "security@example.com",
"BlockEmail": {"Subject": "Account Blocked", "TextBody": "Your account has been blocked due to suspicious activity."},
"NoActionEmail": {"Subject": "Sign-in Alert", "TextBody": "We detected a sign-in from a new device."}
},
"Actions": {
"LowAction": {"Notify": true, "EventAction": "NO_ACTION"},
"MediumAction": {"Notify": true, "EventAction": "MFA_REQUIRED"},
"HighAction": {"Notify": true, "EventAction": "BLOCK"}
}
}'Lambda Triggers
Lambda triggers let you customize and extend Cognito's behavior at key points in the authentication lifecycle. You can validate sign-up data, modify token claims, migrate users from a legacy system, send custom messages, and implement custom authentication challenges. Each trigger type fires at a specific point in the flow and receives an event with context about the user and request.
Available Trigger Types
| Trigger | When It Fires | Common Use |
|---|---|---|
| Pre Sign-up | Before user registration | Validate email domain, auto-confirm users |
| Post Confirmation | After user confirms account | Create user record in DynamoDB, send welcome email |
| Pre Authentication | Before authentication | Block users, check IP allowlists |
| Post Authentication | After successful sign-in | Log sign-in events, update last login |
| Pre Token Generation | Before tokens are issued | Add custom claims, modify groups in token |
| User Migration | When user not found in pool | Migrate from legacy auth system |
| Custom Message | Before sending email/SMS | Customize verification messages |
# Lambda trigger: Pre Token Generation
# Adds custom claims to the ID and access tokens
import json
def handler(event, context):
# Add custom claims based on user attributes
user_attributes = event['request']['userAttributes']
email = user_attributes.get('email', '')
company = user_attributes.get('custom:company', '')
role = user_attributes.get('custom:role', 'user')
# Add claims to the ID token
event['response']['claimsOverrideDetails'] = {
'claimsToAddOrOverride': {
'company': company,
'role': role,
'permissions': json.dumps(get_permissions(role))
},
'groupsToOverride': event['request'].get('groupConfiguration', {}).get('groupsToOverride', [])
}
return event
def get_permissions(role):
"""Map roles to permissions."""
role_permissions = {
'admin': ['read', 'write', 'delete', 'manage-users'],
'editor': ['read', 'write'],
'viewer': ['read'],
'user': ['read']
}
return role_permissions.get(role, ['read'])# Associate Lambda triggers with the User Pool
aws cognito-idp update-user-pool \
--user-pool-id <user-pool-id> \
--lambda-config '{
"PreSignUp": "arn:aws:lambda:us-east-1:123456789:function:cognito-pre-signup",
"PostConfirmation": "arn:aws:lambda:us-east-1:123456789:function:cognito-post-confirm",
"PreAuthentication": "arn:aws:lambda:us-east-1:123456789:function:cognito-pre-auth",
"PostAuthentication": "arn:aws:lambda:us-east-1:123456789:function:cognito-post-auth",
"PreTokenGeneration": "arn:aws:lambda:us-east-1:123456789:function:cognito-pre-token",
"UserMigration": "arn:aws:lambda:us-east-1:123456789:function:cognito-user-migration"
}'Identity Pools (Federated Identities)
Identity Pools provide temporary AWS credentials to users authenticated by Cognito User Pools, social providers, or enterprise providers. This enables your frontend application to directly access AWS services like S3 (upload files), DynamoDB (read user data), or API Gateway (call APIs) without routing through a backend server. Identity Pools map authenticated and unauthenticated users to IAM roles with specific permissions.
# Create an Identity Pool
aws cognito-identity create-identity-pool \
--identity-pool-name production-app-identity \
--allow-unauthenticated-identities \
--cognito-identity-providers '[{
"ProviderName": "cognito-idp.us-east-1.amazonaws.com/<user-pool-id>",
"ClientId": "<client-id>",
"ServerSideTokenCheck": true
}]'
# Set the IAM roles for the Identity Pool
aws cognito-identity set-identity-pool-roles \
--identity-pool-id <identity-pool-id> \
--roles '{
"authenticated": "arn:aws:iam::123456789:role/Cognito_AuthenticatedRole",
"unauthenticated": "arn:aws:iam::123456789:role/Cognito_UnauthenticatedRole"
}' \
--role-mappings '{
"cognito-idp.us-east-1.amazonaws.com/<user-pool-id>:<client-id>": {
"Type": "Token",
"AmbiguousRoleResolution": "AuthenticatedRole"
}
}'Unauthenticated Access
Only enable unauthenticated identities if your application genuinely needs guest access to AWS resources (for example, uploading analytics events before sign-in). The unauthenticated IAM role should have extremely limited permissions. Never grant unauthenticated users write access to DynamoDB tables, S3 buckets with sensitive data, or any administrative APIs. Review unauthenticated permissions regularly to prevent privilege escalation.
API Gateway Integration
The most common Cognito integration pattern is using a User Pool as an authorizer for API Gateway. Clients authenticate with Cognito, receive a JWT token, and include it in API requests. API Gateway validates the token against the User Pool before forwarding the request to your backend Lambda function or HTTP endpoint.
# Create a Cognito authorizer for API Gateway
aws apigatewayv2 create-authorizer \
--api-id <api-id> \
--authorizer-type JWT \
--name cognito-jwt-auth \
--identity-source '$request.header.Authorization' \
--jwt-configuration '{
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/<user-pool-id>",
"Audience": ["<client-id>"]
}'
# Apply the authorizer to a route
aws apigatewayv2 update-route \
--api-id <api-id> \
--route-id <route-id> \
--authorization-type JWT \
--authorizer-id <authorizer-id> \
--authorization-scopes '["openid", "email"]'
# Test the API with a Cognito token
TOKEN=$(aws cognito-idp admin-initiate-auth \
--user-pool-id <user-pool-id> \
--client-id <client-id> \
--auth-flow ADMIN_USER_PASSWORD_AUTH \
--auth-parameters USERNAME=user@example.com,PASSWORD=P@ssword123! \
--query 'AuthenticationResult.IdToken' \
--output text)
curl -H "Authorization: Bearer $TOKEN" \
https://api-id.execute-api.us-east-1.amazonaws.com/prod/users/meUser Management Operations
Cognito provides administrative APIs for managing users programmatically. These are useful for admin panels, bulk user operations, and automated workflows.
# Create a user (admin)
aws cognito-idp admin-create-user \
--user-pool-id <user-pool-id> \
--username user@example.com \
--user-attributes Name=email,Value=user@example.com Name=name,Value="Jane Doe" Name=custom:company,Value="Acme Corp" \
--temporary-password "TempP@ss123!" \
--message-action SUPPRESS
# List users with a filter
aws cognito-idp list-users \
--user-pool-id <user-pool-id> \
--filter 'email ^= "admin"' \
--query 'Users[].{Username: Username, Email: Attributes[?Name==`email`].Value|[0], Status: UserStatus, Created: UserCreateDate}' \
--output table
# Disable a user
aws cognito-idp admin-disable-user \
--user-pool-id <user-pool-id> \
--username user@example.com
# Add a user to a group
aws cognito-idp admin-add-user-to-group \
--user-pool-id <user-pool-id> \
--username user@example.com \
--group-name admins
# Create a group
aws cognito-idp create-group \
--user-pool-id <user-pool-id> \
--group-name admins \
--description "Administrator users" \
--role-arn arn:aws:iam::123456789:role/AdminRole \
--precedence 1Amazon Cognito provides a complete identity solution that scales from startup to enterprise. Start with the hosted UI for rapid development, add social providers for consumer-facing apps, integrate SAML providers for enterprise SSO, and use Lambda triggers for custom business logic. Pair Cognito with API Gateway for secure API authorization and with Identity Pools for direct AWS resource access. Enable advanced security features for production to protect against credential stuffing, account takeover, and adaptive MFA.
Getting Started with AWSAWS RDS & Aurora Deep DiveAWS Network Firewall GuideKey Takeaways
- 1User Pools provide managed user directories with sign-up, sign-in, MFA, and token management.
- 2Identity Pools grant authenticated users temporary AWS credentials for direct service access.
- 3Lambda triggers customize authentication flows at key lifecycle points (pre-signup, post-auth, token generation).
- 4The hosted UI provides pre-built login pages with OAuth 2.0/OIDC protocol support.
Frequently Asked Questions
How much does Cognito cost?
Can I migrate existing users to Cognito?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.