Cognito UserPools - Hosted UI Customizations (including logo upload)
See original GitHub issueAs per the https://github.com/aws/aws-cdk/issues/6765 tracking issue, CDK doesn’t yet have construct support for all Cognito things. We can use escape hatches, but it would be good to have native support to be able to apply Cognito UserPools Hosted UI Customisations (CSS, logo upload, etc)
Use Case
To apply Cognito UserPool Hosted UI customizations as part of my CDK stack, without having to resort to escape hatches/workarounds.
Proposed Solution
Implement some new constructs that support the UI customisations. As Cloudformation doesn’t currently support uploading the logo, this would probably be achieved by a custom resource that calls the AWS SDK or similar.
The following code snippets are my initial attempts to workaround this with the escape hatch, but while they seemed to deploy, I don’t know that they actually worked in the end. In particular, I’m not sure the latter AWS SDK call was working particularly well for the image upload. Originally I intended to use the CloudFormation part for the CSS, and just do the logo via the SDK, but I chopped and changed the code a little in trying to get things working.
You require a domain set on the UserPool before you are able to apply customisations.
The logo can apparently only be JPG/PNG.
Refs:
- CDK
- https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpooluicustomizationattachment.html
- https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-ui-customization.html
- https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SetUICustomization.html
- https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#setUICustomization-property
Warning, this code may not actually work as it is currently:
const fs = require('fs')
/**
* Cognito Hosted UI customisations.
*
* @type {CfnUserPoolUICustomizationAttachment}
*
* @see https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cognito.CfnUserPoolUICustomizationAttachment.html
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpooluicustomizationattachment.html
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-ui-customization.html
*/
const userPoolHostedUICustomisation = new cognito.CfnUserPoolUICustomizationAttachment(
this,
'UserPoolHostedUICustomisation',
{
userPoolId: userPool.userPoolId,
clientId: 'ALL',
css: fs.readFileSync('./assets/cognito-hosted-ui.css').toString('utf-8'),
}
)
userPoolHostedUICustomisation.node.addDependency(userPool)
userPoolHostedUICustomisation.node.addDependency(userPoolDomain)
/**
* Use the AWS SDK to upload a custom logo + CSS for the Cognito Hosted UI customisations.
*
* @type {AwsCustomResource}
*
* @see https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_custom-resources.AwsCustomResource.html
* @see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SetUICustomization.html
* @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#setUICustomization-property
*/
const userPoolHostedUICustomisations = new cr.AwsCustomResource(
this,
'UserPoolHostedUILogo',
{
resourceType: 'Custom::SetCognitoUserPoolHostedUILogo',
onCreate: {
service: 'CognitoIdentityServiceProvider',
action: 'setUICustomization',
parameters: {
UserPoolId: userPool.userPoolId,
ClientId: 'ALL',
// Note: Wrap with Buffer because the SDK automatically Base64 encodes string, which double encodes the image
ImageFile: Buffer.from(fs.readFileSync('./assets/logo.png').toString('base64')),
// ImageFile: fs.readFileSync('./assets/logo.png'),
CSS: fs
.readFileSync('./assets/cognito-hosted-ui.css')
.toString('utf-8'),
},
physicalResourceId: cr.PhysicalResourceId.of(
`cognito-ui-logo-all-clients-${userPoolDomain.domain}`
),
},
// TODO: can we restrict this policy more? Get the ARN for the user pool domain? Or the user pool maybe?
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
// TODO: ?? resources: [userPool.userPoolArn],
}),
}
)
Other
- 👋 I may be able to implement this feature request
- ⚠️ This feature might incur a breaking change
This is a 🚀 Feature Request
Issue Analytics
- State:
- Created 3 years ago
- Reactions:36
- Comments:8 (1 by maintainers)
Hi all, this is my hacky solution, I hope you all find it helpful.
s3_asset.Asset
s.onEvent
handler is actually a no-op (see explanation below). Instead the logic lives in theisComplete
handler, which downloads the files from S3 and calls SetUICustomization.OK, so why is the onEvent lambda a no-op? Well,
The domain associated with this user pool is currently not Active.
IMO, the construct that creates the UserPoolDomain should do the awaiting – since you can’t really login using that domain until it’s Active anyway. But since it doesn’t, this is the approach I took.
Here are some files that may help:
This is the construct, which assumes that the assets (logo and css) are in
../static/
. Note that the permissions forDescribeUserPoolDomain
are for['*']
…This is the noop:
This is the lambda that does the customization (I’m using runtypes to type-check the ResourceProperties and stream-buffers to read the S3 Readable stream into a Buffer).
I’ve moved bits over to managing using SDK and JS scripts directly that I run after to get around this as I ran out of time. E.g.: