use-password-policy 2.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/LICENSE +21 -0
- package/README.md +254 -135
- package/dist/core.d.mts +339 -0
- package/dist/core.d.ts +339 -0
- package/dist/core.js +629 -0
- package/dist/core.mjs +586 -0
- package/dist/index.d.mts +380 -9
- package/dist/index.d.ts +380 -9
- package/dist/index.js +243 -202
- package/dist/index.mjs +238 -200
- package/dist/styles.css +81 -0
- package/package.json +93 -27
package/dist/core.d.mts
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, bundled list of the most common passwords and password "base words".
|
|
3
|
+
* Matching is case-insensitive and also catches simple variations
|
|
4
|
+
* (trailing digits/symbols, a leading number, and common leet swaps),
|
|
5
|
+
* so "Password123!", "P@ssw0rd" and "qwerty2024" are all caught.
|
|
6
|
+
*
|
|
7
|
+
* It is intentionally small (a few KB) — for full coverage, pair it with
|
|
8
|
+
* `checkPwnedPassword` / `usePwnedPassword` (Have I Been Pwned).
|
|
9
|
+
*/
|
|
10
|
+
declare const COMMON_PASSWORDS: readonly string[];
|
|
11
|
+
|
|
12
|
+
interface CheckPwnedOptions {
|
|
13
|
+
/** Abort the request (e.g. when the user keeps typing). */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
/** Custom fetch implementation. Defaults to the global `fetch`. */
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
18
|
+
endpoint?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Ask HIBP to pad the response so its size doesn't hint at the result.
|
|
21
|
+
* Sends an `Add-Padding` header. Default `false`.
|
|
22
|
+
*/
|
|
23
|
+
padding?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* How many times this password appears in known data breaches, using the
|
|
27
|
+
* Have I Been Pwned range API (k-anonymity). Only the first 5 characters of
|
|
28
|
+
* the password's SHA-1 hash leave the device — never the password itself.
|
|
29
|
+
*
|
|
30
|
+
* Resolves to `0` when the password was not found.
|
|
31
|
+
*/
|
|
32
|
+
declare function checkPwnedPassword(password: string, options?: CheckPwnedOptions): Promise<number>;
|
|
33
|
+
|
|
34
|
+
/** Built-in strength labels, weakest to strongest. */
|
|
35
|
+
type StrengthLabel = 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
36
|
+
/** Text for a rule: a plain string, or a function of the resolved options (handy for i18n). */
|
|
37
|
+
type RuleMessage = string | ((options: ResolvedPolicyOptions) => string);
|
|
38
|
+
/**
|
|
39
|
+
* A single validation rule. Used for both built-in and custom rules.
|
|
40
|
+
*/
|
|
41
|
+
interface PolicyRule {
|
|
42
|
+
/** Unique key. Appears in `policyState` and `requirements`. */
|
|
43
|
+
name: string;
|
|
44
|
+
/** For built-in rules: the option that switches the rule on. */
|
|
45
|
+
optionsKey?: keyof PasswordPolicyOptions;
|
|
46
|
+
/** Return `true` when the password passes. */
|
|
47
|
+
test: (password: string, options: ResolvedPolicyOptions) => boolean;
|
|
48
|
+
/** Human-readable requirement, e.g. "No spaces". Falls back to a prettified `name`. */
|
|
49
|
+
message?: RuleMessage;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of a strength estimator such as zxcvbn.
|
|
53
|
+
* `score` uses the zxcvbn scale: 0 (too guessable) … 4 (very unguessable).
|
|
54
|
+
*/
|
|
55
|
+
interface StrengthEstimate {
|
|
56
|
+
score: 0 | 1 | 2 | 3 | 4;
|
|
57
|
+
/** Optional hint to show the user, e.g. "This is a very common password". */
|
|
58
|
+
feedback?: string;
|
|
59
|
+
}
|
|
60
|
+
type StrengthEstimator = (password: string) => StrengthEstimate;
|
|
61
|
+
/** Every option is optional. Omitted options fall back to the defaults. */
|
|
62
|
+
interface PasswordPolicyOptions {
|
|
63
|
+
/** The password to validate (hook only — `validatePassword` takes it as the first argument). */
|
|
64
|
+
password?: string;
|
|
65
|
+
/** Minimum length. Default `8`. */
|
|
66
|
+
minLength?: number;
|
|
67
|
+
/** Maximum length. Default `0` (no maximum). */
|
|
68
|
+
maxLength?: number;
|
|
69
|
+
/** Require a lowercase letter. Default `true`. */
|
|
70
|
+
lowercaseCheck?: boolean;
|
|
71
|
+
/** Require an uppercase letter. Default `true`. */
|
|
72
|
+
uppercaseCheck?: boolean;
|
|
73
|
+
/** Require a digit. Default `true`. */
|
|
74
|
+
numberCheck?: boolean;
|
|
75
|
+
/** Require a special character. Default `true`. */
|
|
76
|
+
specialCharCheck?: boolean;
|
|
77
|
+
/** Reject very common passwords ("password", "qwerty123", "Password1!" …). Default `false`. */
|
|
78
|
+
commonPasswordCheck?: boolean;
|
|
79
|
+
/** Replace the built-in common-password list. */
|
|
80
|
+
commonPasswords?: readonly string[];
|
|
81
|
+
/**
|
|
82
|
+
* Reject predictable patterns: repeated characters (`aaaaaaaa`), repeated chunks
|
|
83
|
+
* (`abcabcabc`), sequences and keyboard runs (`123456789`, `qwertyuiop`), and passwords
|
|
84
|
+
* made of only a few distinct characters (including all spaces). Default `false`;
|
|
85
|
+
* on in the NIST presets.
|
|
86
|
+
*/
|
|
87
|
+
patternCheck?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Check Have I Been Pwned as part of the result. The hook runs it automatically once
|
|
90
|
+
* every other rule passes; on the server use `validatePasswordAsync`. The synchronous
|
|
91
|
+
* `validatePassword` ignores this option. Default `false`.
|
|
92
|
+
*/
|
|
93
|
+
breachCheck?: boolean | BreachCheckOptions;
|
|
94
|
+
/**
|
|
95
|
+
* When set (even to `''`), adds a `match` rule that passes only if
|
|
96
|
+
* `password === confirmPassword`.
|
|
97
|
+
*/
|
|
98
|
+
confirmPassword?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Plug in a real strength estimator (e.g. zxcvbn via `fromZxcvbn`).
|
|
101
|
+
* When set, `strengthLabel` and `strengthPercent` come from the estimator.
|
|
102
|
+
*/
|
|
103
|
+
strengthEstimator?: StrengthEstimator;
|
|
104
|
+
/** With `strengthEstimator`: minimum score (0–4) required. Adds a `strength` rule. */
|
|
105
|
+
minStrength?: 0 | 1 | 2 | 3 | 4;
|
|
106
|
+
/** Your own rules, checked after the built-in ones. */
|
|
107
|
+
customRules?: PolicyRule[];
|
|
108
|
+
/** Override requirement text per rule name, e.g. `{ minLength: 'Mindestens 8 Zeichen' }`. */
|
|
109
|
+
messages?: Partial<Record<string, RuleMessage>>;
|
|
110
|
+
lowercaseRegex?: RegExp;
|
|
111
|
+
uppercaseRegex?: RegExp;
|
|
112
|
+
numberRegex?: RegExp;
|
|
113
|
+
specialCharRegex?: RegExp;
|
|
114
|
+
}
|
|
115
|
+
interface BreachCheckOptions {
|
|
116
|
+
/**
|
|
117
|
+
* What to do when the breach service can't be reached: `true` lets the password
|
|
118
|
+
* through (the requirement passes and `breach.status` is `'error'`), `false` blocks it.
|
|
119
|
+
* Default `true`.
|
|
120
|
+
*/
|
|
121
|
+
failOpen?: boolean;
|
|
122
|
+
/** Hook only: wait this long after the last keystroke. Default `500` ms. */
|
|
123
|
+
debounceMs?: number;
|
|
124
|
+
/** Custom fetch implementation. */
|
|
125
|
+
fetch?: typeof fetch;
|
|
126
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
127
|
+
endpoint?: string;
|
|
128
|
+
/** Send the `Add-Padding` header. Default `false`. */
|
|
129
|
+
padding?: boolean;
|
|
130
|
+
}
|
|
131
|
+
type BreachStatus = 'idle' | 'checking' | 'safe' | 'pwned' | 'error';
|
|
132
|
+
interface BreachResult {
|
|
133
|
+
/**
|
|
134
|
+
* `idle`: not checked yet (other rules still failing, or no input).
|
|
135
|
+
* `checking`: request in flight. `safe` / `pwned`: answered. `error`: couldn't reach the service.
|
|
136
|
+
*/
|
|
137
|
+
status: BreachStatus;
|
|
138
|
+
/** Times seen in breaches (0 unless `pwned`). */
|
|
139
|
+
count: number;
|
|
140
|
+
}
|
|
141
|
+
/** Options after defaults are applied. Passed to every rule's `test`. */
|
|
142
|
+
type ResolvedPolicyOptions = Required<Omit<PasswordPolicyOptions, 'password' | 'confirmPassword' | 'strengthEstimator' | 'minStrength' | 'breachCheck'>> & Pick<PasswordPolicyOptions, 'confirmPassword' | 'strengthEstimator' | 'minStrength' | 'breachCheck'>;
|
|
143
|
+
/** @deprecated Use `ResolvedPolicyOptions`. */
|
|
144
|
+
type PolicyDefaults = ResolvedPolicyOptions;
|
|
145
|
+
/** Pass/fail for each active rule, keyed by rule name. */
|
|
146
|
+
interface PasswordPolicyState {
|
|
147
|
+
[key: string]: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** One row of a requirements checklist. */
|
|
150
|
+
interface Requirement {
|
|
151
|
+
name: string;
|
|
152
|
+
passed: boolean;
|
|
153
|
+
message: string;
|
|
154
|
+
/** `true` while the result isn't known yet (the breach check before it has answered). */
|
|
155
|
+
pending?: boolean;
|
|
156
|
+
}
|
|
157
|
+
/** Result of `validatePassword` (and the hook). */
|
|
158
|
+
interface ValidationResult {
|
|
159
|
+
/** `true` only when every active rule passes. */
|
|
160
|
+
isValid: boolean;
|
|
161
|
+
/** Pass/fail per rule name. */
|
|
162
|
+
policyState: PasswordPolicyState;
|
|
163
|
+
/** Ordered checklist with human-readable messages. */
|
|
164
|
+
requirements: Requirement[];
|
|
165
|
+
/** Messages of the rules that failed, in order. Handy for form errors. */
|
|
166
|
+
errors: string[];
|
|
167
|
+
/** Number of rules that passed. */
|
|
168
|
+
strengthScore: number;
|
|
169
|
+
/** 0–1. Share of rules passed, or the estimator score / 4 when an estimator is set. */
|
|
170
|
+
strengthPercent: number;
|
|
171
|
+
strengthLabel: StrengthLabel;
|
|
172
|
+
/** The estimator's result, if `strengthEstimator` is set. */
|
|
173
|
+
estimate?: StrengthEstimate;
|
|
174
|
+
/** Breach-check state, when `breachCheck` is on (hook and `validatePasswordAsync`). */
|
|
175
|
+
breach?: BreachResult;
|
|
176
|
+
}
|
|
177
|
+
/** What `usePasswordPolicy` returns. */
|
|
178
|
+
interface HookReturnValue extends ValidationResult {
|
|
179
|
+
password?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* use-password-policy/core
|
|
184
|
+
*
|
|
185
|
+
* Framework-free password validation. No React import, so it runs anywhere:
|
|
186
|
+
* the browser, Node, Deno, Bun, edge functions — which lets you enforce the
|
|
187
|
+
* exact same policy on the client and the server.
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
declare const DEFAULT_OPTIONS: ResolvedPolicyOptions;
|
|
191
|
+
/**
|
|
192
|
+
* Ready-made policies. Spread one and override what you need:
|
|
193
|
+
* `usePasswordPolicy({ ...presets.nist, password })`
|
|
194
|
+
*/
|
|
195
|
+
declare const presets: {
|
|
196
|
+
/** The classic checklist (these are also the defaults): 8+ chars, upper, lower, number, symbol. */
|
|
197
|
+
classic: {
|
|
198
|
+
minLength: number;
|
|
199
|
+
lowercaseCheck: true;
|
|
200
|
+
uppercaseCheck: true;
|
|
201
|
+
numberCheck: true;
|
|
202
|
+
specialCharCheck: true;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* NIST SP 800-63B-4 (Aug 2025) for passwords used on their own:
|
|
206
|
+
* at least 15 characters, allow up to 64, no composition rules, block common passwords
|
|
207
|
+
* and predictable patterns. Add `breachCheck: true` to also check known breaches.
|
|
208
|
+
*/
|
|
209
|
+
nist: {
|
|
210
|
+
minLength: number;
|
|
211
|
+
maxLength: number;
|
|
212
|
+
lowercaseCheck: false;
|
|
213
|
+
uppercaseCheck: false;
|
|
214
|
+
numberCheck: false;
|
|
215
|
+
specialCharCheck: false;
|
|
216
|
+
commonPasswordCheck: true;
|
|
217
|
+
patternCheck: true;
|
|
218
|
+
};
|
|
219
|
+
/** NIST SP 800-63B-4 when the password is one factor of MFA: at least 8 characters. */
|
|
220
|
+
nistMfa: {
|
|
221
|
+
minLength: number;
|
|
222
|
+
maxLength: number;
|
|
223
|
+
lowercaseCheck: false;
|
|
224
|
+
uppercaseCheck: false;
|
|
225
|
+
numberCheck: false;
|
|
226
|
+
specialCharCheck: false;
|
|
227
|
+
commonPasswordCheck: true;
|
|
228
|
+
patternCheck: true;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
declare const DEFAULT_MESSAGES: Record<string, RuleMessage>;
|
|
232
|
+
/** Merge user options over the defaults, ignoring keys set to `undefined`. */
|
|
233
|
+
declare function resolveOptions(options?: PasswordPolicyOptions): ResolvedPolicyOptions;
|
|
234
|
+
/** Length in Unicode code points, so an emoji counts as one character (as NIST specifies). */
|
|
235
|
+
declare const passwordLength: (password: string) => number;
|
|
236
|
+
/**
|
|
237
|
+
* `true` if the password is on the list, is a list entry with simple decoration
|
|
238
|
+
* (trailing digits/symbols "password123!", a leading number "123qwerty", leet swaps
|
|
239
|
+
* "p@ssw0rd"), or is only list words joined together ("passwordpassword",
|
|
240
|
+
* "dragon dragon dragon", "Summer2024!Summer").
|
|
241
|
+
*/
|
|
242
|
+
declare function isCommonPassword(password: string, list?: readonly string[]): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* `true` for passwords that follow a predictable pattern: only whitespace, three or
|
|
245
|
+
* fewer distinct characters ("aaaaaaaa", "abababab"), a repeated chunk ("abcabcabc",
|
|
246
|
+
* "qwertyqwertyqwerty"), or mostly sequences and keyboard runs ("123456789012345",
|
|
247
|
+
* "abcdefghijk", "qwertyuiop", "987654321").
|
|
248
|
+
*/
|
|
249
|
+
declare function isPredictablePattern(password: string): boolean;
|
|
250
|
+
/**
|
|
251
|
+
* Validate a password against a policy. Pure and synchronous.
|
|
252
|
+
*
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { validatePassword, presets } from 'use-password-policy/core';
|
|
255
|
+
* const { isValid, errors } = validatePassword(req.body.password, presets.nist);
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
declare function validatePassword(password: string, options?: PasswordPolicyOptions): ValidationResult;
|
|
259
|
+
/**
|
|
260
|
+
* Fold a breach-check outcome into a validation result: adds a `notBreached`
|
|
261
|
+
* requirement (pending while unanswered), makes `isValid` depend on it, and marks a
|
|
262
|
+
* breached password Very Weak. The hook and `validatePasswordAsync` use this; you
|
|
263
|
+
* only need it for custom flows.
|
|
264
|
+
*/
|
|
265
|
+
declare function applyBreachResult(result: ValidationResult, breach: BreachResult, options?: PasswordPolicyOptions): ValidationResult;
|
|
266
|
+
/**
|
|
267
|
+
* Like `validatePassword`, plus the Have I Been Pwned check when `breachCheck` is on.
|
|
268
|
+
* The breach lookup only runs once every other rule passes. Use this on the server.
|
|
269
|
+
*
|
|
270
|
+
* ```ts
|
|
271
|
+
* const { isValid, errors, breach } = await validatePasswordAsync(password, { ...presets.nist, breachCheck: true });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function validatePasswordAsync(password: string, options?: PasswordPolicyOptions): Promise<ValidationResult>;
|
|
275
|
+
/** Anything shaped like the result of `zxcvbn(password)` or `@zxcvbn-ts/core`'s `zxcvbn(password)`. */
|
|
276
|
+
interface ZxcvbnLikeResult {
|
|
277
|
+
score: number;
|
|
278
|
+
feedback?: {
|
|
279
|
+
warning?: string | null;
|
|
280
|
+
suggestions?: readonly string[];
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
type ZxcvbnFn = (password: string, userInputs?: (string | number)[]) => ZxcvbnLikeResult;
|
|
284
|
+
/**
|
|
285
|
+
* Wrap zxcvbn as a `strengthEstimator`. Accepts either a function
|
|
286
|
+
* (`zxcvbn` package, @zxcvbn-ts v3) or an object with `check`
|
|
287
|
+
* (@zxcvbn-ts v4's `new ZxcvbnFactory(options)`).
|
|
288
|
+
*
|
|
289
|
+
* ```ts
|
|
290
|
+
* import { ZxcvbnFactory } from '@zxcvbn-ts/core';
|
|
291
|
+
* const zxcvbn = new ZxcvbnFactory(options);
|
|
292
|
+
* usePasswordPolicy({ password, strengthEstimator: fromZxcvbn(zxcvbn), minStrength: 3 });
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
declare function fromZxcvbn(zxcvbn: ZxcvbnFn | {
|
|
296
|
+
check: ZxcvbnFn;
|
|
297
|
+
}, userInputs?: (string | number)[]): StrengthEstimator;
|
|
298
|
+
/** Minimal shape of Zod's refinement context (works with Zod 3 and 4). */
|
|
299
|
+
interface ZodLikeRefinementCtx {
|
|
300
|
+
addIssue(issue: {
|
|
301
|
+
code: 'custom';
|
|
302
|
+
message: string;
|
|
303
|
+
}): void;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Use your policy inside a Zod schema:
|
|
307
|
+
*
|
|
308
|
+
* ```ts
|
|
309
|
+
* const schema = z.object({ password: z.string().superRefine(zodPasswordRule(policy)) });
|
|
310
|
+
* ```
|
|
311
|
+
* Adds one issue per failed rule (or only the first with `{ allErrors: false }`).
|
|
312
|
+
*/
|
|
313
|
+
declare function zodPasswordRule(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
314
|
+
allErrors?: boolean | undefined;
|
|
315
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => void;
|
|
316
|
+
/**
|
|
317
|
+
* Async version of `zodPasswordRule` that also runs the breach check when
|
|
318
|
+
* `breachCheck` is on. Use it with `safeParseAsync` / `parseAsync`.
|
|
319
|
+
*/
|
|
320
|
+
declare function zodPasswordRuleAsync(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
321
|
+
allErrors?: boolean | undefined;
|
|
322
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => Promise<void>;
|
|
323
|
+
/**
|
|
324
|
+
* Async version of `passwordValidator` that also runs the breach check when
|
|
325
|
+
* `breachCheck` is on. react-hook-form accepts async `validate` functions.
|
|
326
|
+
*/
|
|
327
|
+
declare function passwordValidatorAsync(options?: PasswordPolicyOptions): (value: string) => Promise<true | string>;
|
|
328
|
+
/**
|
|
329
|
+
* A validate function that returns `true` or the first error message.
|
|
330
|
+
* Drops straight into react-hook-form's `validate`, and works with anything
|
|
331
|
+
* that uses the same convention.
|
|
332
|
+
*
|
|
333
|
+
* ```ts
|
|
334
|
+
* register('password', { validate: passwordValidator(policy) })
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
declare function passwordValidator(options?: PasswordPolicyOptions): (value: string) => true | string;
|
|
338
|
+
|
|
339
|
+
export { type BreachCheckOptions, type BreachResult, type BreachStatus, COMMON_PASSWORDS, type CheckPwnedOptions, DEFAULT_MESSAGES, DEFAULT_OPTIONS, type HookReturnValue, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyDefaults, type PolicyRule, type Requirement, type ResolvedPolicyOptions, type RuleMessage, type StrengthEstimate, type StrengthEstimator, type StrengthLabel, type ValidationResult, type ZodLikeRefinementCtx, type ZxcvbnLikeResult, applyBreachResult, checkPwnedPassword, fromZxcvbn, isCommonPassword, isPredictablePattern, passwordLength, passwordValidator, passwordValidatorAsync, presets, resolveOptions, validatePassword, validatePasswordAsync, zodPasswordRule, zodPasswordRuleAsync };
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, bundled list of the most common passwords and password "base words".
|
|
3
|
+
* Matching is case-insensitive and also catches simple variations
|
|
4
|
+
* (trailing digits/symbols, a leading number, and common leet swaps),
|
|
5
|
+
* so "Password123!", "P@ssw0rd" and "qwerty2024" are all caught.
|
|
6
|
+
*
|
|
7
|
+
* It is intentionally small (a few KB) — for full coverage, pair it with
|
|
8
|
+
* `checkPwnedPassword` / `usePwnedPassword` (Have I Been Pwned).
|
|
9
|
+
*/
|
|
10
|
+
declare const COMMON_PASSWORDS: readonly string[];
|
|
11
|
+
|
|
12
|
+
interface CheckPwnedOptions {
|
|
13
|
+
/** Abort the request (e.g. when the user keeps typing). */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
/** Custom fetch implementation. Defaults to the global `fetch`. */
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
18
|
+
endpoint?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Ask HIBP to pad the response so its size doesn't hint at the result.
|
|
21
|
+
* Sends an `Add-Padding` header. Default `false`.
|
|
22
|
+
*/
|
|
23
|
+
padding?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* How many times this password appears in known data breaches, using the
|
|
27
|
+
* Have I Been Pwned range API (k-anonymity). Only the first 5 characters of
|
|
28
|
+
* the password's SHA-1 hash leave the device — never the password itself.
|
|
29
|
+
*
|
|
30
|
+
* Resolves to `0` when the password was not found.
|
|
31
|
+
*/
|
|
32
|
+
declare function checkPwnedPassword(password: string, options?: CheckPwnedOptions): Promise<number>;
|
|
33
|
+
|
|
34
|
+
/** Built-in strength labels, weakest to strongest. */
|
|
35
|
+
type StrengthLabel = 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
36
|
+
/** Text for a rule: a plain string, or a function of the resolved options (handy for i18n). */
|
|
37
|
+
type RuleMessage = string | ((options: ResolvedPolicyOptions) => string);
|
|
38
|
+
/**
|
|
39
|
+
* A single validation rule. Used for both built-in and custom rules.
|
|
40
|
+
*/
|
|
41
|
+
interface PolicyRule {
|
|
42
|
+
/** Unique key. Appears in `policyState` and `requirements`. */
|
|
43
|
+
name: string;
|
|
44
|
+
/** For built-in rules: the option that switches the rule on. */
|
|
45
|
+
optionsKey?: keyof PasswordPolicyOptions;
|
|
46
|
+
/** Return `true` when the password passes. */
|
|
47
|
+
test: (password: string, options: ResolvedPolicyOptions) => boolean;
|
|
48
|
+
/** Human-readable requirement, e.g. "No spaces". Falls back to a prettified `name`. */
|
|
49
|
+
message?: RuleMessage;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of a strength estimator such as zxcvbn.
|
|
53
|
+
* `score` uses the zxcvbn scale: 0 (too guessable) … 4 (very unguessable).
|
|
54
|
+
*/
|
|
55
|
+
interface StrengthEstimate {
|
|
56
|
+
score: 0 | 1 | 2 | 3 | 4;
|
|
57
|
+
/** Optional hint to show the user, e.g. "This is a very common password". */
|
|
58
|
+
feedback?: string;
|
|
59
|
+
}
|
|
60
|
+
type StrengthEstimator = (password: string) => StrengthEstimate;
|
|
61
|
+
/** Every option is optional. Omitted options fall back to the defaults. */
|
|
62
|
+
interface PasswordPolicyOptions {
|
|
63
|
+
/** The password to validate (hook only — `validatePassword` takes it as the first argument). */
|
|
64
|
+
password?: string;
|
|
65
|
+
/** Minimum length. Default `8`. */
|
|
66
|
+
minLength?: number;
|
|
67
|
+
/** Maximum length. Default `0` (no maximum). */
|
|
68
|
+
maxLength?: number;
|
|
69
|
+
/** Require a lowercase letter. Default `true`. */
|
|
70
|
+
lowercaseCheck?: boolean;
|
|
71
|
+
/** Require an uppercase letter. Default `true`. */
|
|
72
|
+
uppercaseCheck?: boolean;
|
|
73
|
+
/** Require a digit. Default `true`. */
|
|
74
|
+
numberCheck?: boolean;
|
|
75
|
+
/** Require a special character. Default `true`. */
|
|
76
|
+
specialCharCheck?: boolean;
|
|
77
|
+
/** Reject very common passwords ("password", "qwerty123", "Password1!" …). Default `false`. */
|
|
78
|
+
commonPasswordCheck?: boolean;
|
|
79
|
+
/** Replace the built-in common-password list. */
|
|
80
|
+
commonPasswords?: readonly string[];
|
|
81
|
+
/**
|
|
82
|
+
* Reject predictable patterns: repeated characters (`aaaaaaaa`), repeated chunks
|
|
83
|
+
* (`abcabcabc`), sequences and keyboard runs (`123456789`, `qwertyuiop`), and passwords
|
|
84
|
+
* made of only a few distinct characters (including all spaces). Default `false`;
|
|
85
|
+
* on in the NIST presets.
|
|
86
|
+
*/
|
|
87
|
+
patternCheck?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Check Have I Been Pwned as part of the result. The hook runs it automatically once
|
|
90
|
+
* every other rule passes; on the server use `validatePasswordAsync`. The synchronous
|
|
91
|
+
* `validatePassword` ignores this option. Default `false`.
|
|
92
|
+
*/
|
|
93
|
+
breachCheck?: boolean | BreachCheckOptions;
|
|
94
|
+
/**
|
|
95
|
+
* When set (even to `''`), adds a `match` rule that passes only if
|
|
96
|
+
* `password === confirmPassword`.
|
|
97
|
+
*/
|
|
98
|
+
confirmPassword?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Plug in a real strength estimator (e.g. zxcvbn via `fromZxcvbn`).
|
|
101
|
+
* When set, `strengthLabel` and `strengthPercent` come from the estimator.
|
|
102
|
+
*/
|
|
103
|
+
strengthEstimator?: StrengthEstimator;
|
|
104
|
+
/** With `strengthEstimator`: minimum score (0–4) required. Adds a `strength` rule. */
|
|
105
|
+
minStrength?: 0 | 1 | 2 | 3 | 4;
|
|
106
|
+
/** Your own rules, checked after the built-in ones. */
|
|
107
|
+
customRules?: PolicyRule[];
|
|
108
|
+
/** Override requirement text per rule name, e.g. `{ minLength: 'Mindestens 8 Zeichen' }`. */
|
|
109
|
+
messages?: Partial<Record<string, RuleMessage>>;
|
|
110
|
+
lowercaseRegex?: RegExp;
|
|
111
|
+
uppercaseRegex?: RegExp;
|
|
112
|
+
numberRegex?: RegExp;
|
|
113
|
+
specialCharRegex?: RegExp;
|
|
114
|
+
}
|
|
115
|
+
interface BreachCheckOptions {
|
|
116
|
+
/**
|
|
117
|
+
* What to do when the breach service can't be reached: `true` lets the password
|
|
118
|
+
* through (the requirement passes and `breach.status` is `'error'`), `false` blocks it.
|
|
119
|
+
* Default `true`.
|
|
120
|
+
*/
|
|
121
|
+
failOpen?: boolean;
|
|
122
|
+
/** Hook only: wait this long after the last keystroke. Default `500` ms. */
|
|
123
|
+
debounceMs?: number;
|
|
124
|
+
/** Custom fetch implementation. */
|
|
125
|
+
fetch?: typeof fetch;
|
|
126
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
127
|
+
endpoint?: string;
|
|
128
|
+
/** Send the `Add-Padding` header. Default `false`. */
|
|
129
|
+
padding?: boolean;
|
|
130
|
+
}
|
|
131
|
+
type BreachStatus = 'idle' | 'checking' | 'safe' | 'pwned' | 'error';
|
|
132
|
+
interface BreachResult {
|
|
133
|
+
/**
|
|
134
|
+
* `idle`: not checked yet (other rules still failing, or no input).
|
|
135
|
+
* `checking`: request in flight. `safe` / `pwned`: answered. `error`: couldn't reach the service.
|
|
136
|
+
*/
|
|
137
|
+
status: BreachStatus;
|
|
138
|
+
/** Times seen in breaches (0 unless `pwned`). */
|
|
139
|
+
count: number;
|
|
140
|
+
}
|
|
141
|
+
/** Options after defaults are applied. Passed to every rule's `test`. */
|
|
142
|
+
type ResolvedPolicyOptions = Required<Omit<PasswordPolicyOptions, 'password' | 'confirmPassword' | 'strengthEstimator' | 'minStrength' | 'breachCheck'>> & Pick<PasswordPolicyOptions, 'confirmPassword' | 'strengthEstimator' | 'minStrength' | 'breachCheck'>;
|
|
143
|
+
/** @deprecated Use `ResolvedPolicyOptions`. */
|
|
144
|
+
type PolicyDefaults = ResolvedPolicyOptions;
|
|
145
|
+
/** Pass/fail for each active rule, keyed by rule name. */
|
|
146
|
+
interface PasswordPolicyState {
|
|
147
|
+
[key: string]: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** One row of a requirements checklist. */
|
|
150
|
+
interface Requirement {
|
|
151
|
+
name: string;
|
|
152
|
+
passed: boolean;
|
|
153
|
+
message: string;
|
|
154
|
+
/** `true` while the result isn't known yet (the breach check before it has answered). */
|
|
155
|
+
pending?: boolean;
|
|
156
|
+
}
|
|
157
|
+
/** Result of `validatePassword` (and the hook). */
|
|
158
|
+
interface ValidationResult {
|
|
159
|
+
/** `true` only when every active rule passes. */
|
|
160
|
+
isValid: boolean;
|
|
161
|
+
/** Pass/fail per rule name. */
|
|
162
|
+
policyState: PasswordPolicyState;
|
|
163
|
+
/** Ordered checklist with human-readable messages. */
|
|
164
|
+
requirements: Requirement[];
|
|
165
|
+
/** Messages of the rules that failed, in order. Handy for form errors. */
|
|
166
|
+
errors: string[];
|
|
167
|
+
/** Number of rules that passed. */
|
|
168
|
+
strengthScore: number;
|
|
169
|
+
/** 0–1. Share of rules passed, or the estimator score / 4 when an estimator is set. */
|
|
170
|
+
strengthPercent: number;
|
|
171
|
+
strengthLabel: StrengthLabel;
|
|
172
|
+
/** The estimator's result, if `strengthEstimator` is set. */
|
|
173
|
+
estimate?: StrengthEstimate;
|
|
174
|
+
/** Breach-check state, when `breachCheck` is on (hook and `validatePasswordAsync`). */
|
|
175
|
+
breach?: BreachResult;
|
|
176
|
+
}
|
|
177
|
+
/** What `usePasswordPolicy` returns. */
|
|
178
|
+
interface HookReturnValue extends ValidationResult {
|
|
179
|
+
password?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* use-password-policy/core
|
|
184
|
+
*
|
|
185
|
+
* Framework-free password validation. No React import, so it runs anywhere:
|
|
186
|
+
* the browser, Node, Deno, Bun, edge functions — which lets you enforce the
|
|
187
|
+
* exact same policy on the client and the server.
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
declare const DEFAULT_OPTIONS: ResolvedPolicyOptions;
|
|
191
|
+
/**
|
|
192
|
+
* Ready-made policies. Spread one and override what you need:
|
|
193
|
+
* `usePasswordPolicy({ ...presets.nist, password })`
|
|
194
|
+
*/
|
|
195
|
+
declare const presets: {
|
|
196
|
+
/** The classic checklist (these are also the defaults): 8+ chars, upper, lower, number, symbol. */
|
|
197
|
+
classic: {
|
|
198
|
+
minLength: number;
|
|
199
|
+
lowercaseCheck: true;
|
|
200
|
+
uppercaseCheck: true;
|
|
201
|
+
numberCheck: true;
|
|
202
|
+
specialCharCheck: true;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* NIST SP 800-63B-4 (Aug 2025) for passwords used on their own:
|
|
206
|
+
* at least 15 characters, allow up to 64, no composition rules, block common passwords
|
|
207
|
+
* and predictable patterns. Add `breachCheck: true` to also check known breaches.
|
|
208
|
+
*/
|
|
209
|
+
nist: {
|
|
210
|
+
minLength: number;
|
|
211
|
+
maxLength: number;
|
|
212
|
+
lowercaseCheck: false;
|
|
213
|
+
uppercaseCheck: false;
|
|
214
|
+
numberCheck: false;
|
|
215
|
+
specialCharCheck: false;
|
|
216
|
+
commonPasswordCheck: true;
|
|
217
|
+
patternCheck: true;
|
|
218
|
+
};
|
|
219
|
+
/** NIST SP 800-63B-4 when the password is one factor of MFA: at least 8 characters. */
|
|
220
|
+
nistMfa: {
|
|
221
|
+
minLength: number;
|
|
222
|
+
maxLength: number;
|
|
223
|
+
lowercaseCheck: false;
|
|
224
|
+
uppercaseCheck: false;
|
|
225
|
+
numberCheck: false;
|
|
226
|
+
specialCharCheck: false;
|
|
227
|
+
commonPasswordCheck: true;
|
|
228
|
+
patternCheck: true;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
declare const DEFAULT_MESSAGES: Record<string, RuleMessage>;
|
|
232
|
+
/** Merge user options over the defaults, ignoring keys set to `undefined`. */
|
|
233
|
+
declare function resolveOptions(options?: PasswordPolicyOptions): ResolvedPolicyOptions;
|
|
234
|
+
/** Length in Unicode code points, so an emoji counts as one character (as NIST specifies). */
|
|
235
|
+
declare const passwordLength: (password: string) => number;
|
|
236
|
+
/**
|
|
237
|
+
* `true` if the password is on the list, is a list entry with simple decoration
|
|
238
|
+
* (trailing digits/symbols "password123!", a leading number "123qwerty", leet swaps
|
|
239
|
+
* "p@ssw0rd"), or is only list words joined together ("passwordpassword",
|
|
240
|
+
* "dragon dragon dragon", "Summer2024!Summer").
|
|
241
|
+
*/
|
|
242
|
+
declare function isCommonPassword(password: string, list?: readonly string[]): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* `true` for passwords that follow a predictable pattern: only whitespace, three or
|
|
245
|
+
* fewer distinct characters ("aaaaaaaa", "abababab"), a repeated chunk ("abcabcabc",
|
|
246
|
+
* "qwertyqwertyqwerty"), or mostly sequences and keyboard runs ("123456789012345",
|
|
247
|
+
* "abcdefghijk", "qwertyuiop", "987654321").
|
|
248
|
+
*/
|
|
249
|
+
declare function isPredictablePattern(password: string): boolean;
|
|
250
|
+
/**
|
|
251
|
+
* Validate a password against a policy. Pure and synchronous.
|
|
252
|
+
*
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { validatePassword, presets } from 'use-password-policy/core';
|
|
255
|
+
* const { isValid, errors } = validatePassword(req.body.password, presets.nist);
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
declare function validatePassword(password: string, options?: PasswordPolicyOptions): ValidationResult;
|
|
259
|
+
/**
|
|
260
|
+
* Fold a breach-check outcome into a validation result: adds a `notBreached`
|
|
261
|
+
* requirement (pending while unanswered), makes `isValid` depend on it, and marks a
|
|
262
|
+
* breached password Very Weak. The hook and `validatePasswordAsync` use this; you
|
|
263
|
+
* only need it for custom flows.
|
|
264
|
+
*/
|
|
265
|
+
declare function applyBreachResult(result: ValidationResult, breach: BreachResult, options?: PasswordPolicyOptions): ValidationResult;
|
|
266
|
+
/**
|
|
267
|
+
* Like `validatePassword`, plus the Have I Been Pwned check when `breachCheck` is on.
|
|
268
|
+
* The breach lookup only runs once every other rule passes. Use this on the server.
|
|
269
|
+
*
|
|
270
|
+
* ```ts
|
|
271
|
+
* const { isValid, errors, breach } = await validatePasswordAsync(password, { ...presets.nist, breachCheck: true });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function validatePasswordAsync(password: string, options?: PasswordPolicyOptions): Promise<ValidationResult>;
|
|
275
|
+
/** Anything shaped like the result of `zxcvbn(password)` or `@zxcvbn-ts/core`'s `zxcvbn(password)`. */
|
|
276
|
+
interface ZxcvbnLikeResult {
|
|
277
|
+
score: number;
|
|
278
|
+
feedback?: {
|
|
279
|
+
warning?: string | null;
|
|
280
|
+
suggestions?: readonly string[];
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
type ZxcvbnFn = (password: string, userInputs?: (string | number)[]) => ZxcvbnLikeResult;
|
|
284
|
+
/**
|
|
285
|
+
* Wrap zxcvbn as a `strengthEstimator`. Accepts either a function
|
|
286
|
+
* (`zxcvbn` package, @zxcvbn-ts v3) or an object with `check`
|
|
287
|
+
* (@zxcvbn-ts v4's `new ZxcvbnFactory(options)`).
|
|
288
|
+
*
|
|
289
|
+
* ```ts
|
|
290
|
+
* import { ZxcvbnFactory } from '@zxcvbn-ts/core';
|
|
291
|
+
* const zxcvbn = new ZxcvbnFactory(options);
|
|
292
|
+
* usePasswordPolicy({ password, strengthEstimator: fromZxcvbn(zxcvbn), minStrength: 3 });
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
declare function fromZxcvbn(zxcvbn: ZxcvbnFn | {
|
|
296
|
+
check: ZxcvbnFn;
|
|
297
|
+
}, userInputs?: (string | number)[]): StrengthEstimator;
|
|
298
|
+
/** Minimal shape of Zod's refinement context (works with Zod 3 and 4). */
|
|
299
|
+
interface ZodLikeRefinementCtx {
|
|
300
|
+
addIssue(issue: {
|
|
301
|
+
code: 'custom';
|
|
302
|
+
message: string;
|
|
303
|
+
}): void;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Use your policy inside a Zod schema:
|
|
307
|
+
*
|
|
308
|
+
* ```ts
|
|
309
|
+
* const schema = z.object({ password: z.string().superRefine(zodPasswordRule(policy)) });
|
|
310
|
+
* ```
|
|
311
|
+
* Adds one issue per failed rule (or only the first with `{ allErrors: false }`).
|
|
312
|
+
*/
|
|
313
|
+
declare function zodPasswordRule(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
314
|
+
allErrors?: boolean | undefined;
|
|
315
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => void;
|
|
316
|
+
/**
|
|
317
|
+
* Async version of `zodPasswordRule` that also runs the breach check when
|
|
318
|
+
* `breachCheck` is on. Use it with `safeParseAsync` / `parseAsync`.
|
|
319
|
+
*/
|
|
320
|
+
declare function zodPasswordRuleAsync(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
321
|
+
allErrors?: boolean | undefined;
|
|
322
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => Promise<void>;
|
|
323
|
+
/**
|
|
324
|
+
* Async version of `passwordValidator` that also runs the breach check when
|
|
325
|
+
* `breachCheck` is on. react-hook-form accepts async `validate` functions.
|
|
326
|
+
*/
|
|
327
|
+
declare function passwordValidatorAsync(options?: PasswordPolicyOptions): (value: string) => Promise<true | string>;
|
|
328
|
+
/**
|
|
329
|
+
* A validate function that returns `true` or the first error message.
|
|
330
|
+
* Drops straight into react-hook-form's `validate`, and works with anything
|
|
331
|
+
* that uses the same convention.
|
|
332
|
+
*
|
|
333
|
+
* ```ts
|
|
334
|
+
* register('password', { validate: passwordValidator(policy) })
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
declare function passwordValidator(options?: PasswordPolicyOptions): (value: string) => true | string;
|
|
338
|
+
|
|
339
|
+
export { type BreachCheckOptions, type BreachResult, type BreachStatus, COMMON_PASSWORDS, type CheckPwnedOptions, DEFAULT_MESSAGES, DEFAULT_OPTIONS, type HookReturnValue, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyDefaults, type PolicyRule, type Requirement, type ResolvedPolicyOptions, type RuleMessage, type StrengthEstimate, type StrengthEstimator, type StrengthLabel, type ValidationResult, type ZodLikeRefinementCtx, type ZxcvbnLikeResult, applyBreachResult, checkPwnedPassword, fromZxcvbn, isCommonPassword, isPredictablePattern, passwordLength, passwordValidator, passwordValidatorAsync, presets, resolveOptions, validatePassword, validatePasswordAsync, zodPasswordRule, zodPasswordRuleAsync };
|