Skip to content

Cognito Identity Pool rejects valid provider token for S3 upload authentication. Error: Please provide a valid public provider

1

Cognito Identity Pool "InvalidParameterException" with Developer Authenticated Identities - IAM Role Trust Issue?

I'm encountering a persistent "InvalidParameterException: Please provide a valid public provider" error when trying to retrieve AWS credentials in my frontend application using Cognito Identity Pool with developer authenticated identities. I've thoroughly investigated the configuration and am seeking assistance in identifying the root cause.

Setup:

I have a Cognito Identity Pool configured with a custom developer authentication provider My backend (NestJS) generates OpenID Connect (OIDC) tokens using GetOpenIdTokenForDeveloperIdentityCommand and returns the Identity ID and Token to the frontend. My frontend (Nuxt 3) uses @aws-sdk/client-cognito-identity to call GetCredentialsForIdentityCommand with the received Identity ID and Token. I have an IAM role associated with my Cognito Identity Pool that should grant access to an S3 bucket. Problem:

Despite the seemingly correct configuration, I receive the "InvalidParameterException" when calling GetCredentialsForIdentityCommand in my frontend.

Debugging Steps Taken:

Provider Name Verification: I've meticulously confirmed that the provider name is consistent across my Cognito Identity Pool configuration, backend code, and frontend code.

Token Verification: I've verified that my backend is generating valid JWT tokens using a JWT decoder (jwt.io). The aud (audience) claim in the token matches my Cognito Identity Pool ID.

Request Payload Inspection: I've inspected the request payload being sent to cognito-identity.eu-central-1.amazonaws.com in my browser's Network tab. The Logins object is correctly structured:

{
  "yomu.michi.dev.api": "THE_RAW_TOKEN_FROM_BACKEND"
}

The IdentityId and IdentityPoolId are also present and correct in the request.

IAM Role Trust Relationship Review: I've carefully reviewed the trust relationship of my IAM role. It is set to allow cognito-identity.amazonaws.com to assume the role and includes the necessary conditions:

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "YOUR_COGNITO_IDENTITY_POOL_ID"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}

I've also used a JSON validator to ensure there are no syntax errors or hidden characters in the trust policy.

Region and ID Consistency: I've ensured that the AWS region and Cognito Identity Pool ID are consistent across my Cognito configuration, IAM role, AWS CLI commands, and frontend code.

Question:

Is there something I'm missing in the interaction between Cognito, IAM, and my developer authenticated identities? Any insights or suggestions for further debugging would be greatly appreciated.

Additional Information:

I'm using the latest versions of the AWS SDKs in both my frontend and backend.

Backend code:

import { Injectable } from '@nestjs/common'
import { CognitoIdentityClient, GetOpenIdTokenForDeveloperIdentityCommand } from '@aws-sdk/client-cognito-identity'

@Injectable()
export class AwsS3Service {
  private readonly cognitoClient: CognitoIdentityClient
  private readonly COGNITO_IDENTITY_POOL_ID = process.env.AWS_COGNITO_IDENTITY_POOL_ID
  private readonly COGNITO_DEVELOPER_PROVIDER_NAME = process.env.AWS_COGNITO_DEVELOPER_PROVIDER_NAME

  constructor() {
    this.cognitoClient = new CognitoIdentityClient({
      region: process.env.AWS_REGION,
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
      }
    })
  }

  async getOpenIdToken(userId?: string) {
    const providerName = userId || 'readerAppMvp'
    console.log('providerName', providerName)
    console.log('cognito developer provider', this.COGNITO_DEVELOPER_PROVIDER_NAME)

    const command = new GetOpenIdTokenForDeveloperIdentityCommand({
      IdentityPoolId: this.COGNITO_IDENTITY_POOL_ID,
      Logins: {
        [this.COGNITO_DEVELOPER_PROVIDER_NAME]: providerName
      },
      TokenDuration: 3600 // 1 hour
    })

    const response = await this.cognitoClient.send(command)

    if (!response.IdentityId || !response.Token) {
      throw new Error('Failed to get Cognito credentials')
    }

    return {
      identityId: response.IdentityId,
      token: response.Token
    }
  }
}

frontend Code:

<template>
  <div>
    <file-pond
      ref="pond"
      name="filepond"
      label-idle="Drag & Drop your EPUB files or <span class='filepond--label-action'>Browse</span>"
      allow-multiple="false"
      accepted-file-types="application/epub+zip"
      :server="serverOptions"
      :credits="false"
    />
  </div>
</template>

<script setup>
import { CognitoIdentityClient, GetCredentialsForIdentityCommand } from '@aws-sdk/client-cognito-identity'
import { S3Client } from '@aws-sdk/client-s3'
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
import 'filepond/dist/filepond.min.css'
import vueFilePond from 'vue-filepond'

const config = useRuntimeConfig()
const FilePond = vueFilePond(FilePondPluginFileValidateType)
// Server options for file upload
const serverOptions = {
  url: '/api/upload',
  process: {
    url: './process',
    method: 'POST',
    withCredentials: false,
    headers: {},
    timeout: 7000,
    onload: (response) => response.key,
    onerror: (response) => response.data,
    ondata: (formData) => {
      formData.append('hello', 'world')
      return formData
    }
  }
}

onMounted(async () => {
  console.log(config.public)
  const credentials = await getAwsCognitoCredentials()
  console.log('getting credentials', credentials)
  // const s3Service = await createS3Service()
  // console.log(s3Service.config.credentials)
})

