playbook_ui 11.12.1.pre.alpha.passphrase1 → 11.12.1

Sign up to get free protection for your applications and to get access to all the features.
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.