Cognito refuses to verify updated Email address with Email Link

0

I have a cdk L2 cognito.UserPool (see below). I only want email and password authentication. I want users to be able to change their email. I want their email to be verified either at sign up or upon changing their email. Right now, as currently configured in this construct, despite requesting LINK only, it sends a verification code when a user requests to update their email address.

I thought I would try to override this by accessing the Cfn construct and doing it that way, but now when I CDK build, it fails with this error: {##Verify##}' at 'verificationMessageTemplate.emailMessage' failed to satisfy constraint: Member must satisfy regular expression pattern: [\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*\{####\}[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]* (Service: CognitoIdentityProvider, Status Code: 400, Request ID: 991e7388-ee61-4acb-bc74-774bcb20b009) (SDK Attempt Count: 1)" (RequestToken: 3cc68927-52fc-a0d5-7c66-d2f509e51220, HandlerErrorCode: InvalidRequest)

L2 Construct:

    const userPool = new cognito.UserPool(this, 'UserPool', {
      accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
      standardAttributes: {
        email: {required: true, mutable: true},
      },
      signInAliases: {
        email: true,
        username: false,
        phone: false
      },
      featurePlan: cognito.FeaturePlan.PLUS,
      customThreatProtectionMode: cognito.CustomThreatProtectionMode.FULL_FUNCTION,
      standardThreatProtectionMode: cognito.StandardThreatProtectionMode.FULL_FUNCTION,
      deletionProtection: true,
      deviceTracking: {
        challengeRequiredOnNewDevice: false,
        deviceOnlyRememberedOnUserPrompt: true,
      },
      email: cognito.UserPoolEmail.withSES({
        sesRegion: props.region,
        fromEmail: `noreply@${props.domain}`,
        fromName: 'My Domain',
        replyTo: `support@${props.domain}`,
        sesVerifiedDomain: props.domain,
      }),
      keepOriginal: {
        email: true,
      },
      passwordPolicy: {
        minLength: 8,
        requireLowercase: true,
        requireUppercase: true,
        requireDigits: true,
        requireSymbols: true,
      },
      autoVerify: { email: true },
      signInCaseSensitive: false,
      signInPolicy: {
        allowedFirstAuthFactors: { password: true },
      },
      selfSignUpEnabled: true,
      userVerification: {
        emailSubject: `Verify your email for your account on ${props.domain}`,
        emailBody: `Thanks for signing up for an account!\n \n Click this link to verify your email address:{##Verify Email##}`,
        emailStyle: cognito.VerificationEmailStyle.LINK,
      },
      smsRole: new iam.Role(this, 'cognitoUserPoolSMSRole', {
        assumedBy: new iam.ServicePrincipal('cognito-idp.amazonaws.com'),
        inlinePolicies: {
          Policies: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              resources: ['*'],
              actions: ['sns:publish'],
            }),
          ],
        }),
        },
      }),
    });

L1/Cfn attempt to override:

    // L2 construct doesn't have userAttributeUpdateValidator so need to configure that on the L1 construct
    const cfnUserPool = userPool.node.defaultChild as cognito.CfnUserPool;
    cfnUserPool.userAttributeUpdateSettings = {
      attributesRequireVerificationBeforeUpdate: ['email'],
    };
    cfnUserPool.verificationMessageTemplate = {
        defaultEmailOption: 'CONFIRM_WITH_LINK',
        emailMessage: `Please click the following link to verify your new email address:\n\n{####}`,
        emailSubject: `Verify your new email address for ${props.domain}`,
        emailMessageByLink: `Please click the following link to verify your new email address:\n\n{####}`,
        emailSubjectByLink: `Verify your new email address for ${props.domain}`,
    };
1 Answer
-1

The issue you're experiencing with Cognito refusing to verify updated email addresses with an email link is due to a combination of configuration settings and the way Cognito handles attribute updates.

First, it's important to note that the behavior you're seeing where a verification code is sent instead of a link when updating an email address is expected. This is because Cognito treats attribute updates differently from initial verification during sign-up.

To address your specific concerns:

  1. For initial sign-up verification, your configuration using emailStyle: cognito.VerificationEmailStyle.LINK in the userVerification section is correct for sending a link.

  2. For attribute updates (like changing an email address), Cognito by default uses a code-based verification. This is why you're seeing a code sent even though you've configured link-based verification for sign-up.

  3. The error you're encountering when trying to override this behavior with the L1/Cfn construct is due to an incorrect placeholder in your email message template. Cognito expects the {####} placeholder for both code and link-based verifications.

To resolve this issue and achieve your desired behavior:

  1. Keep your L2 construct configuration as is for the initial sign-up verification.

  2. For attribute updates, modify your L1/Cfn override as follows:

cfnUserPool.verificationMessageTemplate = {
    defaultEmailOption: 'CONFIRM_WITH_LINK',
    emailMessage: `Please click the following link to verify your new email address:\n\n{##Verify Your Email##}`,
    emailSubject: `Verify your new email address for ${props.domain}`,
    emailMessageByLink: `Please click the following link to verify your new email address:\n\n{##Verify Your Email##}`,
    emailSubjectByLink: `Verify your new email address for ${props.domain}`,
};

Note the change in the placeholder from {####} to {##Verify Your Email##}. This is the correct format for link-based verification.

  1. Keep the userAttributeUpdateSettings configuration as you have it:
cfnUserPool.userAttributeUpdateSettings = {
    attributesRequireVerificationBeforeUpdate: ['email'],
};

This ensures that email updates require verification before being applied.

With these changes, Cognito should send a verification link instead of a code when users update their email address. Remember that the actual text between the ## in the placeholder can be customized, but the format must be {##Your Custom Text##}.

If you continue to experience issues, double-check your Cognito User Pool settings in the AWS Console to ensure all configurations align with your CDK deployment.
Sources
UserAttributeUpdateSettingsType - Amazon Cognito User Pools
Configuring verification and invitation messages - Amazon Cognito
CfnUserPool.UserAttributeUpdateSettingsProperty (AWS CDK 2.177.0 API)

profile picture
answered 23 days ago
  • No this results in the same error: {##Verify Your Email##}' at 'verificationMessageTemplate.emailMessage' failed to satisfy constraint: Member must satisfy regular expression pattern: [\p{L}\p{M}\p{S}\p{N}\p{P}\s*]{####}[\p{L}\p{M}\p{S}\p{N}\p{P}\s]* (Service: CognitoIdentityProvider, Status Code: 400, Request ID: 60f669a6-e741-4bce-96fb-45f22e816a3e) (SDK Attempt Count: 1)" (RequestToken: 8889d132-0f6f-c611-ed45-6b0946d7a5e0, HandlerErrorCode: InvalidRequest)

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions