-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(cognito-idp): cannot use Cognito identity pool for role mappings #19222
Comments
It looks like this is because there is no Cognito provider type in this enum: aws-cdk/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts Lines 103 to 124 in 4eac4de
Feel free to submit it. |
@rix0rrr said:
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.
(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
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. |
FYI, the issue is currently incorrectly labelled @aws-cdk/aws-iam. |
The possible workaround that @michaeljfazio provides does not work, because an IdentityPool creates a DefaultRoleAttachment, and the CfnIdentityPoolRoleAttachment suggested conflicts with the existing DefaultRoleAttachment. |
@SamStephens Thanks for your comments. I have been hitting my head against this (also using Python). Your description of the issue is inline with my thinking. However, I get a bit lost when you describe the solution.
Are you suggesting a change to CDK, or is this a change to CloudFormation itself? Reading through the
At the end of the day, CDK will need to populate the RoleMappings on the CloudFormation Template. To create the Cognito IDP key, CDK is going to need to resolve the tokens at time of synthesis due to the fact that it's a CloudFormation restriction that keys can't be composed of tokens, correct? As a workaround, I was trying to apply some ideas from #12988 (i.e. leaning on |
@alukach this would be a CDK change. If you have a look at the documentation for a RoleMapping, it says:
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:
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:
Instead. |
@SamStephens Thanks for the clarification.
This does indeed track with the example in the IdentityPoolRoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
"authenticated": !GetAtt AuthenticatedRole.Arn
"unauthenticated": !GetAtt UnAuthenticatedRole.Arn
RoleMappings:
"graph.facebook.com":
IdentityProvider: "graph.facebook.com"
AmbiguousRoleResolution: Deny
Type: Rules
RulesConfiguration:
Rules:
- Claim: "sub"
MatchType: "Equals"
RoleARN: !GetAtt AuthenticatedRole.Arn
Value: "goodvalue"
"userpool1":
IdentityProvider: !Ref CognitoUserPool
AmbiguousRoleResolution: Deny
Type: Rules
RulesConfiguration:
Rules:
- Claim: "sub"
MatchType: "Equals"
RoleARN: !GetAtt AuthenticatedRole.Arn
Value: "goodvalue" I will do my best to find some time tomorrow to play around with the CDK lib and see if I can make such a change in the lib (not sure how to cross-compile to Python locally, so we'll see how this goes...) |
@alukach Awesome; I'm not working this week, but am back next week and would have time to look at this. I think the main challenge is working out how the RoleMappings keys should be defined now they're no longer always the IdentityProvider. I think we need to be careful that this scheme is resilient, that a given identity provider always retains the same key. For a concrete example of what I mean, a naive solution would be just to make all the keys be numbered incrementally:
The problem being that if you add a new role mapping to the beginning of the role mapping list, the keys of all the existing mappings will change:
My take is that the cleanest way to deal with this is to not make the CDK generate the keys; to have them provided by users. I'd do this by adding a roleMappingName optional string to IdentityPoolRoleMapping. I'd then update the role mapping logic to use that name if it's provided. Change the map setting code to be:
A nice side effect of doing it this way is that the code is backwards compatible. Existing role mappings continue to work as before, but you can provide a roleMappingName to avoid using the providerUrl as the key in the role mapping hash. |
@rix0rrr or @kaizencc I've put together a pull request to solve this. It'd be great to get someone to look at this; it's not possible to define a CDK User Pool and then define an Identity Pool using that User Pool for role mapping without this fix; a use case that seems like it would be relatively common. Thanks. |
…s not provided and it is a token (#21191) This property is for use when the identityProvider is a Token. By default identityProvider is used as the key in the role mapping hash, but Cloudformation only allows concrete strings to be used as hash keys. In particular this feature is a requirement to allow a previously defined CDK UserPool to be used as an identityProvider. closes #19222 Please note that the integ test results will need updating. I attempted to run the tests, and received the error ``` Error: ENOENT: no such file or directory, open '/home/sam/aws-cdk/packages/aws-cdk/lib/init-templates/v1/info.json' ERROR integ.identitypool 0.535s Command exited with status 1 ``` I've used `npm` to update to the latest CDK CLI. I appear to not be the only person facing this issue; see #21056 (comment) ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
|
@SamStephens I obviously was unable to resolve this. Thanks so much for the fix, works like a charm! |
Perfect. We're not going to use Cognito, so I've not seen any benefit from doing this, so I'm glad it's proved useful for someone 😁 |
@SamStephens Actually, I may have spoken too soon. Now seeing: Can you help me understand what is going on in this line?
For reference, here's my code (Python): def _create_identity_pool(
self, userpool: cognito.UserPool
) -> cognito_id_pool.IdentityPool:
userpool_provider = cognito_id_pool.UserPoolAuthenticationProvider(
user_pool=userpool,
)
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(
# HACK: Need to manually provide this as per:
# https://github.com/aws/aws-cdk/issues/19222
userpool.user_pool_provider_url
),
use_token=True,
mapping_key="userpool1",
)
],
) |
Is a simple way to get a Token for the sake of the unit test. In real world usage, you'd use Fn.importValue to import a value exported from another stack. You might do this if you have a user pool declared in one Cloudformation/CDK stack, and then want to declare an identity pool that integrates with it in another. In real world usage you'd be more likely to do what you're doing. I just didn't do that in the unit tests to keep them as simple as possible. As to why what you're doing is failing, it's hard for me to say. I suspect it isn't a problem with the construct itself but the To confirm that it's not a problem with this fix, I'd hard-code the value in, and see if you get the same error: def _create_identity_pool(
self, userpool: cognito.UserPool
) -> cognito_id_pool.IdentityPool:
userpool_provider = cognito_id_pool.UserPoolAuthenticationProvider(
user_pool=userpool,
)
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(
# Use your actual provider URL here
'https://cognito-idp.us-west-2.amazonaws.com/us-west-2_ABCDEFGHI'),
),
use_token=True,
# No mapping_key
)
],
) If this fails with the same error you reported above, then the issue is not with this fix. Looking at my code, when I hard-coded the provider URL (to work around this issue, before I solved it), my URL didn't include the https:// prefix. So if what I have above fails, try: def _create_identity_pool(
self, userpool: cognito.UserPool
) -> cognito_id_pool.IdentityPool:
userpool_provider = cognito_id_pool.UserPoolAuthenticationProvider(
user_pool=userpool,
)
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(
# Use your actual provider URL here, without the https:// prefix.
'cognito-idp.us-west-2.amazonaws.com/us-west-2_ABCDEFGHI'),
),
use_token=True,
)
],
) If you find it is the https:// prefix that is causing the issue, you'll need to log a separate bug against the CDK for them to sort that out. Good luck. |
@SamStephens thanks a lot for fixing this! I ran into this issue tonight, saw this thread and when updated the CDK everything worked perfectly. |
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 #21585 |
…s not provided and it is a token (aws#21191) This property is for use when the identityProvider is a Token. By default identityProvider is used as the key in the role mapping hash, but Cloudformation only allows concrete strings to be used as hash keys. In particular this feature is a requirement to allow a previously defined CDK UserPool to be used as an identityProvider. closes aws#19222 Please note that the integ test results will need updating. I attempted to run the tests, and received the error ``` Error: ENOENT: no such file or directory, open '/home/sam/aws-cdk/packages/aws-cdk/lib/init-templates/v1/info.json' ERROR integ.identitypool 0.535s Command exited with status 1 ``` I've used `npm` to update to the latest CDK CLI. I appear to not be the only person facing this issue; see aws#21056 (comment) ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Description
The following role mapping will fail:
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.
Acknowledge
The text was updated successfully, but these errors were encountered: