# auth.validate-password-change Checks a change-password form in one call, after the API has asked `auth.password-hash` whether the current password is right, and answers in the shape an API's `validation_failed` error and a form both want: ``` matches = verifyPassword(currentPassword, user.passwordHash) validatePasswordChange(currentPassword, newPassword, matches, user.email, user.name, passwordPolicy("nist-800-63b-4-single-factor")) # {valid: false, fields: {"currentPassword": "That is not your current password."}} ``` Checking the hash is not a pure function's job (it needs the stored hash), so its answer comes in as `currentPasswordMatches`; the rule of what to say about it lives here, next to the other messages. A wrong current password is a field message (400), not a 401: the person's session is fine, they mistyped. **currentPassword**: empty is "Enter your current password."; otherwise `currentPasswordMatches` false is "That is not your current password.". An empty password never counts as matching, whatever the flag says. **newPassword**: empty is "Enter a new password."; otherwise every failure of `auth.password-policy`'s `checkPassword` against the account's stored email and name, in the policy's order, and then "Choose a password that is different from your current one." when it is exactly the current password, all joined with a space. Use the same policy name sign-up uses, so a password that could not be chosen at sign-up cannot be chosen here either. Passwords are compared exactly as typed: not trimmed and not case-folded, so `"correct horse battery staple "` with a trailing space is a different password. `fields` is keyed `currentPassword` then `newPassword`, the JSON body's own names. A value that is not a string is treated as empty. A nonsensical policy throws, as in `checkPassword`. After a valid change the API stores a new hash and revokes every token the account holds (`auth.access-token`: bump the token version).