Cognito fails to give valid credentials enabling Evidently commands

70 Views Asked by At

I have a CloudWatch Evidently project that I want to use to control feature flags on a public web UI. Since I can't embed durable AWS credentials on the client-side app, I understand Cognito is the recommended tool to allow unauthenticated access to AWS services.

I've created the Cognito Identity Pool, IAM role, and linkage between the two using this CloudFormation template:

    CognitoIdentityPool:
      Type: AWS::Cognito::IdentityPool
      Properties:
        AllowClassicFlow: True
        AllowUnauthenticatedIdentities: True
        IdentityPoolName: my-identity-pool
    UIIAMRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement: 
            - Effect: Allow
              Principal: 
                Federated: cognito-identity.amazonaws.com
              Action: sts:AssumeRoleWithWebIdentity
              Condition:
                StringEquals:
                  cognito-identity.amazonaws.com:aud: !Ref CognitoIdentityPool
                ForAnyValue:StringLike:
                  cognito-identity.amazonaws.com:amr: unauthenticated
        MaxSessionDuration: 3600
        Policies: 
          - PolicyName: root
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action:
                    - cognito-identity:GetCredentialsForIdentity
                    - evidently:ListFeatures
                    - evidently:EvaluateFeature
                  Resource: '*'
    IdentityPoolRoleAttachment:
      Type: AWS::Cognito::IdentityPoolRoleAttachment
      Properties:
        IdentityPoolId: !Ref CognitoIdentityPool
        Roles:
          unauthenticated: !GetAtt UIIAMRole.Arn 

The console UI shows everything created as expected: guest access is enabled on the Identity Pool, associated with the created IAM role, and the role has a policy including the correct Evidently rights.

But when I try to receive the credentials, it says the role does not have rights to use the Evidently feature:

$ aws cognito-identity get-id --identity-pool us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
{
    "IdentityId": "us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

$ aws cognito-identity get-credentials-for-identity --identity-id us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

{
    "IdentityId": "us-east-1:xxxxxxxxxxxxxxxxxxxxx",
    "Credentials": {
        "AccessKeyId": "xxxxxxxxxxxxxxxxxxxxx",
        "SecretKey": "xxxxxxxxxxxxxxxxxxxxx",
        "SessionToken": "xxxxxxxxxxxxxxxxxxxxx",
        "Expiration": "2023-11-30T11:03:53-05:00"
    }
}

$ export AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxx

$ export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxx

$ export AWS_SESSION_TOKEN=xxxxxxxxxxxxxxxxxxxxx

$ aws evidently list-features --project my-evidently-project

An error occurred (AccessDeniedException) when calling the ListFeatures operation: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/my-role-UIIAMRole-xxxxxxxxxx/CognitoIdentityCredentials is not authorized to perform: evidently:ListFeatures on resource: arn:aws:evidently:us-east-1:xxxxxxxxxxxx:project/my-evidently-project/feature/* 

I've also tried obtaining and using the credentials using the Node SDK:

const evidentlyClient = new EvidentlyClient({
    region: 'us-east-1',
    credentials: fromCognitoIdentityPool({
      identityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
      clientConfig: { region: 'us-east-1' }
    })
  })
  const featureResp = await evidentlyClient.send(
    new ListFeaturesCommand({
      project: 'arc-editorial-search-api'
    })
  )

which also fails with the same error.

Can anyone tell me what I'm doing wrong?

1

There are 1 best solutions below

0
On

I believe the approach in the question is using the enhanced flow, which has limited rights, per this document. The following commands work, which I think corresponds to the basic flow:

$ aws cognito-identity get-id --identity-pool us-east-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx
{
    "IdentityId": "us-east-1:xxxxxxxxx"
}

$ aws cognito-identity get-open-id-token --identity-id us-east-1:xxxxxxxxx
{
    "IdentityId": "us-east-1:xxxxxxxxx",
    "Token": "xxx..."
}

$ aws sts assume-role-with-web-identity --role-arn arn:aws:iam::xxxxxxxxxxxx:role/my-iam-role --role-session-name test --web-identity-token xxx...
{
    "Credentials": {
        "AccessKeyId": "xxxxxxxxxxxxxxx",
        "SecretAccessKey": "xxxxxxxxxxxxxxx",
        "SessionToken": "xxxxxxxxxxxxxxx...",
        "Expiration": "2023-12-01T15:56:05+00:00"
    },
    "SubjectFromWebIdentityToken": "us-east-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "AssumedRoleUser": {
        "AssumedRoleId": "xxxxxxxxxxxxxxxx:test",
        "Arn": "arn:aws:sts::xxxxxxxxxxxx:assumed-role/my-iam-role/test"
    },
    "Provider": "cognito-identity.amazonaws.com",
    "Audience": "us-east-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

$ AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx AWS_SESSION_TOKEN=xxx aws evidently list-features --project arc-editorial-search-api
{
    "features": [
        ...
    ]
}

and using the sdk:

import {
  CognitoIdentityClient,
  GetIdCommand,
  GetOpenIdTokenCommand
} from '@aws-sdk/client-cognito-identity'
import {
  STSClient,
  AssumeRoleWithWebIdentityCommand
} from '@aws-sdk/client-sts'
import {
  EvidentlyClient,
  ListFeaturesCommand
} from '@aws-sdk/client-evidently'
;(async () => {
  const cognitoClient = new CognitoIdentityClient({ region: 'us-east-1' })
  const { IdentityId } = await cognitoClient.send(
    new GetIdCommand({
      AccountId: 'xxxxxxxxxxxx',
      IdentityPoolId: 'us-east-1:xxxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx'
    })
  )
  const { Token: WebIdentityToken } = await cognitoClient.send(
    new GetOpenIdTokenCommand({
      IdentityId
    })
  )

  const stsClient = new STSClient({ region: 'us-east-1' })
  const {
    Credentials: {
      AccessKeyId: accessKeyId = '',
      SecretAccessKey: secretAccessKey = '',
      SessionToken: sessionToken = ''
    } = {}
  } = await stsClient.send(
    new AssumeRoleWithWebIdentityCommand({
      RoleArn:
        'arn:aws:iam::xxxxxxxxxxxxx:role/my-iam-role',
      RoleSessionName: 'test',
      WebIdentityToken
    })
  )
  if (accessKeyId && secretAccessKey && sessionToken) {
    const evidentlyClient = new EvidentlyClient({
      region: 'us-east-1',
      credentials: {
        accessKeyId,
        secretAccessKey,
        sessionToken
      }
    })
    const featureResp = await evidentlyClient.send(
      new ListFeaturesCommand({
        project: 'arc-editorial-search-api'
      })
    )
    console.log(featureResp)
  }
})()