The problem
Acme Shop wants customer accounts. People sign up with their email address, sign in with a password and a second factor or with their Google account, reset passwords they forget, and stay signed in on their own browser for weeks. The orders API has to know who is calling without asking a session store on every request, and the photo feature should let the browser upload straight to S3 instead of streaming every byte through a server.
Built by hand, that means storing password hashes and choosing the hashing well, sending and checking verification codes, slowing down password guessing, running an OAuth 2.0 and OpenID Connect server with signing keys that rotate, and keeping up with each social provider's quirks. It also means deciding how a browser, which can't keep a secret, gets AWS credentials that can only write that one customer's photos.
How it works
Amazon Cognito is AWS's identity service for an application's own users, such as customers and partners. The people and workloads that run the AWS account belong to AWS IAM and IAM Identity Center instead. Cognito has two parts with similar names and different jobs.
User pools and identity pools
| User pool | Identity pool | |
|---|---|---|
| What it is | a user directory plus an OAuth 2.0 authorization server and OpenID Connect provider | a broker that turns proof of a sign-in into temporary AWS credentials |
| Takes | passwords, passkeys, one-time codes, or a sign-in at Google, Apple, Facebook, Amazon, a SAML 2.0 or an OIDC provider | an ID token from a user pool or another OIDC provider, a SAML assertion, a social provider's token, or nothing at all for guests |
| Gives | JSON Web Tokens: an ID token, an access token and a refresh token | a stable identity ID, and credentials for an IAM role issued by AWS STS |
| Price | per monthly active user, by feature plan | no charge (the AWS calls made with the credentials are billed as usual) |
| In Acme Shop | acme-customers |
acme-shop |
Each works without the other: many applications need only a user pool, and an identity pool can trust another identity provider. Amazon Cognito Sync, the identity pools' old service for syncing device data, stopped taking new customers in July 2026; AWS suggests AppSync and DynamoDB instead.
App clients and OAuth 2.0 flows
An app client is a user pool's settings for one application: the sign-in methods and OAuth grants it may use, the scopes it may request, its callback URLs, its token lifetimes, and whether it has a client secret.
- Public and confidential clients.
shop-web, a single-page app, is a public client with no secret, because anything shipped to a browser or a phone can be read. A confidential client runs on a server and authenticates at the token endpoint with a client secret (client_secret_basicorclient_secret_post); since January 2026 an app client can hold two secrets at once, so a secret can be rotated without downtime. - Authorization code with PKCE is the flow for people. The app sends the browser to
/oauth2/authorizewith acode_challenge(Cognito supports only theS256method), the user signs in, the browser comes back with a code, and the app exchanges the code and itscode_verifierat/oauth2/tokenfor an ID token, an access token and a refresh token. For public clients, Cognito's documentation recommends allowing this grant only, always with PKCE. - Client credentials is the flow for machines. A confidential app client asks for an access token carrying custom scopes defined on a resource server, at the token endpoint or, since July 2026, through the
GetClientTokenAPI, which needs no user pool domain. It is billed per token request, not per user (US East, October 2026: $2.25 per 1,000 requests for the first 250,000 in a month, falling to $1.125 per 1,000 above five million, with no charge for the app clients themselves). Its quota category,ClientAuthentication, allows 150 requests per second and can't be raised, so callers should cache each token until it expires. - Implicit returns the ID and access tokens directly in the redirect URL: no refresh token, no PKCE, and tokens exposed to browser history and to anything that can read the URL. RFC 9700, the IETF's current OAuth 2.0 security best practice, says not to use it; leave it switched off.
Apps that draw their own sign-in screens skip the browser redirect and call the user pool API through an AWS SDK: InitiateAuth with USER_AUTH (choice-based sign-in with a password, a passkey or a one-time code), USER_SRP_AUTH (the Secure Remote Password protocol, so the password never crosses the network), USER_PASSWORD_AUTH, or a custom challenge flow built from Lambda triggers.
Managed login and custom domains
A user pool domain serves the OAuth 2.0 endpoints (/oauth2/authorize, /oauth2/token, /oauth2/userInfo, /oauth2/revoke, /oauth2/idpresponse) and the hosted sign-in pages. It is either a prefix domain, such as acme.auth.us-east-1.amazoncognito.com, or a custom domain, such as auth.shop.example. A custom domain needs a public certificate in AWS Certificate Manager in US East (N. Virginia), because Cognito serves it through a CloudFront distribution, and its parent domain (shop.example) must resolve in DNS. With both kinds of domain, the OpenID Connect discovery document is published only on the custom one.
The pages come in two generations:
- Managed login (November 2024, Essentials and Plus plans) supports passkeys, one-time codes and localized pages, and has a visual branding editor for logos, background images, colours, light and dark modes and the layout of the form.
- The classic hosted UI, the only choice on the Lite plan, takes a logo and a fixed set of CSS properties.
Neither lets you change the wording on the pages; managed login can only switch them to another supported language. Neither handles profile changes, such as a new email address or MFA preferences, which your app builds on the user pool API, and managed login doesn't run custom authentication challenges. After a sign-in, a session cookie lets the same browser sign in again without credentials for one hour.
Federation
A user pool can hand sign-in to Google, Facebook, Login with Amazon, Sign in with Apple, any SAML 2.0 identity provider or any OpenID Connect provider, and then issues its own tokens as usual. Acme Shop registers an OAuth client with Google, gives it https://auth.shop.example/oauth2/idpresponse as the redirect URI and requests the scopes openid email profile; an attribute mapping copies Google's email into the profile that Cognito keeps for each federated user. Managed login shows a Continue with Google button, and an app can skip the page by adding identity_provider=Google to the authorize request.
The pre sign-up trigger also runs at a federated user's first sign-in, and since January 2026 an inbound federation trigger can reshape the provider's attributes on the way in. Federated users do MFA at their own provider; Cognito doesn't add a factor. Users from social providers are priced like the pool's own users, and users from SAML 2.0 or OIDC providers at a separate rate.
MFA and passkeys
MFA can be off, optional or required, with three kinds of second factor: a TOTP code from an authenticator app (Cognito accepts codes from 30 seconds either side, to allow for clock drift), an SMS message and, on Essentials or Plus, an email message. SMS goes out through Amazon SNS and email through Amazon SES, each billed separately.
Passkeys (Essentials or Plus) are WebAuthn credentials held by a phone, a laptop or a security key. In Cognito a passkey is a first factor, offered through choice-based sign-in and managed login, never a second step after a password. In a pool that requires MFA, a passkey sign-in counts as MFA only if the authenticator verifies the user (with a PIN or a biometric) and the pool's WebAuthn setting FactorConfiguration is MULTI_FACTOR_WITH_USER_VERIFICATION. That is how Acme Shop lets customers choose between a TOTP code and a passkey. A user can register up to 20 passkeys, only after signing in once; the relying party ID defaults to the custom domain (auth.shop.example), and changing it later means everyone registers again. Sign-in with a one-time code as the first factor can't be combined with required MFA.
Threat protection
On the Plus plan, threat protection (formerly advanced security features) compares passwords used at sign-in, sign-up and password changes against leaked credentials, scores the risk of each sign-in from signals such as an unfamiliar device or location (adaptive authentication), and can allow it, demand MFA or block it at each risk level. It starts in audit mode, which only logs and publishes metrics; AWS suggests two weeks or more there before switching on full-function mode. Leaked-password checks need the password itself, so they don't cover SRP or custom authentication, and threat protection doesn't apply to federated sign-in at all.
Lambda triggers
Triggers call your Lambda functions at fixed points in a user's life:
| Trigger | Runs | In Acme Shop |
|---|---|---|
| Pre sign-up | before a user is created: self-service sign-up, admin creation, a federated user's first sign-in | rejects disposable email domains |
| Post confirmation | after a user confirms the account | (a welcome email, a customer record) |
| Pre and post authentication | before and after a sign-in | (block a user, log the sign-in) |
| Pre token generation | before tokens are issued, also on refresh | adds tier: gold |
| Migrate user | when an unknown user signs in with a password or asks for a reset | (moving users from an old system) |
| Custom message, custom sender | when Cognito sends a code or a link | (own templates, another email or SMS provider) |
| Define, create and verify auth challenge | in a custom authentication flow | — |
| Inbound federation | when a federated user signs in | — |
Except for the custom sender triggers, Cognito calls the function synchronously and waits at most 5 seconds, a limit you can't change; it may retry a call that times out. A function that returns an error stops that step of the flow: managed login shows the error's message above the sign-in form, and the API returns it as PreSignUp failed with error …. Pre token generation has three event versions: V1_0 changes only the ID token and works on every plan, V2_0 also changes the access token's claims and scopes, and V3_0 does the same for machine-to-machine tokens; the last two need Essentials or Plus.
Tokens: lifetimes, refresh and revocation
Cognito signs its JWTs with RS256, with one key for ID tokens and another for access tokens, and publishes the public keys at https://cognito-idp.us-east-1.amazonaws.com/<user pool ID>/.well-known/jwks.json; the kid in a token's header names the key.
- The ID token describes the user (
sub,email,cognito:username, groups and custom claims such astier) and is meant for the app; itsaudis the app client ID. - The access token describes what the caller may do: its
scope(openid email orders/read), itscognito:groups, and the app client inclient_id. It carries anaudonly when the app asks for a resource binding (RFC 8707, supported since October 2025). - The refresh token is opaque to the app. It buys new ID and access tokens at the token endpoint (
grant_type=refresh_token) or through theGetTokensFromRefreshTokenAPI.
Each app client sets the lifetimes. By default ID and access tokens last 1 hour (configurable from 5 minutes to 1 day) and refresh tokens 30 days (60 minutes to 10 years). Because the managed login cookie keeps the browser signed in for an hour anyway, AWS advises against ID or access tokens shorter than an hour when you use managed login. Refresh token rotation (Essentials or Plus) returns a new refresh token with every refresh and invalidates the old one, after a grace period of up to 60 seconds if you set one; the new token still expires when the first one would have.
Revocation works on refresh tokens. RevokeToken and /oauth2/revoke end one refresh token and the access and ID tokens issued from it; GlobalSignOut and AdminUserGlobalSignOut end all of a user's tokens. Cognito's own APIs then refuse those access tokens, but the tokens themselves are still correctly signed and unexpired, so an API that verifies them by itself accepts them until exp. Revocation is on by default for new app clients, and it adds the jti and origin_jti claims to the tokens.
Groups and role mapping
Users can belong to groups, listed in cognito:groups in both tokens, which APIs and AppSync can authorize on. A group can also name an IAM role and a precedence: the ID token then lists the roles in cognito:roles and, when one group wins, names it in cognito:preferred_role, which an identity pool can use to choose the role.
Identity pools
In the enhanced flow, the app calls GetId with the ID token in a logins map, under the key cognito-idp.us-east-1.amazonaws.com/<user pool ID>, and gets an identity ID such as us-east-1:9f3c…, the same for that user on every device. It then calls GetCredentialsForIdentity: Cognito calls AssumeRoleWithWebIdentity itself and returns credentials that last one hour. In the basic flow the app calls GetOpenIdToken and then STS itself, which lets it pick the role and the session length, but the basic flow doesn't work in a pool with role mappings.
The identity pool chooses the role:
- Default roles. One role for authenticated identities (
photo-uploader) and, if guest access is on, one for unauthenticated identities. Acme Shop leaves guests off. Guests in the enhanced flow get a scope-down session policy that limits them to a list of services. - Rules and token-based mapping. Rules match claims in the token (for example
tierequalsgold) to roles, in order, and token-based mapping takescognito:preferred_rolefrom the user pool's groups. A setting decides what happens when nothing matches: the default role, or a denial. - Attributes for access control. The pool copies claims into session tags, so one role's policy can compare
aws:PrincipalTag/tierwith a tag on the resource. The role's trust policy must then also allowsts:TagSession, and users must not be able to edit the attributes that become tags: check every app client's write permissions.
Each role's trust policy names the federated principal cognito-identity.amazonaws.com and restricts cognito-identity.amazonaws.com:aud to your identity pool IDs; since August 2025 IAM refuses to save a new or changed trust policy for that principal without the condition. In the permissions policy, the variable ${cognito-identity.amazonaws.com:sub} is the identity ID (not the user pool's sub), which is how photo-uploader keeps each customer inside photos-prod/u/<identity ID>/. Because the browser calls S3 directly, the bucket also needs a CORS rule for the shop's origin.
Integrations: API Gateway, ALB, AppSync and Amplify
- API Gateway REST APIs have a
COGNITO_USER_POOLSauthorizer. On a method without OAuth scopes it treats the token as an ID token; on a method with scopes it requires an access token that carries one of them and answers 401 otherwise. Mapping templates can pass the claims to the backend as$context.authorizer.claims. - API Gateway HTTP APIs use a JWT authorizer, with the user pool as issuer and the app client ID as audience. It checks
kidand the signature against the issuer's JWKS (caching the keys for up to two hours), theniss,audorclient_id,exp,nbf,iatand the route's scopes, as described in JWT validation. - Application Load Balancers have an
authenticate-cognitolistener action: the load balancer runs the sign-in, keeps the session in a cookie and passes the user's claims to the targets inx-amzn-oidc-*headers, of which it signsx-amzn-oidc-data. - AppSync GraphQL APIs accept user pool tokens (
AMAZON_COGNITO_USER_POOLS) and can restrict fields to groups with@aws_auth(cognito_groups: [...]). - Amplify builds its Auth category on Cognito user pools and identity pools.
Pricing and quotas
Prices in US East (N. Virginia), October 2026:
| Plan | Price per monthly active user (MAU) | Free each month | Adds |
|---|---|---|---|
| Lite | $0.0055 from 10,001 to 100,000 MAUs, falling in tiers to $0.0025 above 10 million | 10,000 MAUs | sign-up and sign-in, social, SAML and OIDC federation, TOTP and SMS MFA, Lambda triggers, the classic hosted UI |
| Essentials (the default for new pools) | $0.015 | 10,000 MAUs | managed login and its branding editor, passkeys and one-time codes, email MFA, access token customization, refresh token rotation, password history |
| Plus | $0.02 | none | threat protection, and logs of user activity and risk that you can export |
A user counts as active in a month with any identity operation: a sign-up, a sign-in, a token refresh, a password change, an admin lookup. Users federated through SAML 2.0 or OIDC cost $0.015 each after the first 50, on every plan. User pools created on or before November 22, 2024 keep the old free tier of 50,000 MAUs on Lite. Add-ons cost extra: machine-to-machine tokens (per request, above), multi-Region replication ($0.0045 per MAU on Essentials, $0.006 on Plus) and higher request rates ($20 per extra request per second for a month or more, $45 for less, subject to approval). Identity pools cost nothing; SMS and email are billed by SNS and SES.
The request-rate quotas, also as of October 2026, apply per category and are shared by all the user pools of an account in one Region:
| Category | Covers, for example | Default (requests per second) | Adjustable |
|---|---|---|---|
UserAuthentication |
InitiateAuth, RespondToAuthChallenge, token refresh, managed login sign-in |
120 | yes |
UserCreation |
SignUp, ConfirmSignUp, AdminCreateUser |
50 | yes |
UserFederation |
sign-ins through a third-party provider | 25 | yes |
UserAccountRecovery |
ForgotPassword, ChangePassword |
30 | no |
UserRead |
GetUser, AdminGetUser |
120 | yes |
UserUpdate |
AdminUpdateUserAttributes, GlobalSignOut |
25 | no |
UserToken |
RevokeToken |
120 | yes |
ClientAuthentication |
client credentials grants, GetClientToken |
150 | no |
Each step of a managed login sign-in counts, so a sign-in with an MFA code is two requests. On top of the categories, one user can make at most 10 reads and 10 writes per second, and a user pool domain serves at most 500 requests per second (300 from one IP address, 300 for one app client). Identity pools have quotas per operation instead, such as 25 per second for GetId and 200 for GetCredentialsForIdentity. By default an account can have 1,000 user pools per Region, and a pool 1,000 app clients and 40 million users; all three can be raised.
Where it fits
- Solutions. Sign-in for customer-facing web and mobile apps on AWS; business-to-business SaaS where each customer company signs in through its own SAML 2.0 or OIDC provider; machine-to-machine tokens for internal and partner APIs; and browser or mobile apps that call AWS services directly, such as uploads to S3 or DynamoDB items keyed by the user, through identity pools.
- Patterns in this catalog. A user pool is an OpenID Connect provider and an OAuth 2.0 authorization server: it runs the authorization code flow with PKCE for apps and the client credentials flow for services. Its optional refresh token rotation retires each refresh token once it is used, as in refresh token rotation, though the documentation describes no reuse detection. With Google, Apple, SAML 2.0 or OIDC providers configured, it is the broker in federated identity, and the service provider in SAML single sign-on. APIs that trust its tokens do JWT validation, and an API gateway that checks them for every service is gateway offloading. An identity pool is a token exchange: an ID token goes in, AWS credentials for an IAM role come out. Scoped to one prefix with
${cognito-identity.amazonaws.com:sub}, those credentials work like a valet key to each customer's corner of a bucket. - Usual neighbours. API Gateway, Application Load Balancers, AppSync and Amplify in front; AWS Lambda for the triggers; AWS IAM and STS behind identity pools; Amazon S3 for direct uploads (the S3 page shows the presigned-URL alternative); Amazon SES and SNS for email and SMS; AWS WAF in front of managed login; CloudTrail for the audit trail; and Amazon Verified Permissions for authorization decisions based on Cognito tokens.
- Managed offerings. Cognito is itself a managed, Regional AWS service. The nearest equivalents are Microsoft Entra External ID (Azure AD B2C stopped selling to new customers on May 1, 2025) and Auth0 as services, and Keycloak as the common self-hosted choice.
When to use it
Choose Cognito when the application runs on AWS, its users are customers or partners, and the standard flows cover what you need: sign-in priced per active user, nothing to operate, and built-in integrations with API Gateway, AppSync, load balancers and, through identity pools, the rest of AWS. Look elsewhere when you need to control the sign-in pages' wording and every step of the flows, one identity system across several clouds, or the option to take password hashes with you later.
As of October 2026:
| Amazon Cognito | Keycloak | Auth0 (Okta) | Microsoft Entra External ID | |
|---|---|---|---|---|
| Runs as | AWS service, one Region per user pool | your servers: open source (Apache 2.0), 26.8.0 released on 1 October 2026 | SaaS | SaaS |
| Pricing | per monthly active user by plan; Essentials $0.015 after 10,000 free (US East) | no licence fee; you pay for servers, a database and operations | Free plan up to 25,000 MAUs, paid plans by MAU | first 50,000 MAUs free on the Basic tier, then per MAU, plus add-ons such as SMS and M2M |
| Sign-in pages | managed login: branding editor, no custom wording; or your own UI on the API | themes: FreeMarker templates, CSS and message bundles, as far as you want to go | Universal Login, with Liquid page templates once you have a custom domain | company branding: background, logo, layout, header, footer and custom text |
| Custom logic | Lambda triggers | Java service provider interfaces (SPIs) and configurable authentication flows | Actions: Node.js functions run at triggers | custom authentication extensions that call your REST API |
| AWS integration | built in: API Gateway, ALB, AppSync, Amplify, identity pools | standard OIDC and SAML | standard OIDC and SAML | standard OIDC and SAML |
Any OIDC provider in that table can also feed an identity pool or an IAM OIDC provider when its users need AWS credentials.
Trade-offs
- Priced per active user. The free tier makes small apps cheap, and the cost grows with users rather than servers. In US East, 200,000 monthly active users cost (200,000 − 10,000) × $0.015 = $2,850 a month on Essentials and 200,000 × $0.02 = $4,000 on Plus, before SMS, email and add-ons.
- Quotas shape the design. 120 sign-in requests per second, shared by every user pool in the account and Region, is plenty for most apps but not for a sale that starts at 9:00 sharp: plan the peak, watch
CallCountandThrottleCountper category in CloudWatch, and buy or request capacity ahead of time.UserUpdate(25 per second) andUserAccountRecovery(30) can't be raised, so keep admin calls off busy paths. - Pages you can style, not rewrite. Managed login changes look, not wording or flow; anything beyond that means your own UI on the user pool API, with the security work that comes with it.
- Easier to move in than out. In: the migrate user trigger moves users one by one as they sign in with their old passwords, and a CSV import can now carry password hashes (bcrypt, scrypt, Argon2id or PBKDF2-SHA256; since July 2026, on user pools that run on Cognito's newer infrastructure), which Cognito converts at each user's first sign-in. Out: no API returns password hashes, so a later move means password resets or a migration in the new system that checks passwords against Cognito as users sign in.
- Settings fixed at creation. Whether users sign in with an email address or a phone number as the username, which attributes are required, and every custom attribute you add can't be changed afterwards. Decide them before launch.
- Regional, with a limited replica. A user pool lives in one Region. Multi-Region replication (May 2026, Essentials and Plus, an add-on) adds one replica in a second Region, needs a multi-Region KMS key, and fails managed login over through a Route 53 health check. The replica signs users in and issues tokens, but can't create users, reset passwords or change profiles, doesn't support TOTP MFA, and serves federated users only if they signed in to the primary before. For Acme Shop, whose customers use TOTP, that matters.
- Revocation stops at the token's expiry. Revoking a refresh token or signing a user out globally doesn't recall access tokens that APIs verify by themselves; keep them short-lived, and check revocation-sensitive actions against Cognito.
Implementation notes
The resource server and the app client. The scope orders/read comes from a resource server; shop-web is a public client (no secret) that may use the authorization code grant, the pool's own users and Google, and choice-based sign-in for passkeys. us-east-1_EXAMPLE stands for the pool's ID:
aws cognito-idp create-resource-server \
--user-pool-id us-east-1_EXAMPLE \
--identifier orders --name "Orders API" \
--scopes '[{"ScopeName": "read", "ScopeDescription": "Read your own orders"}]'
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_EXAMPLE \
--client-name shop-web --no-generate-secret \
--allowed-o-auth-flows code --allowed-o-auth-flows-user-pool-client \
--allowed-o-auth-scopes openid email orders/read \
--supported-identity-providers COGNITO Google \
--callback-urls https://shop.example/callback \
--explicit-auth-flows ALLOW_USER_AUTH ALLOW_REFRESH_TOKEN_AUTH
Redeeming the code. After the redirect, shop-web posts the code and its verifier to the token endpoint. A public client sends its client_id in the body and no Authorization header:
POST /oauth2/token HTTP/1.1
Host: auth.shop.example
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&client_id=<shop-web client ID>&code=<code>&code_verifier=<code_verifier>&redirect_uri=https://shop.example/callback
The two triggers, as Node.js functions. The disposable domains are examples, and loyaltyTier stands for your own lookup. Pre token generation must be configured for event version V2_0 to change the access token:
const DISPOSABLE = new Set(['tempinbox.example', 'throwaway.example']);
export const preSignUp = async (event) => {
const domain = event.request.userAttributes.email.split('@').pop().toLowerCase();
if (DISPOSABLE.has(domain)) throw new Error('Please sign up with a permanent email address.');
return event;
};
export const preTokenGeneration = async (event) => {
const tier = await loyaltyTier(event.request.userAttributes.sub); // 'gold' for Ana
event.response = {
claimsAndScopeOverrideDetails: {
idTokenGeneration: { claimsToAddOrOverride: { tier } },
accessTokenGeneration: { claimsToAddOrOverride: { tier } },
},
};
return event;
};
The role behind the identity pool. The trust policy lets only signed-in identities of this identity pool assume photo-uploader (us-east-1:<identity pool ID> stands for the pool's ID):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "cognito-identity.amazonaws.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": { "cognito-identity.amazonaws.com:aud": "us-east-1:<identity pool ID>" },
"ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }
}
}]
}
and the permissions policy lets each customer write only under their own identity ID:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::photos-prod/u/${cognito-identity.amazonaws.com:sub}/*"
}]
}
In the browser, the AWS SDK for JavaScript's fromCognitoIdentityPool credential provider takes the identity pool ID and a logins map from cognito-idp.us-east-1.amazonaws.com/<user pool ID> to the ID token; an S3 client given that provider signs its requests with the credentials it fetches.
Habits that keep it manageable:
- Choose the username attributes, the required attributes and the custom attributes before launch; they can't be changed later.
- Allow only the authorization code grant with PKCE for public clients, and keep implicit off.
- Verify tokens with a maintained library (AWS recommends
aws-jwt-verifyfor Node.js) and checktoken_use,client_idoraud, and the scopes, not just the signature. - Keep access tokens short, and ask Cognito, not the token, when a revoked session must not act.
- On Plus, run threat protection in audit mode first and look at its risk scores before enforcing.
- Cache machine-to-machine tokens until they expire: each request costs money and counts against a quota that can't be raised.
- Load-test sign-in and sign-up before a launch, and watch
ThrottleCountper category.