Skip to content

Instantly share code, notes, and snippets.

@detroitenglish
Last active January 10, 2020 00:04
Show Gist options
  • Save detroitenglish/0ccd3d47ceff2a68cd116a40a0edf726 to your computer and use it in GitHub Desktop.
Save detroitenglish/0ccd3d47ceff2a68cd116a40a0edf726 to your computer and use it in GitHub Desktop.
Fastify Handler: Password Scoring with zxcvbn + haveibeenpwned Range Query API
import zxcvbn from 'zxcvbn'
import axios from 'axios'
import crypto from 'crypto'
// Input is first validated via native Fastify JSON-schema declaration
export async function passwordStrengthChecker(req, reply) {
const { password } = req.body
let message, pwned, ok
let { score } = zxcvbn(password)
try {
pwned = await pwnedPassword(password)
ok = true
} catch (err) {
req.log.error(err)
ok = false
pwned = 1
message = err.message
}
if (pwned) score = 0
reply.send({ ok, score, pwned, message })
}
const pwnedUrl = p => `https://api.pwnedpasswords.com/range/${p}`
async function pwnedPassword(pw) {
const hash = Array.from(
await crypto
.createHash('sha1')
.update(pw)
.digest('hex')
.toUpperCase()
)
const prefix = hash.splice(0, 5).join('')
const suffix = hash.join('')
let result = await axios({
url: pwnedUrl(prefix),
method: 'GET',
})
.then(result => result.data)
.catch(err => { // something done goofed
throw new Error(`Unable to check password pwnage`)
})
if (!result.includes(suffix)) {
return 0
}
result = result.split('\r\n')
// Update: realized a hash table wasn't necessary
const match = result.find(r => r.includes(suffix))
const hits = match.split(':')[1]
return +hits
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment