aws-cdk: (cognito-idp): cannot use Cognito identity pool for role mappings

Description

The following role mapping will fail:

 const identityPool = new IdentityPool(this, "IdentityPool", {
      roleMappings: [
        {
          providerUrl: IdentityPoolProviderUrl.custom(userPool.userPoolProviderUrl),
          resolveAmbiguousRoles: false,
          useToken: true
        }
      ]
    })

The reason it will fail is because the internal logic is to map the provided URL as the corresponding key value, which is performed here.

The same function is achieved in Cloudformation by specifying the key separately from the provider url. See notes specified under the IdentityProvider field description.

Use Case

Referencing user pool from the same stack.

Proposed Solution

Allow the ability to optionally specify static key when creating a role mapping.

Other information

Possible (untested) workaround is to create role attachment with Cfn resource and manually assign an arbitrary key.

    const identityPool = new IdentityPool(this, "IdentityPool", {
      allowUnauthenticatedIdentities: false
    })

    new CfnIdentityPoolRoleAttachment(this, "RoleAttachment2", {
      identityPoolId: identityPool.identityPoolId,
      roleMappings: {
        cognito: { // 👈 manually specified key of "cognito"
          type: "Token",
          ambiguousRoleResolution: "Deny",
          identityProvider: userPool.userPoolProviderUrl
        }
      }
    }).node.addDependency(identityPool)

Acknowledge

  • I may be able to implement this feature request
  • This feature might incur a breaking change

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 3
  • Comments: 16 (14 by maintainers)

Commits related to this issue

Most upvoted comments

@rix0rrr said:

It looks like this is because there is no Cognito provider type in this enum:

https://github.com/aws/aws-cdk/blob/4eac4deb98411e921e5a2e6477185207b8588f75/packages/%40aws-cdk/aws-cognito-identitypool/lib/identitypool.ts#L103-L124

I don’t think it’s as simple as that.

IdentityPoolProviderType provides the USER_POOL constant, which I believe is for Cognito User Pools. IdentityPoolProviderUrl provides a userPool method that uses this constant. The documentation includes an example of IdentityPoolProviderUrl.userPool being used with a Cognito user pool provider URL.

I’m using the IdentityPoolProviderUrl.userPool method, and getting the same results.

 const identityPool = new IdentityPool(this, "IdentityPool", {
      roleMappings: [
        {
          providerUrl: IdentityPoolProviderUrl.userPool(userPool.userPoolProviderUrl),
          resolveAmbiguousRoles: false,
          useToken: true
        }
      ]
    })

(or more accurately, the Python equivalent, as I use Python). userPool is of course an earlier instantated aws_cognito.UserPool construct.

The specific error message we’re getting is similar to

Error: Resolution error: Resolution error: "${Token[TOKEN.209]}" is used as the key in a map so must resolve to a string, but it resolves to: {"Fn::GetAtt":["UserPool6BA7E5F2","ProviderURL"]}. Consider using "CfnJson" to delay resolution to deployment-time..

The problem being that as @michaeljfazio points out, the provider URL is used as the key of a map. However map keys do not allow for Tokens, only for string constants.

As @michaeljfazio also points out, the solution is to stop providing the identity provider using the role mapping keys, and instead use the IdentityProvider attribute of the role mapping object. The existing code already sets the attribute. So all that is needed is to stop using the provider URL as the map key.

What the map key should be instead of the provider URL is an open question. Maybe it could be an optional parameter to IdentityPoolRoleMapping that you are required to provide if the providerUrl is a Token.

Thanks @SamStephens, you’re spot on, the following did the trick:

    def _create_identity_pool(
        self, userpool: cognito.UserPool
    ) -> cognito_id_pool.IdentityPool:
        client = self.add_programmatic_client(
            "cognito-identity-pool-auth-provider",
            name="Identity Pool Authentication Provider",
        )

        userpool_provider = cognito_id_pool.UserPoolAuthenticationProvider(
            user_pool=userpool,
            user_pool_client=client,
        )

        return cognito_id_pool.IdentityPool(
            self,
            "identity_pool",
            identity_pool_name=f"{Stack.of(self).stack_name} IdentityPool",
            authentication_providers=cognito_id_pool.IdentityPoolAuthenticationProviders(
                user_pools=[userpool_provider],
            ),
            role_mappings=[
                cognito_id_pool.IdentityPoolRoleMapping(
                    provider_url=cognito_id_pool.IdentityPoolProviderUrl.user_pool(
                        f"cognito-idp.{Stack.of(userpool).region}.amazonaws.com/{userpool.user_pool_id}:{client.user_pool_client_id}"
                    ),
                    use_token=True,
                    mapping_key="userpool",
                )
            ],
        )

To smooth over this process, I’m proposing https://github.com/aws/aws-cdk/pull/21585

@SamStephens thanks a lot for fixing this! I ran into this issue tonight, saw this thread and when updated the CDK everything worked perfectly.

@alukach this would be a CDK change.

If you have a look at the documentation for a RoleMapping, it says:

IdentityProvider: Identifier for the identity provider for which the role is mapped. For example: graph.facebook.com or cognito-idp.us-east-1.amazonaws.com/us-east-1_abcdefghi:app_client_id (http://cognito-idp.us-east-1.amazonaws.com/us-east-1_abcdefghi:app_client_id). This is the identity provider that is used by the user for authentication.

If the identity provider property isn’t provided, the key of the entry in the RoleMappings map is used as the identity provider.

I read this as saying that if the IdentityProvider is provided, then the key of the Role Mapping can be any arbitrary string, it does not also have to be the identifier for the identity provider. I have not verified this experimentally, however.

You can see this from the template my CDK currently generates. I’ve hardcoded the identity provider temporarily to work around this issue. The role mapping generated for my attachment is:

    "RoleMappings": {
     "cognito-idp.us-west-2.amazonaws.com/us-west-2_pool_id:client_id": {
      "AmbiguousRoleResolution": "Deny",
      "IdentityProvider": "cognito-idp.us-west-2.amazonaws.com/us-west-2_pool_id:client_id",
      "Type": "Token"
     }
    },

See the identity provider is there as the key in RoleMappings, but also present as IdentityProvider.

I’m suggesting we provide a way for the CDK to generate this as:

    "RoleMappings": {
     "cognito": {
      "AmbiguousRoleResolution": "Deny",
      "IdentityProvider": "cognito-idp.us-west-2.amazonaws.com/us-west-2_pool_id:client_id",
      "Type": "Token"
     }
    },

Instead.