Runbook: Cognito Dev Client Abuse¶
Alarm: tradai-dev-client-auth-failures-<env> (CloudWatch, namespace AWS/Cognito, metric SignInThrottles)
Scope: Non-prod only. The dev client (tradai-dev-client-<env>) uses USER_PASSWORD_AUTH and is the natural target for credential stuffing / password spray. The prod stack does not create this client.
⚠️ Pulumi drift warning. The dev client is owned by the
tradai-persistentPulumi stack. Any AWS-console oraws cognito-idpmutation against the client itself (auth flows, token validity, the client's existence) will be reverted on the nextpulumi up. Prefer the user-level mitigations below — they don't touch the client resource. If you genuinely need to disable the client, land a code change instead of mutating live infra.
When it fires¶
Cognito's adaptive throttling started rejecting InitiateAuth calls against the dev client for a sustained period (≥ 5 throttles per 5-minute window, two consecutive windows). That typically means:
- Credential-stuffing attempt against known dev usernames
- Password spray (one password across many usernames)
- Misconfigured client retrying with stale credentials in a loop
Response (preferred — no Pulumi drift)¶
User-level actions don't touch the Pulumi-managed client and so do not drift:
- Identify the targeted accounts. CloudTrail → filter
eventSource = cognito-idp.amazonaws.com,eventName = InitiateAuth,errorCode = NotAuthorizedExceptionfor the affected pool/client. Note source IPs and target usernames. - Lock the targeted users (immediate):
- Rotate credentials for any user that appears in the failed-auth list (permanent, no email loop):
- Block the source at WAF if it's an external IP. Edge stack owns WAF; add a temporary IP-set entry for 24h. File a follow-up to evaluate making the block permanent.
- Re-enable users after credential rotation:
Response (last resort — kill the dev client)¶
If the abuse continues and user-level mitigations are not enough, the only safe way to remove the USER_PASSWORD_AUTH surface is via a code PR, not a live AWS-console change:
- Open a PR that wraps
_create_dev_client()ininfra/persistent/modules/cognito.pybehind a feature flag, or removes the client outright for the affected env. - Get expedited review + merge.
- Run
just infra-up-persistent <env>to deploy.
Doing this via aws cognito-idp update-user-pool-client or the AWS console would silently revert on the next pulumi up from CI — re-opening the abuse window mid-incident.
Postman / curl auth flow (reference)¶
The dev client exists so Postman / curl can authenticate without SRP. The single canonical example lives here so the docstring, runbook, and PR description don't drift apart:
POST https://cognito-idp.<region>.amazonaws.com/
x-amz-target: AWSCognitoIdentityProviderService.InitiateAuth
Content-Type: application/x-amz-json-1.1
{
"AuthFlow": "USER_PASSWORD_AUTH",
"ClientId": "<cognito_dev_client_id from pulumi stack output>",
"AuthParameters": {
"USERNAME": "user@example.com",
"PASSWORD": "your-password"
}
}
Get the client ID and user pool ID¶
cd infra/persistent
pulumi stack output cognito_dev_client_id --stack <env>
pulumi stack output cognito_user_pool_id --stack <env>
Why this client exists¶
Frontend / API testing convenience — Postman and curl can authenticate with a plain InitiateAuth POST instead of running the SRP handshake. It is intentionally excluded from prod because USER_PASSWORD_AUTH puts the password into the request payload, raising credential-stuffing exposure. Prod callers must use the public PKCE client (tradai-api-client-prod).
Related¶
- Pulumi client:
infra/persistent/modules/cognito.py - Pulumi alarm (foundation stack, SNS-wired):
infra/foundation/modules/cognito_alarms.py - Canonical config:
reports/final-architecture/10-CANONICAL-CONFIG.md§4.2