Answers.Body: <p>When setting a password during front end registration using the core <code>users/save-user</code> action, <a href="https://github.com/craftcms/cms/blob/6d73ac2bcd9be609e79028852711ac7c13c62309/src/controllers/UsersController.php#L1313" rel="nofollow noreferrer">Craft actually sets the <code>newPassword</code> attribute on the user model</a> under the hood, not the <code>password</code> attribute.</p>
  <p><em>This means that you need to test the length for the <code>newPassword</code> attribute, not the <code>password</code> attribute, i.e.:</em></p>
  <pre class="lang-php prettyprint-override"><code>if (mb_strlen($user-&gt;newPassword) &lt; 8) {
      $user-&gt;addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event-&gt;isValid = false;
    }
  </code></pre>
  <p>Another issue with your code is the <code>if ($user-&gt;password != $user-&gt;confirmPassword) {</code> conditional.</p>
  <p>The user model doesn't actually have a <code>confirmPassword</code> attribute, so it's odd if you're not seeing an exception thrown here (are you using <a href="https://craftcms.com/knowledge-base/what-dev-mode-does" rel="nofollow noreferrer"><code>devMode</code></a> during development?). But regardless, the code is not going to work as intended.</p>
  <p>Note that you also can't do <code>$user-&gt;addError('confirmPassword', ...)</code> – again, since the user model doesn't actually have that attribute; the <code>addError()</code> method only works with actual attributes.</p>
  <p>Assuming <code>confirmPassword</code> is the name for an input in your registration form, you'll need to change your code to something like this (personally I think an if/else statement makes sense here, since you can't set separate errors on the model for the password and the &quot;confirm password&quot;):</p>
  <pre class="lang-php prettyprint-override"><code>if (mb_strlen($user-&gt;newPassword) &lt; 8) {
      $user-&gt;addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event-&gt;isValid = false;
    } else if ($user-&gt;newPassword !== Craft::$app-&gt;getRequest()-&gt;getBodyParam('confirmPassword')) {
      $user-&gt;addError('password', Craft::t('yii', 'Passwords do not match'));
  }
  </code></pre>
  <p>Beyond this, while doing custom validation in an <code>EVENT_BEFORE_VALIDATE</code> event handler should work, the recommended way to do custom validation in Craft is to add Yii validation rules via the <code>Element::EVENT_DEFINE_RULES</code> event. <a href="https://craftcms.com/docs/4.x/extend/extending-system-components.html#custom-validation-rules" rel="nofollow noreferrer">Check out this page in the official docs.</a></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-01-15T00:11:45.680Z
Answers.Text: Wow, thanks for the detailed answer! My code was kind of hacked together from some old questions on here - shows I still have so much to learn. Thank you.
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-01-14T20:30:28.377Z
Answers.LastActivityDate: 2023-01-14T20:35:43.127Z
Answers.LastEditDate: 2023-01-14T20:35:43.127Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I'm trying to add some rules for users signing up. The first two (<code>fullName</code> and <code>email</code>) work fine. However, the password rules are triggered no matter what. Even if the passwords match and are over 8 characters, it still fails.</p>
  <pre><code>Event::on(User::class, Model::EVENT_BEFORE_VALIDATE, static function (Event $event) {
    // If a user is already logged in we are not on the public registration page
    if (Craft::$app-&gt;getUser()-&gt;getIdentity()) {
      return;
    }
    $user = $event-&gt;sender;
  
    // Do the custom validation
    if (!$user-&gt;fullName) {
      $user-&gt;addError('fullName', Craft::t('yii', 'Please enter your name.'));
      $event-&gt;isValid = false;
    }
  
    if (!$user-&gt;email) {
      $user-&gt;addError('email', Craft::t('yii', 'Please enter a valid email.'));
      $event-&gt;isValid = false;
    }
  
    if (mb_strlen($user-&gt;password) &lt; 8) {
      $user-&gt;addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event-&gt;isValid = false;
    }
  
    if ($user-&gt;password != $user-&gt; confirmPassword) {
      $user-&gt;addError('confirmPassword', Craft::t('yii', 'Passwords do not match'));
      $event-&gt;isValid = false;
    }
  });
  </code></pre>
  <pre><code>&lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
  &lt;input id=&quot;password&quot; name=&quot;password&quot; type=&quot;password&quot;&gt;
  {{ user ? forms.errorsList(user.getErrors('password')) }}
  
  &lt;label for=&quot;confirmPassword&quot;&gt;Confirm Password&lt;/label&gt;
  &lt;input id=&quot;confirmPassword&quot; name=&quot;confirmPassword&quot; type=&quot;password&quot;&gt;
      {{ user ? forms.errorsList(user.getErrors('confirmPassword')) }}
  &lt;/div&gt;
  </code></pre>
  <p>Can anyone spot what I'm missing?</p>
  
