playbook_ui 11.12.1.pre.alpha.passphrase1 → 11.12.1

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_passphrase/_passphrase.jsx +97 -56
  3. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.html.erb +1 -145
  4. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.jsx +3 -127
  5. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_breached.md +2 -11
  6. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.jsx +8 -90
  7. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.html.erb +2 -0
  8. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.jsx +20 -6
  9. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_default.md +1 -0
  10. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.html.erb +2 -2
  11. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_input_props.jsx +1 -1
  12. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.html.erb +5 -318
  13. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +48 -134
  14. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.md +5 -11
  15. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.jsx +20 -96
  16. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.md +2 -6
  17. data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +0 -4
  18. data/app/pb_kits/playbook/pb_passphrase/docs/index.js +0 -1
  19. data/app/pb_kits/playbook/pb_passphrase/passphrase.html.erb +1 -1
  20. data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +9 -5
  21. data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +47 -0
  22. data/app/pb_kits/playbook/pb_passphrase/passwordStrength.js +55 -0
  23. data/app/pb_kits/playbook/pb_passphrase/useHaveIBeenPwned.js +52 -0
  24. data/app/pb_kits/playbook/pb_passphrase/useZxcvbn.js +58 -0
  25. data/lib/playbook/version.rb +2 -2
  26. metadata +8 -9
  27. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.html.erb +0 -136
  28. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_common.md +0 -5
  29. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_confirmation.html.erb +0 -51
  30. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_confirmation.jsx +0 -39
  31. data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_strength_change.html.erb +0 -123
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a2d4fa765df74ba9a5c6abd8ec9bd670371bb4b752bbdec10225d4d35d66293
4
- data.tar.gz: ce21ba1dc523fc7875b39a604a0b63f7c54b5f62cd27297a2209f3cfa1b42dc2
3
+ metadata.gz: 3257a43d2c224d202dcfa36f64ae30921ed80d752b8db6927059ebbe7032d6e6
4
+ data.tar.gz: 573bb60fedf4fc904f3832026d46ac23ee22dc965792801565c9006449dbdcc7
5
5
  SHA512:
6
- metadata.gz: dace3a848d70cbc4cc6e854aa8bd0c687c8acc6485f37235daabbd18acab8b6393e8e4e2a4c021deb64811303c60364ac8fb43e00201bdd9f0e3f1c59f205d5c
7
- data.tar.gz: 7fb618f8c14835809a4b2d02d0afac8476048a5c2209e846466999263a68021ec0ba069ad40bab7b8d7ff72c21952c37d4bc1a887c5e6e93e11214298ae93e7d
6
+ metadata.gz: 38e8c49e2e18ae015912af7ac41da7c6a9fc57b7e201f4ca55c9edb2ea93d0114c917963c98f67972aa4cf50dda11cb4ec40f494e4da260403d63fa9e5920e4a
7
+ data.tar.gz: 9421ac89e00399825a91ed280324cd1b1a4a43aa8310b88c2143f21724d818c26aeca9af36e82cc9cf55e2e254a059d03d6c356364228631e82a4b8589d0a35e
@@ -1,10 +1,14 @@
1
1
  /* @flow */
2
+ /* eslint-disable react-hooks/rules-of-hooks */
2
3
 
3
- import React, { useCallback, useMemo, useState } from "react"
4
- import classnames from "classnames"
4
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
5
+ import classnames from 'classnames'
5
6
 
6
- import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props"
7
- import { globalProps } from "../utilities/globalProps"
7
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
8
+ import { globalProps } from '../utilities/globalProps'
9
+
10
+ import useZxcvbn from './useZxcvbn'
11
+ import useHaveIBeenPwned from './useHaveIBeenPwned'
8
12
 
9
13
  import Body from '../pb_body/_body'
10
14
  import Caption from '../pb_caption/_caption'
@@ -12,10 +16,14 @@ import CircleIconButton from '../pb_circle_icon_button/_circle_icon_button'
12
16
  import Flex from '../pb_flex/_flex'
