# auth.password-policy
Decide whether a new password is acceptable, and say why not in words a form
can show. The same function runs in the browser as the person types and on
the server, which has the final say, so the two can never disagree:
```
policy = passwordPolicy("nist-800-63b-4-single-factor")
check = checkPassword(password, email, name, policy)
# {valid: false, failures: [{code: "too_short", message: "Use at least 15 characters."}, ...]}
```
It is a group because a policy is only useful to `checkPassword`;
`passwordPolicy` looks one up by name from the data, so both sides of an
application name the policy rather than copying its numbers.
## Policies (data/policies.json)
| name | min | max | classes | source |
|---|---:|---:|---:|---|
| `nist-800-63b-4-single-factor` | 15 | 128 | 0 | NIST SP 800-63B-4 §3.1.1.2: a password that is the only factor SHALL be at least 15 characters |
| `nist-800-63b-4-multi-factor` | 8 | 128 | 0 | the same section: 8 when a second factor is also required |
| `composition-12-3` | 12 | 128 | 3 | for organisations whose own rules still demand character classes |
All three check the common-password list and personal words. NIST SP
800-63B-4 (26 August 2025) says verifiers SHALL NOT impose composition rules
and SHOULD permit at least 64 characters; the maximum here is 128, which
bounds the work a hash does without refusing any real passphrase. Figures
checked against https://pages.nist.gov/800-63-4/sp800-63b.html. A new
revision will be a new policy name in a new version, never an edit of these.
A caller may also pass its own `PasswordPolicy` record; nonsense numbers
(a minimum below 1, a maximum below the minimum, more than 4 classes) throw.
## Rules, in the order failures are reported
1. `too_short` / `too_long`: length in characters, meaning Unicode code
points, as NIST specifies. An emoji is one character, not the two UTF-16
units JavaScript's `length` counts.
2. `too_few_character_classes`: lower-case a-z, capitals A-Z, digits 0-9, and
everything else (symbols, spaces, and any non-ASCII letter). Only checked
when the policy asks for classes.
3. `too_common`: the password, with A-Z folded to a-z, is on the list in
`data/common-passwords.json` (exact match, not substring).
4. `contains_email`: the password contains the email's local part, or any
piece of it between punctuation (`ada.lovelace@...` gives `ada.lovelace`,
`ada` and `lovelace`), ignoring case.
5. `contains_name`: the password contains any word of the name, ignoring
case. Pieces shorter than 3 characters are ignored in both, since refusing
every password that contains "al" helps nobody.
Every broken rule is listed, not just the first, so a form can show them all
at once. Messages are plain sentences meant for the person choosing the
password; the codes are stable for code to branch on. Case folding is ASCII
only, so every language folds identically.
## The common-password list
The 1,000 most common distinct passwords of 8 or more characters from the
UK National Cyber Security Centre's list of the 100,000 most common passwords
in Have I Been Pwned's breach corpus (NCSC, "Passwords, passwords everywhere",
April 2019, `PwnedPasswordsTop100k.txt`), lower-cased and de-duplicated, with
each entry's rank in that list (the last one is rank 2,902). ncsc.gov.uk was
unavailable while this was built, so the file was taken from the verbatim
mirror in SecLists (`Passwords/Common-Credentials/100k-most-used-passwords-NCSC.txt`).
Shorter entries are left out because every policy here refuses them for
length already. A 15-character minimum makes the list matter much less; that
is the point of NIST's longer minimum. A full breached-password check (such
as the Pwned Passwords range API) needs the network and belongs in the
application, not here.
Sources: NIST SP 800-63B-4, Digital Identity Guidelines: Authentication and
Authenticator Management, section 3.1.1.2
(https://pages.nist.gov/800-63-4/sp800-63b.html); NCSC, Top 100k passwords
(https://www.ncsc.gov.uk/static-assets/documents/PwnedPasswordsTop100k.txt,
mirrored at https://github.com/danielmiessler/SecLists).
## Notices
Contains public sector information licensed under the Open Government
Licence v3.0 (https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
Source: NCSC, top 100,000 passwords from Have I Been Pwned's Pwned Passwords.
1.0.1 adds its attribution notices (NOTICE). The code and the tests are unchanged.