Missing checks in Cognito external IdP flows

Missing checks in Cognito external IdP flows

AWS Cognito User Pools can register more than one external identity provider in the same pool, including OIDC and SAML sources. Hosted UI and custom login pages both hand off into the same federated sign-in flow, so the real control point is not the front end. It sits in the trigger path.

That matters because the checks people usually care about, such as domain allowlists, tenant restrictions, JIT provisioning rules, attribute normalisation, and token shaping, only work if they run on the right trigger source. Put them in the wrong place and they become decoration. The login still succeeds, the user still lands in the pool, and the awkward bit starts later.

The ugly part is persistence. Once Cognito creates the federated user object, there is no automatic rollback. Cleaning it up becomes a manual job, which is a poor trade when the account should never have passed the gate in the first place.

The first federated login is the only gate that matters

AWS Cognito PreSignUp_ExternalProvider is the only trigger that runs before Cognito persists a federated user. That makes it the last clean blocking point for first-time sign-in from an external identity provider. If the allowlist or tenant check lives anywhere else, it is already late for prevention.

Checks placed in PostConfirmation or PostAuthentication do not stop creation. PostAuthentication fires after a successful login and can only observe or log. It cannot block the session. That is a useful place for detection, not for access control. Using it as a gate is how you end up with accounts that are both valid and wrong, which is a miserable combination.

PreAuthentication has the same weakness in this flow. It sits too far down the path to protect the first federated login. If the account has already been created by the time the code runs, the damage is done. Hosted login does not care that a later trigger found something objectionable. It already has a user.

Federated sign-in also skips several custom authentication hooks entirely. No custom challenge, no migrate user flow, no custom message, no custom sender trigger. If the control only exists in one of those paths, it simply will not run for this entry point. Cognito is tidy like that.

JIT provisioning is where this gets messy in practice. A first login can create a user based on external claims, then attribute mapping folds those claims into the pool profile. If the mapping or identity checks are too loose, the wrong account can be minted with just enough detail to look ordinary. That is usually when people notice.

A failed JIT step does not buy much comfort either. The operational gap after a bad first login can still leave the account around long enough to reach other features, including password reset or impersonation-style paths where the platform trusts the existence of the user rather than the origin of the login. That is not a theoretical problem, just an irritatingly durable one.

Where the check belongs in Cognito’s trigger path

The useful control is blunt: reject the federated sign-in in PreSignUp_ExternalProvider before Cognito stores the user. Domain allowlists, tenant binding, and any policy that decides whether the external identity is allowed into the pool belong there.

If the pool uses multiple identity providers, the check also needs to cope with different claim shapes from OIDC and SAML. Cognito merges claims according to attribute mapping, so the trigger should not trust that every IdP presents the same fields in the same way. Weak assumptions here tend to age badly.

InboundFederation_ExternalProvider matters for the same reason. If identity checks are only wired for the first-login path and skip the inbound federation path, later behaviour can drift away from the original policy. One path gets checked, another path gets a free pass, and the account lifecycle quietly splits in two.

Do not hang security logic off TokenGeneration_Authentication unless the authentication route really uses that path. SDK and admin auth can hit one source, hosted sign-in another, and the logic can vanish from the latter without warning. Cognito does not apologise for that sort of thing.

The practical boundary is simple enough. If a rule decides whether an external identity may become a user at all, it belongs in PreSignUp_ExternalProvider. If a rule only watches or reshapes an already accepted session, it belongs after creation and should be treated as audit, not control. That is the point where many multi-SSO setups quietly get it wrong.

Related posts

CVE-2026-3300 and WordPress admin account abuse

Everest Forms Pro CVE-2026-3300 is not a tidy bug, it is the sort of mess that turns form input into PHP and then acts surprised when attackers notice. I would not trust any site running the affected...

Review CI/CD install scripts for malicious code

Install scripts are the bit people wave through until they bite; in CI/CD, that means code runs under a trusted name before anyone has looked properly. I pin versions, record what landed, and treat...

Check npm and PyPI packages for compromise

A clean package yesterday means very little if a fresh update arrives with new maintainers, odd install scripts, or a version jump that does not make sense. I look for software supply chain trouble by...