Fixing Cognito Identity Pool 'No Authentication Provider Mapping' Error: Root Cause & Complete Solution
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 15 mins
TL;DR
- What broke: Your Cognito Identity Pool has no authentication provider mapped to it — no User Pool, no OIDC, no SAML. Every call to
GetIdorGetCredentialsForIdentityfails withNotAuthorizedExceptionor silently falls through to unauthenticated role (if enabled), granting wrong-scoped AWS credentials to callers. - How to fix it: Add the correct
CognitoIdentityProvidersblock (for User Pool federation) or the appropriateSupportedLoginProviders/OpenIdConnectProviderARNsentry matching your IdP. Verify the IAM role trust policy allowscognito-identity.amazonaws.comwith the correctamrcondition. - Use our Client-Side Sandbox above to auto-refactor this. Paste your broken Terraform or CloudFormation Identity Pool block and get a corrected config without sending your ARNs to a third-party server.
The Incident (What Does the Error Mean?)
Raw error surface — what you see in CloudWatch or your SDK:
NotAuthorizedException: Identity pool <us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx> does not have a provider configured for login provider <cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX>
or silently:
{
"__type": "NotAuthorizedException",
"message": "Access to Identity '<identity-id>' is forbidden."
}
Immediate consequence: cognitoidentity:GetId cannot resolve the incoming JWT issuer to a known provider entry in the pool. The identity broker has no mapping to evaluate, so it either rejects the token outright or — worse — falls back to the unauthenticated role if AllowUnauthenticatedIdentities is true. That second case is the silent production killer: your users get credentials, but they're the wrong credentials scoped to the unauth role, and you won't know until an IAM policy boundary blocks a real operation downstream.
The Attack Vector / Blast Radius
This is not just a configuration nuisance. The blast radius depends on your AllowUnauthenticatedIdentities setting:
Scenario A — Unauthenticated identities disabled (correct posture): All federated logins hard-fail. Full auth outage. Every user hitting GetCredentialsForIdentity gets a 403. Your React/mobile app cannot bootstrap AWS SDK clients. S3 uploads, API Gateway SigV4 calls, AppSync subscriptions — all dead.
Scenario B — Unauthenticated identities enabled (dangerous posture): Callers presenting a valid-but-unmapped JWT token silently receive unauthenticated role credentials. If that unauth IAM role is over-permissioned (a common misconfiguration), an attacker can present any token from any issuer, get AWS credentials, and pivot laterally. The Cognito Identity Pool becomes an open credential vending machine. This is a real attack path against misconfigured multi-tenant SaaS deployments.
The core problem: CognitoIdentityProviders (for User Pool) or SupportedLoginProviders (for social/OIDC) is either completely absent from the Identity Pool definition, or the ProviderName string does not exactly match the JWT iss claim — a single character mismatch causes the same failure.
How to Fix It
Basic Fix — Terraform
The cognito_identity_pool resource is missing its provider block entirely or has a wrong provider_name.
resource "aws_cognito_identity_pool" "main" {
identity_pool_name = "my-app-identity-pool"
- allow_unauthenticated_identities = true
+ allow_unauthenticated_identities = false
- # No provider block — this is the root cause
+ cognito_identity_providers {
+ client_id = aws_cognito_user_pool_client.app.id
+ provider_name = "cognito-idp.${var.aws_region}.amazonaws.com/${aws_cognito_user_pool.main.id}"
+ server_side_token_check = true
+ }
}
Critical: provider_name must exactly equal the iss claim in the Cognito User Pool JWT — format is cognito-idp.<region>.amazonaws.com/<UserPoolId>. No trailing slash. No https:// prefix for this field (unlike OIDC).
Enterprise Best Practice — Role Mapping with Token-Based Disambiguation
For multi-IdP pools, use Rules-based role mapping with token claim conditions instead of a single default authenticated role. This enforces least-privilege per identity source.
resource "aws_cognito_identity_pool_roles_attachment" "main" {
identity_pool_id = aws_cognito_identity_pool.main.id
roles = {
- "authenticated" = aws_iam_role.cognito_auth.arn
- "unauthenticated" = aws_iam_role.cognito_unauth.arn
+ "authenticated" = aws_iam_role.cognito_auth_default.arn
}
+ role_mapping {
+ identity_provider = "cognito-idp.${var.aws_region}.amazonaws.com/${aws_cognito_user_pool.main.id}:${aws_cognito_user_pool_client.app.id}"
+ ambiguous_role_resolution = "Deny"
+ type = "Rules"
+
+ mapping_rule {
+ claim = "cognito:groups"
+ match_type = "Contains"
+ value = "admin"
+ role_arn = aws_iam_role.cognito_admin_role.arn
+ }
+
+ mapping_rule {
+ claim = "cognito:groups"
+ match_type = "Contains"
+ value = "readonly"
+ role_arn = aws_iam_role.cognito_readonly_role.arn
+ }
+ }
}
Set ambiguous_role_resolution = "Deny" always. Never use AuthenticatedRole as the ambiguous fallback in production — it defeats the purpose of role mapping entirely.
Also verify the IAM trust policy on every role includes the amr condition:
{
"Effect": "Allow",
"Principal": { "Federated": "cognito-identity.amazonaws.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
- "cognito-identity.amazonaws.com:aud": "<identity-pool-id>"
+ "cognito-identity.amazonaws.com:aud": "${aws_cognito_identity_pool.main.id}"
},
+ "ForAnyValue:StringLike": {
+ "cognito-identity.amazonaws.com:amr": "authenticated"
+ }
}
}
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov — catch missing provider blocks at plan time:
Checkov rule CKV_AWS_212 flags Identity Pools with allow_unauthenticated_identities = true. Add to your pipeline:
checkov -d . --check CKV_AWS_212 --framework terraform
Write a custom check for missing cognito_identity_providers blocks:
# checkov custom check skeleton
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
from checkov.common.models.enums import CheckResult, CheckCategories
class CognitoIdentityPoolHasProvider(BaseResourceCheck):
def __init__(self):
super().__init__(
name="Ensure Cognito Identity Pool has at least one auth provider",
id="CKV_CUSTOM_COGNITO_001",
categories=[CheckCategories.IAM],
supported_resources=["aws_cognito_identity_pool"]
)
def scan_resource_conf(self, conf):
providers = conf.get("cognito_identity_providers", [])
oidc = conf.get("openid_connect_provider_arns", [])
saml = conf.get("saml_provider_arns", [])
if not any([providers, oidc, saml]):
return CheckResult.FAILED
return CheckResult.PASSED
2. OPA/Conftest policy for Terraform plan JSON:
package cognito.identity_pool
deny[msg] {
r := input.resource_changes[_]
r.type == "aws_cognito_identity_pool"
r.change.after.allow_unauthenticated_identities == true
msg := sprintf("Identity pool '%v' allows unauthenticated identities — prohibited in production.", [r.name])
}
deny[msg] {
r := input.resource_changes[_]
r.type == "aws_cognito_identity_pool"
count(r.change.after.cognito_identity_providers) == 0
count(r.change.after.openid_connect_provider_arns) == 0
count(r.change.after.saml_provider_arns) == 0
msg := sprintf("Identity pool '%v' has no authentication provider configured.", [r.name])
}
3. AWS Config Rule: Enable cognito-identity-pool-no-unauthenticated managed rule. Pair it with an SSM Automation remediation document that sets AllowUnauthenticatedIdentities to false on violation detection.
4. Integration test in your pipeline: After terraform apply in staging, run a canary that calls GetId with a valid test JWT and asserts 200 OK. A missing provider mapping will surface in < 2 seconds before it hits production.