Tags: <modules>
Title: Custom validation rules on user signup
ThreadText: # Custom validation rules on user signup
  
  (Asked by: username_0 on 2023-01-14)
  
  I'm trying to add some rules for users signing up. The first two (`fullName` and `email`) work fine. However, the password rules are triggered no matter what. Even if the passwords match and are over 8 characters, it still fails.
  
  ```
  Event::on(User::class, Model::EVENT_BEFORE_VALIDATE, static function (Event $event) {
    // If a user is already logged in we are not on the public registration page
    if (Craft::$app->getUser()->getIdentity()) {
      return;
    }
    $user = $event->sender;
  
    // Do the custom validation
    if (!$user->fullName) {
      $user->addError('fullName', Craft::t('yii', 'Please enter your name.'));
      $event->isValid = false;
    }
  
    if (!$user->email) {
      $user->addError('email', Craft::t('yii', 'Please enter a valid email.'));
      $event->isValid = false;
    }
  
    if (mb_strlen($user->password) < 8) {
      $user->addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event->isValid = false;
    }
  
    if ($user->password != $user-> confirmPassword) {
      $user->addError('confirmPassword', Craft::t('yii', 'Passwords do not match'));
      $event->isValid = false;
    }
  });
  
  ```
  
  ```
  <label for="password">Password</label>
  <input id="password" name="password" type="password">
  {{ user ? forms.errorsList(user.getErrors('password')) }}
  
  <label for="confirmPassword">Confirm Password</label>
  <input id="confirmPassword" name="confirmPassword" type="password">
      {{ user ? forms.errorsList(user.getErrors('confirmPassword')) }}
  </div>
  
  ```
  
  Can anyone spot what I'm missing?
  
  **Tags:** modules
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2023-01-14. Score: 1 | Accepted answer
  
  When setting a password during front end registration using the core `users/save-user` action, [Craft actually sets the `newPassword` attribute on the user model](https://github.com/craftcms/cms/blob/6d73ac2bcd9be609e79028852711ac7c13c62309/src/controllers/UsersController.php#L1313) under the hood, not the `password` attribute.
  
  *This means that you need to test the length for the `newPassword` attribute, not the `password` attribute, i.e.:*
  
  ```php
  if (mb_strlen($user->newPassword) < 8) {
      $user->addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event->isValid = false;
    }
  
  ```
  
  Another issue with your code is the `if ($user->password != $user->confirmPassword) {` conditional.
  
  The user model doesn't actually have a `confirmPassword` attribute, so it's odd if you're not seeing an exception thrown here (are you using [`devMode`](https://craftcms.com/knowledge-base/what-dev-mode-does) during development?). But regardless, the code is not going to work as intended.
  
  Note that you also can't do `$user->addError('confirmPassword', ...)` – again, since the user model doesn't actually have that attribute; the `addError()` method only works with actual attributes.
  
  Assuming `confirmPassword` is the name for an input in your registration form, you'll need to change your code to something like this (personally I think an if/else statement makes sense here, since you can't set separate errors on the model for the password and the "confirm password"):
  
  ```php
  if (mb_strlen($user->newPassword) < 8) {
      $user->addError('password', Craft::t('yii', 'Password must be more than 8 characters.'));
      $event->isValid = false;
    } else if ($user->newPassword !== Craft::$app->getRequest()->getBodyParam('confirmPassword')) {
      $user->addError('password', Craft::t('yii', 'Passwords do not match'));
  }
  
  ```
  
  Beyond this, while doing custom validation in an `EVENT_BEFORE_VALIDATE` event handler should work, the recommended way to do custom validation in Craft is to add Yii validation rules via the `Element::EVENT_DEFINE_RULES` event. [Check out this page in the official docs.](https://craftcms.com/docs/4.x/extend/extending-system-components.html#custom-validation-rules)
  
  > **Comments:**
  > **username_0**: Wow, thanks for the detailed answer! My code was kind of hacked together from some old questions on here - shows I still have so much to learn. Thank you.  
  
  ---
  
  