13
17
  import Icon from '../pb_icon/_icon'
14
18
  import PbReactPopover from '../pb_popover/_popover'
19
+ import ProgressSimple from '../pb_progress_simple/_progress_simple'
15
20
  import TextInput from '../pb_text_input/_text_input'
16
21
 
17
22
  type PassphraseProps = {
18
23
  aria?: object,
24
+ averageThreshold?: number,
25
+ checkPwned?: boolean,
26
+ common?: boolean,
19
27
  confirmation?: boolean,
20
28
  className?: string,
21
29
  data?: object,
@@ -23,8 +31,11 @@ type PassphraseProps = {
23
31
  id?: string,
24
32
  inputProps?: {},
25
33
  label?: string,
34
+ minLength?: number,
26
35
  onChange: (String) => void,
27
- showTipsBelow?: "always" | "xs" | "sm" | "md" | "lg" | "xl",
36
+ showTipsBelow?: 'always' | 'xs' | 'sm' | 'md' | 'lg' | 'xl',
37
+ onStrengthChange?: (number) => void,
38
+ strongThreshold?: number,
28
39
  tips?: Array<string>,
29
40
  uncontrolled?: boolean,
30
41
  value: string,
@@ -33,56 +44,71 @@ type PassphraseProps = {
33
44
  const Passphrase = (props: PassphraseProps) => {
34
45
  const {
35
46
  aria = {},
47
+ averageThreshold = 2,
48
+ checkPwned = false,
36
49
  className,
50
+ common = false,
37
51
  confirmation = false,
38
- data = {},
39
52
  dark = false,
53
+ data = {},
40
54
  id,
41
55
  inputProps = {},
42
- label = confirmation ? "Confirm Passphrase" : "Passphrase",
56
+ label = confirmation ? 'Confirm Passphrase' : 'Passphrase',
57
+ minLength = 12,
43
58
  onChange = () => {},
44
- showTipsBelow = "always",
59
+ showTipsBelow = 'always',
60
+ onStrengthChange,
61
+ strongThreshold = 3,
45
62
  tips = [],
46
63
  uncontrolled = false,
47
- value = "",
64
+ value = '',
48
65
  } = props
66
+ const ariaProps = buildAriaProps(aria)
49
67
 
50
- const [uncontrolledValue, setUncontrolledValue] = useState("")
51
- const [showPopover, setShowPopover] = useState(false)
52
- const [showPassphrase, setShowPassphrase] = useState(false)
68
+ const [uncontrolledValue, setUncontrolledValue] = useState('')
53
69
 
54
70
  const handleChange = useCallback(
55
- (e) => (uncontrolled ? setUncontrolledValue(e.target.value) : onChange(e)),
71
+ (e) => uncontrolled ? setUncontrolledValue(e.target.value) : onChange(e),
56
72
  [uncontrolled, onChange]
57
73
  )
58
74
 
59
75
  const displayValue = useMemo(
60
76
  () => (uncontrolled ? uncontrolledValue : value),
61
- [value, uncontrolledValue, uncontrolled]
77
+ [value, uncontrolledValue, uncontrolled],
62
78
  )
63
79
 
80
+ const [showPopover, setShowPopover] = useState(false)
64
81
  const toggleShowPopover = () => setShowPopover(!showPopover)
65
82
  const handleShouldClosePopover = (shouldClosePopover) => {
66
83
  setShowPopover(!shouldClosePopover)
67
84
  }
68
85
 
86
+ const [showPassphrase, setShowPassphrase] = useState(false)
69
87
  const toggleShowPassphrase = (e) => {
70
88
  e.preventDefault()
71
89
  setShowPassphrase(!showPassphrase)
72
90
  }
73
91
 
92
+ const classes = classnames(buildCss('pb_passphrase'), globalProps(props), className)
93
+
94
+ const isPwned = checkPwned ? useHaveIBeenPwned(displayValue, minLength) : false
95
+
96
+ const { percent: progressPercent, variant: progressVariant, text: strengthLabel, strength } = useZxcvbn({ passphrase: displayValue, common, isPwned, confirmation, averageThreshold, minLength, strongThreshold })
97
+
98
+ useEffect(() => {
99
+ if (typeof onStrengthChange === 'function') {
100
+ onStrengthChange(strength)
101
+ }
102
+ }, [strength])
103
+
74
104
  const tipClass = classnames(
75
- "passphrase-popover",
76
- showTipsBelow === "always" ? null : `show-below-${showTipsBelow}`
105
+ 'passphrase-popover',
106
+ (showTipsBelow === 'always' ? null : `show-below-${showTipsBelow}`),
77
107
  )
78
-
79
- const ariaProps = buildAriaProps(aria)
80
- const classes = classnames(
81
- buildCss("pb_passphrase"),
82
- globalProps(props),
83
- className
108
+ const dataProps = useMemo(
109
+ () => (buildDataProps(Object.assign({}, data, { strength }))),
110
+ [data, strength]
84
111
  )
85
- const dataProps = buildDataProps(data)
86
112
 
87
113
  const popoverReference = (
88
114
  <CircleIconButton
@@ -95,17 +121,17 @@ const Passphrase = (props: PassphraseProps) => {
95
121
  )
96
122
 
97
123
  return (
98
- <div
99
- {...ariaProps}
100
- {...dataProps}
101
- className={classes}
124
+ <div
125
+ {...ariaProps}
126
+ {...dataProps}
127
+ className={classes}
102
128
  id={id}
103
129
  >
104
130
  <label>
105
131
  <Flex align="baseline">
106
- <Caption
107
- className="passphrase-label"
108
- text={label}
132
+ <Caption
133
+ className="passphrase-label"
134
+ text={label}
109
135
  />
110
136
  <If condition={tips.length > 0 && !confirmation}>
111
137
  <PbReactPopover
@@ -116,28 +142,30 @@ const Passphrase = (props: PassphraseProps) => {
116
142
  shouldClosePopover={handleShouldClosePopover}
117
143
  show={showPopover}
118
144
  >
119
- <Flex
120
- align="center"
145
+ <Flex
146
+ align="center"
121
147
  orientation="column"
122
148
  >
123
- <Caption
124
- marginBottom="xs"
125
- text="Tips for a good passphrase"
149
+ <Caption
150
+ marginBottom="xs"
151
+ text="Tips for a good passphrase"
126
152
  />
127
153
  <div>
128
- {tips.map((tip, i) => (
129
- <Caption
130
- key={i}
131
- marginBottom="xs"
132
- size="xs"
133
- >
134
- <Icon
135
- icon="shield-check"
136
- marginRight="xs"
137
- />
138
- {tip}
139
- </Caption>
140
- ))}
154
+ {
155
+ tips.map((tip, i) => (
156
+ <Caption
157
+ key={i}
158
+ marginBottom="xs"
159
+ size="xs"
160
+ >
161
+ <Icon
162
+ icon="shield-check"
163
+ marginRight="xs"
164
+ />
165
+ {tip}
166
+ </Caption>
167
+ ))
168
+ }
141
169
  </div>
142
170
  </Flex>
143
171
  </PbReactPopover>
@@ -150,23 +178,23 @@ const Passphrase = (props: PassphraseProps) => {
150
178
  marginBottom="xs"
151
179
  onChange={handleChange}
152
180
  placeholder="Enter a passphrase..."
153
- type={showPassphrase ? "text" : "password"}
181
+ type={showPassphrase ? 'text' : 'password'}
154
182
  value={displayValue}
155
183
  {...inputProps}
156
184
  />
157
- <span
158
- className="show-passphrase-icon"
185
+ <span
186
+ className="show-passphrase-icon"
159
187
  onClick={toggleShowPassphrase}
160
188
  >
161
189
  <Body
162
- className={showPassphrase ? "hide-icon" : ""}
190
+ className={showPassphrase ? 'hide-icon' : ''}
163
191
  color="light"
164
192
  dark={dark}
165
193
  >
166
194
  <Icon icon="eye-slash" />
167
195
  </Body>
168
196
  <Body
169
- className={showPassphrase ? "" : "hide-icon"}
197
+ className={showPassphrase ? '' : 'hide-icon'}
170
198
  color="light"
171
199
  dark={dark}
172
200
  >
@@ -175,8 +203,21 @@ const Passphrase = (props: PassphraseProps) => {
175
203
  </span>
176
204
  </div>
177
205
  </label>
206
+ <If condition={!confirmation}>
207
+ <ProgressSimple
208
+ className={displayValue.length === 0 ? 'progress-empty-input' : null}
209
+ dark={dark}
210
+ percent={progressPercent}
211
+ variant={progressVariant}
212
+ />
213
+ <Caption
214
+ dark={dark}
215
+ size="xs"
216
+ text={strengthLabel}
217
+ />
218
+ </If>
178
219
  </div>
179
- );
180
- };
220
+ )
221
+ }
181
222
 
182
- export default Passphrase;
223
+ export default Passphrase
@@ -1,145 +1 @@
1
- <%= pb_rails("passphrase", props: { label: "Passphrase", classname: "passphrase_breached" }) %>
2
-
3
- <%= pb_rails("progress_simple", props: { percent: 0, id: "bar_breached" }) %>
4
-
5
- <%= pb_rails("caption", props: { size: 'xs', text: "hello", id: "caption_breached" }) %>
6
-
7
- <%= javascript_tag do %>
8
- window.addEventListener("DOMContentLoaded", () => {
9
-
10
- // variables for the kits you are targeting
11
- const passphrase = document.querySelector(".passphrase_breached").querySelector("input")
12
- const barVariant = document.getElementById("bar_breached")
13
- const barPercent = document.getElementById("bar_breached").querySelector("div")
14
- const caption = document.getElementById("caption_breached")
15
-
16
- // hide the bar and captions
17
- barVariant.style.display = 'none';
18
- barPercent.style.display = 'none';
19
- caption.style.display = 'none';
20
-
21
-
22
- const handleStrengthCalculation = (settings) => {
23
- const {
24
- passphrase = "",
25
- common = false,
26
- isPwned = false,
27
- averageThreshold = 2,
28
- minLength = 12,
29
- strongThreshold = 3,
30
- } = settings
31
-
32
- const resultByScore = {
33
- 0: {
34
- variant: 'negative',
35
- label: '',
36
- percent: 0,
37
- },
38
- 1: {
39
- variant: 'negative',
40
- label: 'This passphrase is too common',
41
- percent: 25,
42
- },
43
- 2: {
44
- variant: 'negative',
45
- label: 'Too weak',
46
- percent: 25,
47
- },
48
- 3: {
49
- variant: 'warning',
50
- label: 'Almost there, keep going!',
51
- percent: 50,
52
- },
53
- 4: {
54
- variant: 'positive',
55
- label: 'Success! Strong passphrase',
56
- percent: 100,
57
- }
58
- }
59
-
60
- const { score } = zxcvbn(passphrase);
61
-
62
- const noPassphrase = passphrase.length <= 0
63
- const commonPassphrase = common || isPwned
64
- const weakPassphrase = passphrase.length < minLength || score < averageThreshold
65
- const averagePassphrase = score < strongThreshold
66
- const strongPassphrase = score >= strongThreshold
67
-
68
- if (noPassphrase) {
69
- return {...resultByScore[0], score}
70
- } else if (commonPassphrase) {
71
- return {...resultByScore[1], score}
72
- } else if (weakPassphrase) {
73
- return {...resultByScore[2], score}
74
- } else if (averagePassphrase){
75
- return {...resultByScore[3], score}
76
- } else if (strongPassphrase) {
77
- return {...resultByScore[4], score}
78
- }
79
- }
80
-
81
- // event listeners attached to the input field
82
- passphrase.addEventListener('input', (e) => {
83
- const passphrase = e.target.value;
84
-
85
- let pwndMatch = false
86
-
87
- const checkHaveIBeenPwned = async function (passphrase) {
88
- const buffer = new TextEncoder('utf-8').encode(passphrase)
89
- const digest = await crypto.subtle.digest('SHA-1', buffer)
90
- const hashArray = Array.from(new Uint8Array(digest))
91
- const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
92
-
93
- const firstFive = hashHex.slice(0, 5)
94
- const endOfHash = hashHex.slice(5)
95
-
96
- const resp = await fetch(`https://api.pwnedpasswords.com/range/${firstFive}`)
97
- const text = await resp.text()
98
-
99
- if (passphrase.length < 5) {
100
- pwndMatch = false
101
- }
102
- else {
103
- pwndMatch = text.split('\n').some((line) => {
104
- return line.split(':')[0] === endOfHash.toUpperCase()
105
- })
106
- }
107
-
108
- // pass in passphrase and isPwnd match to the handleStrengthCalculation and set that equal to result variable
109
- const result = handleStrengthCalculation({ passphrase: passphrase, isPwned: pwndMatch });
110
-
111
-
112
- // conditional statment to show or hide progress_simple bar and caption if user has entered a password
113
- if (passphrase) {
114
- barVariant.style.display = 'block';
115
-
116
- barPercent.style.display = 'block';
117
-
118
- caption.style.display = 'block';
119
- } else {
120
- barVariant.style.display = 'none';
121
-
122
- barPercent.style.display = 'none';
123
-
124
- caption.style.display = 'none';
125
- }
126
-
127
- // set the width of the progress_simple kit
128
- barPercent.style.width = result.percent.toString()+ "%"
129
-
130
-
131
- // set the variant of the progress_simple kit
132
- barVariant.setAttribute("class", "pb_progress_simple_kit_"+ result.variant +"_left");
133
-
134
-
135
- // set the text of the caption kit
136
- caption.textContent = result.label
137
- }
138
-
139
- checkHaveIBeenPwned(passphrase)
140
-
141
-
142
- });
143
-
144
- })
145
- <% end %>
1
+ <%= pb_rails("passphrase", props: { check_pwned: true }) %>
@@ -1,146 +1,22 @@
1
- import React, { useState, useEffect } from 'react'
1
+ import React, { useState } from 'react'
2
2
 
3
- import { Caption, Passphrase, ProgressSimple } from '../..'
4
- import zxcvbn from 'zxcvbn'
3
+ import Passphrase from '../_passphrase'
5
4
 
6
5
  const PassphraseBreached = (props) => {
7
6
  const [input, setInput] = useState('')
8
- const [isPwned, setIsPwned] = useState(false)
9
- const [checkStrength, setCheckStrength] = useState({
10
- label: '',
11
- percent: 0,
12
- score: 0,
13
- variant: ''
14
- })
15
-
16
7
 
17
8
  const handleChange = (e) => setInput(e.target.value)
18
9
 
19
- const checkHaveIBeenPwned = async function (passphrase) {
20
- const buffer = new TextEncoder('utf-8').encode(passphrase)
21
- const digest = await crypto.subtle.digest('SHA-1', buffer)
22
- const hashArray = Array.from(new Uint8Array(digest))
23
- const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
24
-
25
- const firstFive = hashHex.slice(0, 5)
26
- const endOfHash = hashHex.slice(5)
27
-
28
- const resp = await fetch(`https://api.pwnedpasswords.com/range/${firstFive}`)
29
- const text = await resp.text()
30
-
31
- const match = text.split('\n').some((line) => {
32
- //Each line is <sha-1-hash-suffix>:<count of incidents>
33
- return line.split(':')[0] === endOfHash.toUpperCase()
34
- })
35
- return match
36
- }
37
-
38
- const handleStrengthCalculation = (settings) => {
39
- const {
40
- passphrase = "",
41
- common = false,
42
- isPwned = false,
43
- averageThreshold = 2,
44
- minLength = 12,
45
- strongThreshold = 3,
46
- } = settings
47
-
48
- const resultByScore = {
49
- 0: {
50
- variant: 'negative',
51
- label: '',
52
- percent: 0,
53
- },
54
- 1: {
55
- variant: 'negative',
56
- label: 'This passphrase is too common',
57
- percent: 25,
58
- },
59
- 2: {
60
- variant: 'negative',
61
- label: 'Too weak',
62
- percent: 25,
63
- },
64
- 3: {
65
- variant: 'warning',
66
- label: 'Almost there, keep going!',
67
- percent: 50,
68
- },
69
- 4: {
70
- variant: 'positive',
71
- label: 'Success! Strong passphrase',
72
- percent: 100,
73
- }
74
- }
75
-
76
- const { score } = zxcvbn(passphrase);
77
-
78
- const noPassphrase = passphrase.length <= 0
79
- const commonPassphrase = common || isPwned
80
- const weakPassphrase = passphrase.length < minLength || score < averageThreshold
81
- const averagePassphrase = score < strongThreshold
82
- const strongPassphrase = score >= strongThreshold
83
-
84
- if (noPassphrase) {
85
- return {...resultByScore[0], score}
86
- } else if (commonPassphrase) {
87
- return {...resultByScore[1], score}
88
- } else if (weakPassphrase) {
89
- return {...resultByScore[2], score}
90
- } else if (averagePassphrase){
91
- return {...resultByScore[3], score}
92
- } else if (strongPassphrase) {
93
- return {...resultByScore[4], score}
94
- }
95
- }
96
-
97
- useEffect(
98
- () => {
99
- const delay = 400
100
- const result = handleStrengthCalculation({ passphrase: input, isPwned: isPwned });
101
-
102
- setCheckStrength({ ...result })
103
-
104
- // only check the API for passphrases above the minimum size
105
- if (input.length < 5) {
106
- setIsPwned(false)
107
- return
108
- }
109
-
110
- const handler = setTimeout(() => {
111
- checkHaveIBeenPwned(input)
112
- .then((pwned) => setIsPwned(pwned))
113
- .catch(() => setIsPwned(false))
114
- }, delay)
115
-
116
- return () => {
117
- clearTimeout(handler)
118
- }
119
- },
120
- [input, isPwned]
121
- )
122
-
123
10
  return (
124
11
  <>
125
12
  <div>
126
13
  <br />
127
14
  <Passphrase
15
+ checkPwned
128
16
  onChange={handleChange}
129
17
  value={input}
130
18
  {...props}
131
19
  />
132
- {checkStrength.percent > 0 ?
133
- <>
134
- <ProgressSimple
135
- className={input.length === 0 ? "progress-empty-input" : null}
136
- percent={checkStrength.percent}
137
- variant={checkStrength.variant}
138
- />
139
- <Caption size='xs'
140
- text={checkStrength.label}
141
- />
142
- </>
143
- : null}
144
20
  </div>
145
21
  </>
146
22
  )
@@ -1,12 +1,3 @@
1
- Use <a href='https://haveibeenpwned.com/Passwords'>HaveIBeenPwned's</a> API to check for breached passwords.
2
-
3
- As the passphrase is typed, it is checked against more than half a billion breached passwords, to help ensure its not compromised.
1
+ Use `checkPwned | checked_pwned` prop to enable checking against <a href='https://haveibeenpwned.com/Passwords'>HaveIBeenPwned's</a> API. As the passphrase is typed, it is checked against more than half a billion breached passwords, to help ensure its not compromised.
4
2
  Should it fail, the feedback will express the passphrase is too common, prompting the user to change.
5
-
6
- This uses their k-Anonymity model, so only the first 5 characters of a hashed copy of the passphrase are sent.
7
-
8
- <div class="pb_pill_kit_warning"><div class="pb_title_kit_size_4 pb_pill_text">Disclaimer</div></div>
9
-
10
- This example depends on the `zxcvbn` library and `haveibeenpwned` API.
11
-
12
- You can use any library to achieve the same result, this example only intends to show how to add more features to the `Passphrase` kit.
3
+ This uses their k-Anonymity model, so only the first 5 characters of a hashed copy of the passphrase are sent.