Federated account linking: provider-name casing

Why Google or Apple sign-in failed with “SourceProviderName must match a Provider” on staging but not on prod, and how the PreSignUp linker tolerates it. Reflects live state as of 2026-09-25.

The symptom

A user who already has an email/password account signs in with Google or Apple and the app shows:

OAuthSignInException: PreSignUp failed with error SourceProviderName must match
a Provider that is configured for the User Pool.

It only happens on the link path, when the federated email matches an existing native account. Brand-new federated users never reach it.

The cause

The PreSignUp trigger (*-cognito-presignup-link) links the incoming social identity to the native account with AdminLinkProviderForUser. It reads the provider name from the prefix of the federated username Cognito hands it (Google_1094…). AdminLinkProviderForUser matches that name against the pool’s providers case-sensitively.

Whether the prefix keeps its casing depends on the pool, and the setting cannot be changed after the pool is created:

Pool UsernameConfiguration.CaseSensitive Prefix the trigger receives
phenom-prod (us-east-1_knEL7cqS3) not set (case-sensitive) Google_…, SignInWithApple_…
phenom-staging (us-east-1_n8gO6SbP6) false google_…, signinwithapple_…

A case-insensitive pool lowercases the whole username, prefix included. So the same Lambda code linked fine on prod and failed on staging. CloudTrail showed this directly:

2026-09-25T03:39:11Z  prod     src=Google    ok
2026-09-25T03:31:17Z  staging  src=google    InvalidParameterException

The fix

The linker resolves the parsed prefix against the pool’s configured provider names, ignoring case (canonicalizeProviderName in link-helpers.js, fed by a per-container cached ListIdentityProviders). On prod it is a no-op. If the lookup fails it falls back to the parsed name. The role carries read-only cognito-idp:ListIdentityProviders for this.

Both environments run the same linker code. Keep them byte-identical: diff -r the two pre-signup-link-provider/ directories under phenom-infra/environments/ before merging any change to either.

Diagnosing a recurrence

Check what provider name was actually sent, rather than inferring it:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AdminLinkProviderForUser \
  --max-results 20 --query 'Events[].CloudTrailEvent' --output json \
  | jq -r '.[]|fromjson|"\(.eventTime) \(.requestParameters.userPoolId) src=\(.requestParameters.sourceUser.providerName) err=\(.errorCode // "ok")"'

Then compare against the pool’s providers:

aws cognito-idp list-identity-providers --user-pool-id <pool> \
  --query 'Providers[].ProviderName'

Tracked in phenom-infra#321.