pythonamazon-cognitoaws-cdk

Setting UserPool Client Attributes to have read/write access with aws cdk - Python


I am trying to give some custom attributes specific read/write access depending on the attribute. I am getting this error.

Resource handler returned message: "Invalid write attributes specified while creating a client (Service: CognitoIdentityProvider, Status Code: 400, Request ID: <request_id>)" (RequestToken: <request_token>, HandlerErrorCode: InvalidRequest)

Can anyone point me in the right direction or tell me why this is happening? Obviously, I understand what the error is telling me, but I don't know what (specifically) is causing it or how to fix it. Maybe something to do with the way I am creating the attribute to begin with...

Here is my code;

self.my_user_pool = cognito.UserPool(
    self,
    COGNITO_USER_POOL_ID,
    sign_in_aliases=cognito.SignInAliases(email=True),
    self_sign_up_enabled=True,
    auto_verify=cognito.AutoVerifiedAttrs(email=True),
    user_verification=cognito.UserVerificationConfig(
        email_style=cognito.VerificationEmailStyle.LINK
    ),
    custom_attributes={
        "custom_attribute_1": cognito.StringAttribute(mutable=True),
        "custom_attribute_2": cognito.StringAttribute(mutable=True),
    },
    password_policy=cognito.PasswordPolicy(
        min_length=8,
        require_lowercase=True,
        require_uppercase=True,
        require_digits=True,
        require_symbols=True,
    ),
    account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
    removal_policy=RemovalPolicy.DESTROY,
)


client_read_attributes = (cognito.ClientAttributes()).with_custom_attributes(
    "custom:custom_attribute_1", "custom:custom_attribute_2"
)
client_write_attributes = (cognito.ClientAttributes()).with_custom_attributes(
    "custom:custom_attribute_1"
)

self.my_user_pool_client = self.user_pool.add_client(
    "<my_cognito_client_id>",
    access_token_validity=Duration.minutes(60),
    id_token_validity=Duration.minutes(60),
    refresh_token_validity=Duration.days(1),
    auth_flows=cognito.AuthFlow(admin_user_password=True, user_srp=True, custom=True),
    o_auth=cognito.OAuthSettings(flows=cognito.OAuthFlows(implicit_code_grant=True)),
    prevent_user_existence_errors=True,
    generate_secret=True,
    read_attributes=client_read_attributes,
    write_attributes=client_write_attributes,
    enable_token_revocation=True,
)

Solution

  • I finally figured this out, so I am answering my own question. The way you have to do it feels overly verbose and not particularly intuitive. It took me a while, so hopefully I can save someone else some time.

    It is documented minimally but I assumed there would be more flexibility - ie the ability to set select attributes to read only without explicitly re-setting everything else to what is the default, on initial creation of the user pool client. I spent a lot of time playing with this, thinking it was possible and that I was just doing it wrong...

    https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_cognito/ClientAttributes.html

    1. The read/write attributes are set to True by default when you add the user pool client.

    2. There is currently no way to just set some attributes as read only and ignore all the rest (i.e. - keep them as the default read/write settings)

    3. You need to explicitly specify all the attributes that need to have write access

    4. Then you need to pass the client_write_attributes into the client_read_attributes because otherwise it will set the above attribute to write only with no read access, which obviously makes no sense. Then add the attributes that you want to be read only.

    5. This will set all standard and custom attributes shown in the code below to read/write access, except for the standard attributes email_verified and phone_number_verified and the custom attributes customattribute2 and customattribute3. These will be set to read only access.

    6. To make things even a little more exciting... :-) A few of the standard attributes are named differently than they are in the console and will throw an error if you don't get them right. I have listed these below.

    This is the code that worked for me

    client_write_attributes = (
        cognito.ClientAttributes()
        .with_standard_attributes(
            family_name=True,
            email=True,
            address=True,
            birthdate=True,
            gender=True,
            given_name=True,
            locale=True,
            middle_name=True,
            nickname=True,
            phone_number=True,
        )
        .with_custom_attributes("customattribute1")
    )
    
    client_read_attributes = client_write_attributes.with_standard_attributes(
        email_verified=True, phone_number_verified=True
    ).with_custom_attributes("customattribute2", "customattribute3")
    
    self.user_pool_client = self.user_pool.add_client(
        # ...
        # <all other settings>
        read_attributes=client_read_attributes,
        write_attributes=client_write_attributes,
    )