# 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).