use-password-policy 2.0.0 → 3.0.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 +37 -0
- package/LICENSE +21 -0
- package/README.md +232 -135
- package/dist/core.d.mts +255 -0
- package/dist/core.d.ts +255 -0
- package/dist/core.js +468 -0
- package/dist/core.mjs +431 -0
- package/dist/index.d.mts +292 -9
- package/dist/index.d.ts +292 -9
- package/dist/index.js +635 -190
- package/dist/index.mjs +621 -188
- package/dist/styles.css +80 -0
- package/package.json +93 -27
package/dist/index.d.mts
CHANGED
|
@@ -1,43 +1,326 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
interface CheckPwnedOptions {
|
|
4
|
+
/** Abort the request (e.g. when the user keeps typing). */
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
/** Custom fetch implementation. Defaults to the global `fetch`. */
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
9
|
+
endpoint?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Ask HIBP to pad the response so its size doesn't hint at the result.
|
|
12
|
+
* Sends an `Add-Padding` header. Default `false`.
|
|
13
|
+
*/
|
|
14
|
+
padding?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How many times this password appears in known data breaches, using the
|
|
18
|
+
* Have I Been Pwned range API (k-anonymity). Only the first 5 characters of
|
|
19
|
+
* the password's SHA-1 hash leave the device — never the password itself.
|
|
20
|
+
*
|
|
21
|
+
* Resolves to `0` when the password was not found.
|
|
22
|
+
*/
|
|
23
|
+
declare function checkPwnedPassword(password: string, options?: CheckPwnedOptions): Promise<number>;
|
|
24
|
+
|
|
25
|
+
/** Built-in strength labels, weakest to strongest. */
|
|
26
|
+
type StrengthLabel = 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
27
|
+
/** Text for a rule: a plain string, or a function of the resolved options (handy for i18n). */
|
|
28
|
+
type RuleMessage = string | ((options: ResolvedPolicyOptions) => string);
|
|
29
|
+
/**
|
|
30
|
+
* A single validation rule. Used for both built-in and custom rules.
|
|
31
|
+
*/
|
|
3
32
|
interface PolicyRule {
|
|
33
|
+
/** Unique key. Appears in `policyState` and `requirements`. */
|
|
4
34
|
name: string;
|
|
35
|
+
/** For built-in rules: the option that switches the rule on. */
|
|
5
36
|
optionsKey?: keyof PasswordPolicyOptions;
|
|
6
|
-
|
|
37
|
+
/** Return `true` when the password passes. */
|
|
38
|
+
test: (password: string, options: ResolvedPolicyOptions) => boolean;
|
|
39
|
+
/** Human-readable requirement, e.g. "No spaces". Falls back to a prettified `name`. */
|
|
40
|
+
message?: RuleMessage;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of a strength estimator such as zxcvbn.
|
|
44
|
+
* `score` uses the zxcvbn scale: 0 (too guessable) … 4 (very unguessable).
|
|
45
|
+
*/
|
|
46
|
+
interface StrengthEstimate {
|
|
47
|
+
score: 0 | 1 | 2 | 3 | 4;
|
|
48
|
+
/** Optional hint to show the user, e.g. "This is a very common password". */
|
|
49
|
+
feedback?: string;
|
|
7
50
|
}
|
|
51
|
+
type StrengthEstimator = (password: string) => StrengthEstimate;
|
|
52
|
+
/** Every option is optional. Omitted options fall back to the defaults. */
|
|
8
53
|
interface PasswordPolicyOptions {
|
|
54
|
+
/** The password to validate (hook only — `validatePassword` takes it as the first argument). */
|
|
9
55
|
password?: string;
|
|
56
|
+
/** Minimum length. Default `8`. */
|
|
10
57
|
minLength?: number;
|
|
58
|
+
/** Maximum length. Default `0` (no maximum). */
|
|
59
|
+
maxLength?: number;
|
|
60
|
+
/** Require a lowercase letter. Default `true`. */
|
|
11
61
|
lowercaseCheck?: boolean;
|
|
62
|
+
/** Require an uppercase letter. Default `true`. */
|
|
12
63
|
uppercaseCheck?: boolean;
|
|
64
|
+
/** Require a digit. Default `true`. */
|
|
13
65
|
numberCheck?: boolean;
|
|
66
|
+
/** Require a special character. Default `true`. */
|
|
14
67
|
specialCharCheck?: boolean;
|
|
68
|
+
/** Reject very common passwords ("password", "qwerty123", "Password1!" …). Default `false`. */
|
|
69
|
+
commonPasswordCheck?: boolean;
|
|
70
|
+
/** Replace the built-in common-password list. */
|
|
71
|
+
commonPasswords?: readonly string[];
|
|
72
|
+
/**
|
|
73
|
+
* When set (even to `''`), adds a `match` rule that passes only if
|
|
74
|
+
* `password === confirmPassword`.
|
|
75
|
+
*/
|
|
76
|
+
confirmPassword?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Plug in a real strength estimator (e.g. zxcvbn via `fromZxcvbn`).
|
|
79
|
+
* When set, `strengthLabel` and `strengthPercent` come from the estimator.
|
|
80
|
+
*/
|
|
81
|
+
strengthEstimator?: StrengthEstimator;
|
|
82
|
+
/** With `strengthEstimator`: minimum score (0–4) required. Adds a `strength` rule. */
|
|
83
|
+
minStrength?: 0 | 1 | 2 | 3 | 4;
|
|
84
|
+
/** Your own rules, checked after the built-in ones. */
|
|
15
85
|
customRules?: PolicyRule[];
|
|
86
|
+
/** Override requirement text per rule name, e.g. `{ minLength: 'Mindestens 8 Zeichen' }`. */
|
|
87
|
+
messages?: Partial<Record<string, RuleMessage>>;
|
|
16
88
|
lowercaseRegex?: RegExp;
|
|
17
89
|
uppercaseRegex?: RegExp;
|
|
18
90
|
numberRegex?: RegExp;
|
|
19
91
|
specialCharRegex?: RegExp;
|
|
20
92
|
}
|
|
93
|
+
/** Options after defaults are applied. Passed to every rule's `test`. */
|
|
94
|
+
type ResolvedPolicyOptions = Required<Omit<PasswordPolicyOptions, 'password' | 'confirmPassword' | 'strengthEstimator' | 'minStrength'>> & Pick<PasswordPolicyOptions, 'confirmPassword' | 'strengthEstimator' | 'minStrength'>;
|
|
95
|
+
/** @deprecated Use `ResolvedPolicyOptions`. */
|
|
96
|
+
type PolicyDefaults = ResolvedPolicyOptions;
|
|
97
|
+
/** Pass/fail for each active rule, keyed by rule name. */
|
|
21
98
|
interface PasswordPolicyState {
|
|
22
99
|
[key: string]: boolean;
|
|
23
100
|
}
|
|
24
|
-
|
|
25
|
-
|
|
101
|
+
/** One row of a requirements checklist. */
|
|
102
|
+
interface Requirement {
|
|
103
|
+
name: string;
|
|
104
|
+
passed: boolean;
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
/** Result of `validatePassword` (and the hook). */
|
|
108
|
+
interface ValidationResult {
|
|
109
|
+
/** `true` only when every active rule passes. */
|
|
26
110
|
isValid: boolean;
|
|
27
|
-
|
|
28
|
-
strengthLabel: 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
111
|
+
/** Pass/fail per rule name. */
|
|
29
112
|
policyState: PasswordPolicyState;
|
|
113
|
+
/** Ordered checklist with human-readable messages. */
|
|
114
|
+
requirements: Requirement[];
|
|
115
|
+
/** Messages of the rules that failed, in order. Handy for form errors. */
|
|
116
|
+
errors: string[];
|
|
117
|
+
/** Number of rules that passed. */
|
|
118
|
+
strengthScore: number;
|
|
119
|
+
/** 0–1. Share of rules passed, or the estimator score / 4 when an estimator is set. */
|
|
120
|
+
strengthPercent: number;
|
|
121
|
+
strengthLabel: StrengthLabel;
|
|
122
|
+
/** The estimator's result, if `strengthEstimator` is set. */
|
|
123
|
+
estimate?: StrengthEstimate;
|
|
124
|
+
}
|
|
125
|
+
/** What `usePasswordPolicy` returns. */
|
|
126
|
+
interface HookReturnValue extends ValidationResult {
|
|
127
|
+
password?: string;
|
|
30
128
|
}
|
|
31
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Validate a password as the user types.
|
|
132
|
+
*
|
|
133
|
+
* Tip: if you pass an expensive `strengthEstimator` (like zxcvbn), memoize
|
|
134
|
+
* your options object so it is only recomputed when the password changes.
|
|
135
|
+
*/
|
|
32
136
|
declare const usePasswordPolicy: (options?: PasswordPolicyOptions) => HookReturnValue;
|
|
137
|
+
type PwnedStatus = 'idle' | 'checking' | 'safe' | 'pwned' | 'error';
|
|
138
|
+
interface UsePwnedPasswordOptions extends Omit<CheckPwnedOptions, 'signal'> {
|
|
139
|
+
/** Turn the check on/off (e.g. only once the policy passes). Default `true`. */
|
|
140
|
+
enabled?: boolean;
|
|
141
|
+
/** Wait this long after the last keystroke before checking. Default `500` ms. */
|
|
142
|
+
debounceMs?: number;
|
|
143
|
+
}
|
|
144
|
+
interface UsePwnedPasswordResult {
|
|
145
|
+
status: PwnedStatus;
|
|
146
|
+
/** Times seen in breaches (0 when safe or unknown). */
|
|
147
|
+
count: number;
|
|
148
|
+
isPwned: boolean;
|
|
149
|
+
error?: Error;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check the password against Have I Been Pwned while the user types
|
|
153
|
+
* (debounced, cancels stale requests). Only a 5-character hash prefix is sent.
|
|
154
|
+
*/
|
|
155
|
+
declare function usePwnedPassword(password: string, options?: UsePwnedPasswordOptions): UsePwnedPasswordResult;
|
|
33
156
|
|
|
34
|
-
interface PasswordPolicyInputProps extends React.ComponentPropsWithoutRef<'input'> {
|
|
157
|
+
interface PasswordPolicyInputProps extends Omit<React.ComponentPropsWithoutRef<'input'>, 'type' | 'value' | 'defaultValue'> {
|
|
158
|
+
/** Controlled value. Leave undefined to let the component manage its own state. */
|
|
159
|
+
value?: string;
|
|
160
|
+
/** Initial value when uncontrolled. */
|
|
161
|
+
defaultValue?: string;
|
|
162
|
+
/** Same options as `usePasswordPolicy`. */
|
|
35
163
|
policyOptions?: PasswordPolicyOptions;
|
|
164
|
+
/** Fires on every change with the new password and its full validation result. */
|
|
36
165
|
onPasswordChange?: (password: string, validation: HookReturnValue) => void;
|
|
166
|
+
/** Default `true`. */
|
|
37
167
|
showStrengthMeter?: boolean;
|
|
168
|
+
/** Show the strength label ("Strong") under the meter. Default `false`. */
|
|
169
|
+
showStrengthLabel?: boolean;
|
|
170
|
+
/** Default `true`. */
|
|
38
171
|
showRequirementsList?: boolean;
|
|
172
|
+
/** Default `true`. */
|
|
39
173
|
showToggleButton?: boolean;
|
|
174
|
+
/** Accessible labels for the show/hide button. */
|
|
175
|
+
toggleLabels?: {
|
|
176
|
+
show: string;
|
|
177
|
+
hide: string;
|
|
178
|
+
};
|
|
179
|
+
/** Skip the built-in CSS (class names stay). Import `use-password-policy/styles.css` or bring your own. */
|
|
180
|
+
unstyled?: boolean;
|
|
181
|
+
/** Class for the outer wrapper. */
|
|
182
|
+
className?: string;
|
|
183
|
+
/** Extra class for the <input> itself. */
|
|
184
|
+
inputClassName?: string;
|
|
185
|
+
}
|
|
186
|
+
declare const PasswordPolicyInput: React.ForwardRefExoticComponent<PasswordPolicyInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Styles for <PasswordPolicyInput />. Kept deliberately low-specificity:
|
|
190
|
+
* the theme variables sit on `:where(.rpp-root)` (zero specificity), so any
|
|
191
|
+
* class you pass via `className` wins; inner parts use a single class
|
|
192
|
+
* (e.g. `.rpp-toggle`), which still beats global element resets like
|
|
193
|
+
* `button { … }`. Override them with `.your-class .rpp-toggle { … }`.
|
|
194
|
+
*
|
|
195
|
+
* Also shipped as `use-password-policy/styles.css` for use with `unstyled`.
|
|
196
|
+
*/
|
|
197
|
+
declare const passwordPolicyInputCss: string;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A small, bundled list of the most common passwords and password "base words".
|
|
201
|
+
* Matching is case-insensitive and also catches simple variations
|
|
202
|
+
* (trailing digits/symbols, a leading number, and common leet swaps),
|
|
203
|
+
* so "Password123!", "P@ssw0rd" and "qwerty2024" are all caught.
|
|
204
|
+
*
|
|
205
|
+
* It is intentionally small (a few KB) — for full coverage, pair it with
|
|
206
|
+
* `checkPwnedPassword` / `usePwnedPassword` (Have I Been Pwned).
|
|
207
|
+
*/
|
|
208
|
+
declare const COMMON_PASSWORDS: readonly string[];
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* use-password-policy/core
|
|
212
|
+
*
|
|
213
|
+
* Framework-free password validation. No React import, so it runs anywhere:
|
|
214
|
+
* the browser, Node, Deno, Bun, edge functions — which lets you enforce the
|
|
215
|
+
* exact same policy on the client and the server.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
declare const DEFAULT_OPTIONS: ResolvedPolicyOptions;
|
|
219
|
+
/**
|
|
220
|
+
* Ready-made policies. Spread one and override what you need:
|
|
221
|
+
* `usePasswordPolicy({ ...presets.nist, password })`
|
|
222
|
+
*/
|
|
223
|
+
declare const presets: {
|
|
224
|
+
/** The classic checklist (these are also the defaults): 8+ chars, upper, lower, number, symbol. */
|
|
225
|
+
classic: {
|
|
226
|
+
minLength: number;
|
|
227
|
+
lowercaseCheck: true;
|
|
228
|
+
uppercaseCheck: true;
|
|
229
|
+
numberCheck: true;
|
|
230
|
+
specialCharCheck: true;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* NIST SP 800-63B-4 (Aug 2025) for passwords used on their own:
|
|
234
|
+
* at least 15 characters, allow up to 64, no composition rules, block common passwords.
|
|
235
|
+
*/
|
|
236
|
+
nist: {
|
|
237
|
+
minLength: number;
|
|
238
|
+
maxLength: number;
|
|
239
|
+
lowercaseCheck: false;
|
|
240
|
+
uppercaseCheck: false;
|
|
241
|
+
numberCheck: false;
|
|
242
|
+
specialCharCheck: false;
|
|
243
|
+
commonPasswordCheck: true;
|
|
244
|
+
};
|
|
245
|
+
/** NIST SP 800-63B-4 when the password is one factor of MFA: at least 8 characters. */
|
|
246
|
+
nistMfa: {
|
|
247
|
+
minLength: number;
|
|
248
|
+
maxLength: number;
|
|
249
|
+
lowercaseCheck: false;
|
|
250
|
+
uppercaseCheck: false;
|
|
251
|
+
numberCheck: false;
|
|
252
|
+
specialCharCheck: false;
|
|
253
|
+
commonPasswordCheck: true;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
declare const DEFAULT_MESSAGES: Record<string, RuleMessage>;
|
|
257
|
+
/** Merge user options over the defaults, ignoring keys set to `undefined`. */
|
|
258
|
+
declare function resolveOptions(options?: PasswordPolicyOptions): ResolvedPolicyOptions;
|
|
259
|
+
/**
|
|
260
|
+
* `true` if the password is on the list, or is a list entry with simple
|
|
261
|
+
* decoration: trailing digits/symbols ("password123!"), a leading number
|
|
262
|
+
* ("123qwerty") or leet swaps ("p@ssw0rd").
|
|
263
|
+
*/
|
|
264
|
+
declare function isCommonPassword(password: string, list?: readonly string[]): boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Validate a password against a policy. Pure and synchronous.
|
|
267
|
+
*
|
|
268
|
+
* ```ts
|
|
269
|
+
* import { validatePassword, presets } from 'use-password-policy/core';
|
|
270
|
+
* const { isValid, errors } = validatePassword(req.body.password, presets.nist);
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
declare function validatePassword(password: string, options?: PasswordPolicyOptions): ValidationResult;
|
|
274
|
+
/** Anything shaped like the result of `zxcvbn(password)` or `@zxcvbn-ts/core`'s `zxcvbn(password)`. */
|
|
275
|
+
interface ZxcvbnLikeResult {
|
|
276
|
+
score: number;
|
|
277
|
+
feedback?: {
|
|
278
|
+
warning?: string | null;
|
|
279
|
+
suggestions?: readonly string[];
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
type ZxcvbnFn = (password: string, userInputs?: (string | number)[]) => ZxcvbnLikeResult;
|
|
283
|
+
/**
|
|
284
|
+
* Wrap zxcvbn as a `strengthEstimator`. Accepts either a function
|
|
285
|
+
* (`zxcvbn` package, @zxcvbn-ts v3) or an object with `check`
|
|
286
|
+
* (@zxcvbn-ts v4's `new ZxcvbnFactory(options)`).
|
|
287
|
+
*
|
|
288
|
+
* ```ts
|
|
289
|
+
* import { ZxcvbnFactory } from '@zxcvbn-ts/core';
|
|
290
|
+
* const zxcvbn = new ZxcvbnFactory(options);
|
|
291
|
+
* usePasswordPolicy({ password, strengthEstimator: fromZxcvbn(zxcvbn), minStrength: 3 });
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
declare function fromZxcvbn(zxcvbn: ZxcvbnFn | {
|
|
295
|
+
check: ZxcvbnFn;
|
|
296
|
+
}, userInputs?: (string | number)[]): StrengthEstimator;
|
|
297
|
+
/** Minimal shape of Zod's refinement context (works with Zod 3 and 4). */
|
|
298
|
+
interface ZodLikeRefinementCtx {
|
|
299
|
+
addIssue(issue: {
|
|
300
|
+
code: 'custom';
|
|
301
|
+
message: string;
|
|
302
|
+
}): void;
|
|
40
303
|
}
|
|
41
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Use your policy inside a Zod schema:
|
|
306
|
+
*
|
|
307
|
+
* ```ts
|
|
308
|
+
* const schema = z.object({ password: z.string().superRefine(zodPasswordRule(policy)) });
|
|
309
|
+
* ```
|
|
310
|
+
* Adds one issue per failed rule (or only the first with `{ allErrors: false }`).
|
|
311
|
+
*/
|
|
312
|
+
declare function zodPasswordRule(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
313
|
+
allErrors?: boolean | undefined;
|
|
314
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => void;
|
|
315
|
+
/**
|
|
316
|
+
* A validate function that returns `true` or the first error message.
|
|
317
|
+
* Drops straight into react-hook-form's `validate`, and works with anything
|
|
318
|
+
* that uses the same convention.
|
|
319
|
+
*
|
|
320
|
+
* ```ts
|
|
321
|
+
* register('password', { validate: passwordValidator(policy) })
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
declare function passwordValidator(options?: PasswordPolicyOptions): (value: string) => true | string;
|
|
42
325
|
|
|
43
|
-
export { type HookReturnValue, PasswordPolicyInput, type PasswordPolicyInputProps, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyRule, usePasswordPolicy };
|
|
326
|
+
export { COMMON_PASSWORDS, type CheckPwnedOptions, DEFAULT_MESSAGES, DEFAULT_OPTIONS, type HookReturnValue, PasswordPolicyInput, type PasswordPolicyInputProps, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyDefaults, type PolicyRule, type PwnedStatus, type Requirement, type ResolvedPolicyOptions, type RuleMessage, type StrengthEstimate, type StrengthEstimator, type StrengthLabel, type UsePwnedPasswordOptions, type UsePwnedPasswordResult, type ValidationResult, type ZodLikeRefinementCtx, type ZxcvbnLikeResult, checkPwnedPassword, fromZxcvbn, isCommonPassword, passwordPolicyInputCss, passwordValidator, presets, resolveOptions, usePasswordPolicy, usePwnedPassword, validatePassword, zodPasswordRule };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,43 +1,326 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
interface CheckPwnedOptions {
|
|
4
|
+
/** Abort the request (e.g. when the user keeps typing). */
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
/** Custom fetch implementation. Defaults to the global `fetch`. */
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
/** Range API base URL. Defaults to Have I Been Pwned. */
|
|
9
|
+
endpoint?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Ask HIBP to pad the response so its size doesn't hint at the result.
|
|
12
|
+
* Sends an `Add-Padding` header. Default `false`.
|
|
13
|
+
*/
|
|
14
|
+
padding?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How many times this password appears in known data breaches, using the
|
|
18
|
+
* Have I Been Pwned range API (k-anonymity). Only the first 5 characters of
|
|
19
|
+
* the password's SHA-1 hash leave the device — never the password itself.
|
|
20
|
+
*
|
|
21
|
+
* Resolves to `0` when the password was not found.
|
|
22
|
+
*/
|
|
23
|
+
declare function checkPwnedPassword(password: string, options?: CheckPwnedOptions): Promise<number>;
|
|
24
|
+
|
|
25
|
+
/** Built-in strength labels, weakest to strongest. */
|
|
26
|
+
type StrengthLabel = 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
27
|
+
/** Text for a rule: a plain string, or a function of the resolved options (handy for i18n). */
|
|
28
|
+
type RuleMessage = string | ((options: ResolvedPolicyOptions) => string);
|
|
29
|
+
/**
|
|
30
|
+
* A single validation rule. Used for both built-in and custom rules.
|
|
31
|
+
*/
|
|
3
32
|
interface PolicyRule {
|
|
33
|
+
/** Unique key. Appears in `policyState` and `requirements`. */
|
|
4
34
|
name: string;
|
|
35
|
+
/** For built-in rules: the option that switches the rule on. */
|
|
5
36
|
optionsKey?: keyof PasswordPolicyOptions;
|
|
6
|
-
|
|
37
|
+
/** Return `true` when the password passes. */
|
|
38
|
+
test: (password: string, options: ResolvedPolicyOptions) => boolean;
|
|
39
|
+
/** Human-readable requirement, e.g. "No spaces". Falls back to a prettified `name`. */
|
|
40
|
+
message?: RuleMessage;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of a strength estimator such as zxcvbn.
|
|
44
|
+
* `score` uses the zxcvbn scale: 0 (too guessable) … 4 (very unguessable).
|
|
45
|
+
*/
|
|
46
|
+
interface StrengthEstimate {
|
|
47
|
+
score: 0 | 1 | 2 | 3 | 4;
|
|
48
|
+
/** Optional hint to show the user, e.g. "This is a very common password". */
|
|
49
|
+
feedback?: string;
|
|
7
50
|
}
|
|
51
|
+
type StrengthEstimator = (password: string) => StrengthEstimate;
|
|
52
|
+
/** Every option is optional. Omitted options fall back to the defaults. */
|
|
8
53
|
interface PasswordPolicyOptions {
|
|
54
|
+
/** The password to validate (hook only — `validatePassword` takes it as the first argument). */
|
|
9
55
|
password?: string;
|
|
56
|
+
/** Minimum length. Default `8`. */
|
|
10
57
|
minLength?: number;
|
|
58
|
+
/** Maximum length. Default `0` (no maximum). */
|
|
59
|
+
maxLength?: number;
|
|
60
|
+
/** Require a lowercase letter. Default `true`. */
|
|
11
61
|
lowercaseCheck?: boolean;
|
|
62
|
+
/** Require an uppercase letter. Default `true`. */
|
|
12
63
|
uppercaseCheck?: boolean;
|
|
64
|
+
/** Require a digit. Default `true`. */
|
|
13
65
|
numberCheck?: boolean;
|
|
66
|
+
/** Require a special character. Default `true`. */
|
|
14
67
|
specialCharCheck?: boolean;
|
|
68
|
+
/** Reject very common passwords ("password", "qwerty123", "Password1!" …). Default `false`. */
|
|
69
|
+
commonPasswordCheck?: boolean;
|
|
70
|
+
/** Replace the built-in common-password list. */
|
|
71
|
+
commonPasswords?: readonly string[];
|
|
72
|
+
/**
|
|
73
|
+
* When set (even to `''`), adds a `match` rule that passes only if
|
|
74
|
+
* `password === confirmPassword`.
|
|
75
|
+
*/
|
|
76
|
+
confirmPassword?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Plug in a real strength estimator (e.g. zxcvbn via `fromZxcvbn`).
|
|
79
|
+
* When set, `strengthLabel` and `strengthPercent` come from the estimator.
|
|
80
|
+
*/
|
|
81
|
+
strengthEstimator?: StrengthEstimator;
|
|
82
|
+
/** With `strengthEstimator`: minimum score (0–4) required. Adds a `strength` rule. */
|
|
83
|
+
minStrength?: 0 | 1 | 2 | 3 | 4;
|
|
84
|
+
/** Your own rules, checked after the built-in ones. */
|
|
15
85
|
customRules?: PolicyRule[];
|
|
86
|
+
/** Override requirement text per rule name, e.g. `{ minLength: 'Mindestens 8 Zeichen' }`. */
|
|
87
|
+
messages?: Partial<Record<string, RuleMessage>>;
|
|
16
88
|
lowercaseRegex?: RegExp;
|
|
17
89
|
uppercaseRegex?: RegExp;
|
|
18
90
|
numberRegex?: RegExp;
|
|
19
91
|
specialCharRegex?: RegExp;
|
|
20
92
|
}
|
|
93
|
+
/** Options after defaults are applied. Passed to every rule's `test`. */
|
|
94
|
+
type ResolvedPolicyOptions = Required<Omit<PasswordPolicyOptions, 'password' | 'confirmPassword' | 'strengthEstimator' | 'minStrength'>> & Pick<PasswordPolicyOptions, 'confirmPassword' | 'strengthEstimator' | 'minStrength'>;
|
|
95
|
+
/** @deprecated Use `ResolvedPolicyOptions`. */
|
|
96
|
+
type PolicyDefaults = ResolvedPolicyOptions;
|
|
97
|
+
/** Pass/fail for each active rule, keyed by rule name. */
|
|
21
98
|
interface PasswordPolicyState {
|
|
22
99
|
[key: string]: boolean;
|
|
23
100
|
}
|
|
24
|
-
|
|
25
|
-
|
|
101
|
+
/** One row of a requirements checklist. */
|
|
102
|
+
interface Requirement {
|
|
103
|
+
name: string;
|
|
104
|
+
passed: boolean;
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
/** Result of `validatePassword` (and the hook). */
|
|
108
|
+
interface ValidationResult {
|
|
109
|
+
/** `true` only when every active rule passes. */
|
|
26
110
|
isValid: boolean;
|
|
27
|
-
|
|
28
|
-
strengthLabel: 'Very Weak' | 'Weak' | 'Medium' | 'Strong' | 'Very Strong';
|
|
111
|
+
/** Pass/fail per rule name. */
|
|
29
112
|
policyState: PasswordPolicyState;
|
|
113
|
+
/** Ordered checklist with human-readable messages. */
|
|
114
|
+
requirements: Requirement[];
|
|
115
|
+
/** Messages of the rules that failed, in order. Handy for form errors. */
|
|
116
|
+
errors: string[];
|
|
117
|
+
/** Number of rules that passed. */
|
|
118
|
+
strengthScore: number;
|
|
119
|
+
/** 0–1. Share of rules passed, or the estimator score / 4 when an estimator is set. */
|
|
120
|
+
strengthPercent: number;
|
|
121
|
+
strengthLabel: StrengthLabel;
|
|
122
|
+
/** The estimator's result, if `strengthEstimator` is set. */
|
|
123
|
+
estimate?: StrengthEstimate;
|
|
124
|
+
}
|
|
125
|
+
/** What `usePasswordPolicy` returns. */
|
|
126
|
+
interface HookReturnValue extends ValidationResult {
|
|
127
|
+
password?: string;
|
|
30
128
|
}
|
|
31
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Validate a password as the user types.
|
|
132
|
+
*
|
|
133
|
+
* Tip: if you pass an expensive `strengthEstimator` (like zxcvbn), memoize
|
|
134
|
+
* your options object so it is only recomputed when the password changes.
|
|
135
|
+
*/
|
|
32
136
|
declare const usePasswordPolicy: (options?: PasswordPolicyOptions) => HookReturnValue;
|
|
137
|
+
type PwnedStatus = 'idle' | 'checking' | 'safe' | 'pwned' | 'error';
|
|
138
|
+
interface UsePwnedPasswordOptions extends Omit<CheckPwnedOptions, 'signal'> {
|
|
139
|
+
/** Turn the check on/off (e.g. only once the policy passes). Default `true`. */
|
|
140
|
+
enabled?: boolean;
|
|
141
|
+
/** Wait this long after the last keystroke before checking. Default `500` ms. */
|
|
142
|
+
debounceMs?: number;
|
|
143
|
+
}
|
|
144
|
+
interface UsePwnedPasswordResult {
|
|
145
|
+
status: PwnedStatus;
|
|
146
|
+
/** Times seen in breaches (0 when safe or unknown). */
|
|
147
|
+
count: number;
|
|
148
|
+
isPwned: boolean;
|
|
149
|
+
error?: Error;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check the password against Have I Been Pwned while the user types
|
|
153
|
+
* (debounced, cancels stale requests). Only a 5-character hash prefix is sent.
|
|
154
|
+
*/
|
|
155
|
+
declare function usePwnedPassword(password: string, options?: UsePwnedPasswordOptions): UsePwnedPasswordResult;
|
|
33
156
|
|
|
34
|
-
interface PasswordPolicyInputProps extends React.ComponentPropsWithoutRef<'input'> {
|
|
157
|
+
interface PasswordPolicyInputProps extends Omit<React.ComponentPropsWithoutRef<'input'>, 'type' | 'value' | 'defaultValue'> {
|
|
158
|
+
/** Controlled value. Leave undefined to let the component manage its own state. */
|
|
159
|
+
value?: string;
|
|
160
|
+
/** Initial value when uncontrolled. */
|
|
161
|
+
defaultValue?: string;
|
|
162
|
+
/** Same options as `usePasswordPolicy`. */
|
|
35
163
|
policyOptions?: PasswordPolicyOptions;
|
|
164
|
+
/** Fires on every change with the new password and its full validation result. */
|
|
36
165
|
onPasswordChange?: (password: string, validation: HookReturnValue) => void;
|
|
166
|
+
/** Default `true`. */
|
|
37
167
|
showStrengthMeter?: boolean;
|
|
168
|
+
/** Show the strength label ("Strong") under the meter. Default `false`. */
|
|
169
|
+
showStrengthLabel?: boolean;
|
|
170
|
+
/** Default `true`. */
|
|
38
171
|
showRequirementsList?: boolean;
|
|
172
|
+
/** Default `true`. */
|
|
39
173
|
showToggleButton?: boolean;
|
|
174
|
+
/** Accessible labels for the show/hide button. */
|
|
175
|
+
toggleLabels?: {
|
|
176
|
+
show: string;
|
|
177
|
+
hide: string;
|
|
178
|
+
};
|
|
179
|
+
/** Skip the built-in CSS (class names stay). Import `use-password-policy/styles.css` or bring your own. */
|
|
180
|
+
unstyled?: boolean;
|
|
181
|
+
/** Class for the outer wrapper. */
|
|
182
|
+
className?: string;
|
|
183
|
+
/** Extra class for the <input> itself. */
|
|
184
|
+
inputClassName?: string;
|
|
185
|
+
}
|
|
186
|
+
declare const PasswordPolicyInput: React.ForwardRefExoticComponent<PasswordPolicyInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Styles for <PasswordPolicyInput />. Kept deliberately low-specificity:
|
|
190
|
+
* the theme variables sit on `:where(.rpp-root)` (zero specificity), so any
|
|
191
|
+
* class you pass via `className` wins; inner parts use a single class
|
|
192
|
+
* (e.g. `.rpp-toggle`), which still beats global element resets like
|
|
193
|
+
* `button { … }`. Override them with `.your-class .rpp-toggle { … }`.
|
|
194
|
+
*
|
|
195
|
+
* Also shipped as `use-password-policy/styles.css` for use with `unstyled`.
|
|
196
|
+
*/
|
|
197
|
+
declare const passwordPolicyInputCss: string;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A small, bundled list of the most common passwords and password "base words".
|
|
201
|
+
* Matching is case-insensitive and also catches simple variations
|
|
202
|
+
* (trailing digits/symbols, a leading number, and common leet swaps),
|
|
203
|
+
* so "Password123!", "P@ssw0rd" and "qwerty2024" are all caught.
|
|
204
|
+
*
|
|
205
|
+
* It is intentionally small (a few KB) — for full coverage, pair it with
|
|
206
|
+
* `checkPwnedPassword` / `usePwnedPassword` (Have I Been Pwned).
|
|
207
|
+
*/
|
|
208
|
+
declare const COMMON_PASSWORDS: readonly string[];
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* use-password-policy/core
|
|
212
|
+
*
|
|
213
|
+
* Framework-free password validation. No React import, so it runs anywhere:
|
|
214
|
+
* the browser, Node, Deno, Bun, edge functions — which lets you enforce the
|
|
215
|
+
* exact same policy on the client and the server.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
declare const DEFAULT_OPTIONS: ResolvedPolicyOptions;
|
|
219
|
+
/**
|
|
220
|
+
* Ready-made policies. Spread one and override what you need:
|
|
221
|
+
* `usePasswordPolicy({ ...presets.nist, password })`
|
|
222
|
+
*/
|
|
223
|
+
declare const presets: {
|
|
224
|
+
/** The classic checklist (these are also the defaults): 8+ chars, upper, lower, number, symbol. */
|
|
225
|
+
classic: {
|
|
226
|
+
minLength: number;
|
|
227
|
+
lowercaseCheck: true;
|
|
228
|
+
uppercaseCheck: true;
|
|
229
|
+
numberCheck: true;
|
|
230
|
+
specialCharCheck: true;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* NIST SP 800-63B-4 (Aug 2025) for passwords used on their own:
|
|
234
|
+
* at least 15 characters, allow up to 64, no composition rules, block common passwords.
|
|
235
|
+
*/
|
|
236
|
+
nist: {
|
|
237
|
+
minLength: number;
|
|
238
|
+
maxLength: number;
|
|
239
|
+
lowercaseCheck: false;
|
|
240
|
+
uppercaseCheck: false;
|
|
241
|
+
numberCheck: false;
|
|
242
|
+
specialCharCheck: false;
|
|
243
|
+
commonPasswordCheck: true;
|
|
244
|
+
};
|
|
245
|
+
/** NIST SP 800-63B-4 when the password is one factor of MFA: at least 8 characters. */
|
|
246
|
+
nistMfa: {
|
|
247
|
+
minLength: number;
|
|
248
|
+
maxLength: number;
|
|
249
|
+
lowercaseCheck: false;
|
|
250
|
+
uppercaseCheck: false;
|
|
251
|
+
numberCheck: false;
|
|
252
|
+
specialCharCheck: false;
|
|
253
|
+
commonPasswordCheck: true;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
declare const DEFAULT_MESSAGES: Record<string, RuleMessage>;
|
|
257
|
+
/** Merge user options over the defaults, ignoring keys set to `undefined`. */
|
|
258
|
+
declare function resolveOptions(options?: PasswordPolicyOptions): ResolvedPolicyOptions;
|
|
259
|
+
/**
|
|
260
|
+
* `true` if the password is on the list, or is a list entry with simple
|
|
261
|
+
* decoration: trailing digits/symbols ("password123!"), a leading number
|
|
262
|
+
* ("123qwerty") or leet swaps ("p@ssw0rd").
|
|
263
|
+
*/
|
|
264
|
+
declare function isCommonPassword(password: string, list?: readonly string[]): boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Validate a password against a policy. Pure and synchronous.
|
|
267
|
+
*
|
|
268
|
+
* ```ts
|
|
269
|
+
* import { validatePassword, presets } from 'use-password-policy/core';
|
|
270
|
+
* const { isValid, errors } = validatePassword(req.body.password, presets.nist);
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
declare function validatePassword(password: string, options?: PasswordPolicyOptions): ValidationResult;
|
|
274
|
+
/** Anything shaped like the result of `zxcvbn(password)` or `@zxcvbn-ts/core`'s `zxcvbn(password)`. */
|
|
275
|
+
interface ZxcvbnLikeResult {
|
|
276
|
+
score: number;
|
|
277
|
+
feedback?: {
|
|
278
|
+
warning?: string | null;
|
|
279
|
+
suggestions?: readonly string[];
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
type ZxcvbnFn = (password: string, userInputs?: (string | number)[]) => ZxcvbnLikeResult;
|
|
283
|
+
/**
|
|
284
|
+
* Wrap zxcvbn as a `strengthEstimator`. Accepts either a function
|
|
285
|
+
* (`zxcvbn` package, @zxcvbn-ts v3) or an object with `check`
|
|
286
|
+
* (@zxcvbn-ts v4's `new ZxcvbnFactory(options)`).
|
|
287
|
+
*
|
|
288
|
+
* ```ts
|
|
289
|
+
* import { ZxcvbnFactory } from '@zxcvbn-ts/core';
|
|
290
|
+
* const zxcvbn = new ZxcvbnFactory(options);
|
|
291
|
+
* usePasswordPolicy({ password, strengthEstimator: fromZxcvbn(zxcvbn), minStrength: 3 });
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
declare function fromZxcvbn(zxcvbn: ZxcvbnFn | {
|
|
295
|
+
check: ZxcvbnFn;
|
|
296
|
+
}, userInputs?: (string | number)[]): StrengthEstimator;
|
|
297
|
+
/** Minimal shape of Zod's refinement context (works with Zod 3 and 4). */
|
|
298
|
+
interface ZodLikeRefinementCtx {
|
|
299
|
+
addIssue(issue: {
|
|
300
|
+
code: 'custom';
|
|
301
|
+
message: string;
|
|
302
|
+
}): void;
|
|
40
303
|
}
|
|
41
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Use your policy inside a Zod schema:
|
|
306
|
+
*
|
|
307
|
+
* ```ts
|
|
308
|
+
* const schema = z.object({ password: z.string().superRefine(zodPasswordRule(policy)) });
|
|
309
|
+
* ```
|
|
310
|
+
* Adds one issue per failed rule (or only the first with `{ allErrors: false }`).
|
|
311
|
+
*/
|
|
312
|
+
declare function zodPasswordRule(options?: PasswordPolicyOptions, { allErrors }?: {
|
|
313
|
+
allErrors?: boolean | undefined;
|
|
314
|
+
}): (value: string, ctx: ZodLikeRefinementCtx) => void;
|
|
315
|
+
/**
|
|
316
|
+
* A validate function that returns `true` or the first error message.
|
|
317
|
+
* Drops straight into react-hook-form's `validate`, and works with anything
|
|
318
|
+
* that uses the same convention.
|
|
319
|
+
*
|
|
320
|
+
* ```ts
|
|
321
|
+
* register('password', { validate: passwordValidator(policy) })
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
declare function passwordValidator(options?: PasswordPolicyOptions): (value: string) => true | string;
|
|
42
325
|
|
|
43
|
-
export { type HookReturnValue, PasswordPolicyInput, type PasswordPolicyInputProps, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyRule, usePasswordPolicy };
|
|
326
|
+
export { COMMON_PASSWORDS, type CheckPwnedOptions, DEFAULT_MESSAGES, DEFAULT_OPTIONS, type HookReturnValue, PasswordPolicyInput, type PasswordPolicyInputProps, type PasswordPolicyOptions, type PasswordPolicyState, type PolicyDefaults, type PolicyRule, type PwnedStatus, type Requirement, type ResolvedPolicyOptions, type RuleMessage, type StrengthEstimate, type StrengthEstimator, type StrengthLabel, type UsePwnedPasswordOptions, type UsePwnedPasswordResult, type ValidationResult, type ZodLikeRefinementCtx, type ZxcvbnLikeResult, checkPwnedPassword, fromZxcvbn, isCommonPassword, passwordPolicyInputCss, passwordValidator, presets, resolveOptions, usePasswordPolicy, usePwnedPassword, validatePassword, zodPasswordRule };
|