async function getAwsCognitoCredentials() {
  try {
    const { identityId, token } = await $fetch('/api/auth/upload-credentials')
    console.log('Identity ID:', identityId)
    console.log('Token received:', token)
    console.log('AWS Region:', config.public.awsRegion)
    console.log('AWS Cognito Developer Provider Name:', config.public.awsCognitoDeveloperProviderName)
    console.log('AWS Cognito Identity Pool ID:', config.public.awsCognitoIdentityPoolId)

    const params = {
      IdentityId: identityId,
      IdentityPoolId: config.public.awsCognitoIdentityPoolId,
      Logins: {
        [config.public.awsCognitoDeveloperProviderName]: token
      }
    }

    console.log('Logins:', params)

    const cognitoIdentity = new CognitoIdentityClient({
      region: config.public.awsRegion
    })

    const credentialsResponse = await cognitoIdentity.send(new GetCredentialsForIdentityCommand(params))
    console.log('Cognito credentials received:', credentialsResponse.Credentials)
    return credentialsResponse.Credentials
  } catch (error) {
    console.error('Error fetching AWS credentials:', error)
    console.log('Error:', error.message)
    throw error
  }
}

async function createS3Service() {
  const cognitoCredentials = await getAwsCognitoCredentials()
  if (
    !cognitoCredentials ||
    !cognitoCredentials.AccessKeyId ||
    !cognitoCredentials.SecretKey ||
    !cognitoCredentials.SessionToken
  ) {
    throw new Error('AWS credentials not available')
  }
  return new S3Client({
    region: config.public.awsRegion,
    credentials: {
      accessKeyId: cognitoCredentials.AccessKeyId,
      secretAccessKey: cognitoCredentials.SecretKey,
      sessionToken: cognitoCredentials.SessionToken
    }
  })
}
</script>

1 Answer
-1

Based on the information provided, it appears that the issue lies in the interaction between your Cognito Identity Pool configuration and the developer authenticated identities. Here are some potential causes and solutions to consider:

  1. Provider Name Mismatch: Ensure that the provider name used in your backend (COGNITO_DEVELOPER_PROVIDER_NAME) exactly matches the one configured in your Cognito Identity Pool. Even a slight mismatch can cause the "InvalidParameterException".

  2. Token Format: The token generated by GetOpenIdTokenForDeveloperIdentity should be used directly in the Logins map. Make sure you're not modifying or encoding this token before sending it to the frontend.

  3. IAM Role Trust Relationship: While your trust policy looks correct, double-check that the Cognito Identity Pool ID in the policy exactly matches your actual pool ID. Also, ensure that the role has the necessary permissions to access S3.

  4. Cognito Identity Pool Configuration: Verify that your identity pool is correctly set up to use developer authenticated identities. In the Cognito console, under the identity pool settings, ensure that "Enable access to unauthenticated identities" is turned off and "Authentication providers" includes your custom developer provider.

  5. Region Consistency: Confirm that you're using the same AWS region across all services and SDK clients (Cognito, S3, IAM).

  6. Token Expiration: Ensure that the token you're using hasn't expired. The GetOpenIdTokenForDeveloperIdentity command allows you to set a TokenDuration, which you've set to 1 hour. Make sure you're using the token within this timeframe.

  7. Credentials Caching: The AWS SDK might be caching invalid credentials. Try clearing any cached credentials in your application.

  8. Error Handling: Implement more detailed error logging in your frontend code. Instead of just logging the error message, log the full error object to get more context about the failure.

  9. AWS SDK Versions: Ensure you're using compatible versions of the AWS SDK across your backend and frontend.

If none of these solve the issue, you may need to use AWS CloudTrail to get more detailed logs about the API calls being made to Cognito. This can help identify any discrepancies between what you're sending and what Cognito expects.

Lastly, consider simplifying your authentication flow for testing. Try using a public provider (like Google or Facebook) temporarily to isolate whether the issue is specific to your developer authenticated identities setup.

Sources
Error while getting AWS Credentials through "GetId" -> "GetCredentialsForIdentityCommand" using Google id_token | AWS re:Post
Get AWS Credentials through "GetId" -> "GetCredentialsForIdentityCommand" using Google id_token | AWS re:Post
How is the right authentication flow using Cognito? | AWS re:Post

answered 2 years ago

    1. Yes, i made sure it is the exact same provider name everywhere. If I change it in the frontend and put in a random string I get a completely different error.: Error: Invalid login token. Can't pass in a Cognito token.

    2. Yes, the token is given to the frontend as is and not modified. I believe there would have been a different error in case something was wrong here as well

    1. Yes, I verified this multiple times! the role associated has following permissions: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::redacted-bucket-name/*" } ] }

    2. Guest access is deactivated, authenticated access is activated and the above-mentioned rule with permissions and trust relationships is being used. The custom developer provider is exactly the one being used in the frontend and backend

    1. using the correct region everywhere
    2. As mentioned above its set
    3. Makes no sense.
    4. Already logging everything as pasted above
    1. versions used: backend:

    "dependencies": { "@aws-sdk/client-cognito-identity": "^3.726.1", "@aws-sdk/client-s3": "^3.726.1", "@aws-sdk/client-sts": "^3.726.1", } frontend:

    "dependencies": { "@aws-sdk/client-cognito-identity": "3.731.1", "@aws-sdk/client-s3": "3.731.1", },

    updated aws packages to 3.731.1

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.