Closed
Description
Describe the bug
When creating the cognito user pool with many custom attributes . I get the below error-
Invalid read attributes specified while creating a client
.
My guess is because of eventual consistency. Is there anything wrong in below code that is stooping it from adding all the cusotm attributes at once?
Regression Issue
- Select this option if this issue appears to be a regression.
Last Known Working CDK Library Version
2.1001.0
Expected Behavior
Create all custom attributes at once when user pool is created
Current Behavior
Errors out when there are multiple custom attributes

Reproduction Steps
This my stack and If add more custom attributes it will error out.
import { StandardAttributesMask } from 'aws-cdk-lib/aws-cognito/lib/user-pool-attr.d';
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
import * as cognito from 'aws-cdk-lib/aws-cognito';
import * as cdk from 'aws-cdk-lib';
import * as certificatemanager from 'aws-cdk-lib/aws-certificatemanager';
import * as secretsManager from 'aws-cdk-lib/aws-secretsmanager';
import {
CfnUserPoolResourceServer,
OidcAttributeRequestMethod,
ProviderAttribute,
UserPoolIdentityProviderOidc,
} from 'aws-cdk-lib/aws-cognito';
export interface OidcProviderProps {
name: string;
secretKey: string;
secretKeyLookup?: string;
authorizationUrl?: boolean;
tokenUrl?: boolean;
userInfoUrl?: boolean;
jwksUriUrl?: boolean;
}
export interface CognitoUserPoolProps {
userPoolName: string;
domainName: string;
certificateArn: string;
selfSignupEnabled: boolean;
ssoApplicationOidcProviders: OidcProviderProps[];
ssoAppCallbackUrls: string[] | undefined;
ssoAppLogoutUrl: string[] | undefined;
}
const customAttributes = [
'empId',
'orgId',
];
// User Pool Client attributes
const standardCognitoAttributes: StandardAttributesMask = {
givenName: true,
familyName: true,
email: true,
emailVerified: true,
middleName: true,
fullname: true,
nickname: true,
phoneNumber: true,
phoneNumberVerified: true,
profilePicture: true,
preferredUsername: true,
timezone: true,
lastUpdateTime: true,
};
// Note: When creating the user pool for the first time , with many custom attributes, it might lead to an error - `Invalid read attributes specified while creating a client . To overcome this error, create the user pool with less custom attributes and then add the custom attributes later.`
export class CognitoOIDCCoreStack extends cdk.Stack {
id!: string;
constructor(scope: cdk.App, id: string, props: CognitoUserPoolProps) {
super(scope, id);
this.id = id
// User Pool
const userPool = new cognito.UserPool(this, `universe-userpool-${id}`, {
userPoolName: props.userPoolName,
selfSignUpEnabled: props.selfSignupEnabled,
signInCaseSensitive: false,
signInAliases: {
email: true,
},
autoVerify: {
email: true,
},
standardAttributes: {
email: {
required: true,
mutable: true,
},
givenName: {
required: false,
mutable: true,
},
familyName: {
required: false,
mutable: true,
},
},
customAttributes: {
empId: new cognito.StringAttribute({ mutable: true }),
orgId: new cognito.StringAttribute({ mutable: true }),
// Errors out if I create the user pool with more attributes
},
passwordPolicy: {
minLength: 8,
requireLowercase: true,
requireDigits: true,
requireUppercase: true,
requireSymbols: true,
},
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
userPool.addDomain('cognito-domain', {
customDomain: {
domainName: props.domainName,
certificate: certificatemanager.Certificate.fromCertificateArn(
this,
'cloudfrontCert',
props.certificateArn,
),
},
});
new CfnUserPoolResourceServer(this, 'universe-userpool-resource-server', {
identifier: 'https://universe.io/',
name: `universe-resource-server-${id}`,
userPoolId: userPool.userPoolId,
scopes: [
{
scopeDescription: 'universe Admin with read permissions',
scopeName: 'ublox.ad.read',
},
{
scopeDescription: 'universe Admin with read write permissions',
scopeName: 'ublox.ad.readwrite',
},
{
scopeDescription: 'Tenant product permissions',
scopeName: 'product.read',
},
],
});
this.createSSOApplicationClient(
userPool,
standardCognitoAttributes,
props.ssoAppCallbackUrls,
props.ssoAppLogoutUrl,
props.ssoApplicationOidcProviders,
);
// Outputs
new cdk.CfnOutput(this, 'userPoolId', {
value: userPool.userPoolId,
});
}
createSSOApplicationClient(
userPool: cognito.UserPool,
standardCognitoAttributes: StandardAttributesMask,
callbackUrls?: string[],
logoutUrls?: string[],
oidcProvidersConfig?: OidcProviderProps[],
) {
const oidcProvider: UserPoolIdentityProviderOidc[] = [];
const oidcProviders: cognito.UserPoolClientIdentityProvider[] = [];
const clientReadAttributes = new cognito.ClientAttributes()
.withStandardAttributes(standardCognitoAttributes)
.withCustomAttributes(...customAttributes);
const clientWriteAttributes = new cognito.ClientAttributes()
.withStandardAttributes({
...standardCognitoAttributes,
emailVerified: false,
phoneNumberVerified: false,
})
.withCustomAttributes(...customAttributes);
if (oidcProvidersConfig !== undefined) {
for (let i = 0; i < oidcProvidersConfig.length; i += 1) {
const oidcProviderConfig = oidcProvidersConfig[i];
const oidcSecretKey = secretsManager.Secret.fromSecretNameV2(
this,
`OidcProviderSecret${i}`,
oidcProviderConfig.secretKey,
);
const clientId =
oidcSecretKey.secretValueFromJson('clientId')?.toString() ??
undefined;
const clientSecret =
oidcSecretKey.secretValueFromJson('clientSecret').toString() ??
undefined;
const issuerUrl =
oidcSecretKey.secretValueFromJson('issuerUrl')?.toString() ??
undefined;
const authorization = oidcProviderConfig.authorizationUrl
? oidcSecretKey.secretValueFromJson('authorizationUrl').toString()
: undefined;
const token = oidcProviderConfig.tokenUrl
? oidcSecretKey.secretValueFromJson('tokenUrl')?.toString()
: undefined;
const userInfo = oidcProviderConfig.userInfoUrl
? oidcSecretKey.secretValueFromJson('userInfoUrl')?.toString()
: undefined;
const jwksUri = oidcProviderConfig.jwksUriUrl
? oidcSecretKey.secretValueFromJson('jwksUriUrl')?.toString()
: undefined;
const identityProviderOidc = new UserPoolIdentityProviderOidc(
this,
oidcProviderConfig.name,
{
userPool,
name: oidcProviderConfig.name,
clientId,
clientSecret,
issuerUrl,
...(authorization &&
token &&
userInfo &&
jwksUri && {
endpoints: {
authorization,
token,
userInfo,
jwksUri,
},
}),
scopes: ['profile', 'email', 'openid'],
attributeRequestMethod: OidcAttributeRequestMethod.GET,
attributeMapping: {
email: ProviderAttribute.other('email'),
custom: {
'custom:empId': ProviderAttribute.other('EMP_ID'),
'custom:orgId': ProviderAttribute.other('ORG_ID'),
},
},
},
);
oidcProvider.push(identityProviderOidc);
oidcProviders.push(
cognito.UserPoolClientIdentityProvider.custom(
identityProviderOidc.providerName,
),
);
}
}
// User Pool Client
const userPoolClient = new cognito.UserPoolClient(
this,
'sso-application-cognito-client',
{
userPool,
authFlows: {
adminUserPassword: true,
custom: true,
userSrp: true,
},
supportedIdentityProviders: [
cognito.UserPoolClientIdentityProvider.COGNITO,
...oidcProviders,
],
readAttributes: clientReadAttributes,
writeAttributes: clientWriteAttributes,
oAuth: {
callbackUrls,
logoutUrls,
scopes: [
cognito.OAuthScope.OPENID,
cognito.OAuthScope.EMAIL,
cognito.OAuthScope.PHONE,
cognito.OAuthScope.PROFILE,
cognito.OAuthScope.custom('aws.cognito.signin.user.admin'),
],
flows: {
authorizationCodeGrant: true,
implicitCodeGrant: true,
},
},
userPoolClientName: `universe-SSO-Application-Client-${this.id}`,
},
);
userPoolClient.node.addDependency(...oidcProvider);
// Outputs
new cdk.CfnOutput(this, 'ssoApplicationUserPoolClient', {
value: userPoolClient.userPoolClientId,
});
}
}
Possible Solution
Might be because of eventual consistency. But shouldnt happen
Additional Information/Context
Running the CDK in github action
AWS CDK Library version (aws-cdk-lib)
2.181.1
AWS CDK CLI version
2.1001.0
Node.js Version
22.11.0
OS
macos
Language
TypeScript
Language Version
4.9.5
Other information
No response