Vuetify + zxcvbnでパスワード強度メーター付きのパスワードフォームを作る

マテリアルデザインをお手軽に使える[Vuetify](https://vuetifyjs.com/ja/)とパスワード強度チェッカーの[zxcvbn](https://github.com/dropbox/zxcvbn)を使って、パスワード強度メーター付きのパスワード入力フォームをさくっと作ります。

# サンプル
CodePenで作っています。
<p class="codepen" data-height="574" data-theme-id="dark" data-default-tab="js,result" data-user="harashoo" data-slug-hash="PoqKORg" style="height: 574px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuetify Password Form With Password Strength Meter">
  <span>See the Pen <a href="https://codepen.io/harashoo/pen/PoqKORg">
  Vuetify Password Form With Password Strength Meter</a> by harasho (<a href="https://codepen.io/harashoo">@harashoo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# 解説
### zxcvbnについて
zxcvbnはDropbox社製のパスワード強度チェックライブラリです。
https://github.com/dropbox/zxcvbn

実際に試してみると、以下のようにパスワード強度をチェックしてくれます。

```sh
$ npm install zxcvbn
$ node

> const zxcvbn = require('zxcvbn');
> zxcvbn('password');
{
  password: 'password',
  guesses: 3,
  guesses_log10: 0.4771212547196623,
  sequence: [
    {
      pattern: 'dictionary',
      i: 0,
      j: 7,
      token: 'password',
      matched_word: 'password',
      rank: 2,
      dictionary_name: 'passwords',
      reversed: false,
      l33t: false,
      base_guesses: 2,
      uppercase_variations: 1,
      l33t_variations: 1,
      guesses: 2,
      guesses_log10: 0.30102999566398114
    }
  ],
  calc_time: 9,
  crack_times_seconds: {
    online_throttling_100_per_hour: 108,
    online_no_throttling_10_per_second: 0.3,
    offline_slow_hashing_1e4_per_second: 0.0003,
    offline_fast_hashing_1e10_per_second: 3e-10
  },
  crack_times_display: {
    online_throttling_100_per_hour: '2 minutes',
    online_no_throttling_10_per_second: 'less than a second',
    offline_slow_hashing_1e4_per_second: 'less than a second',
    offline_fast_hashing_1e10_per_second: 'less than a second'
  },
  score: 0,
  feedback: {
    warning: 'This is a top-10 common password',
    suggestions: [ 'Add another word or two. Uncommon words are better.' ]
  }
}
```

今回は上記の返ってきたオブジェクトにある`score`を使います。

```
> zxcvbn('password').score
0
> zxcvbn('hoge').score
1
> zxcvbn('Tr0ub4dour&3').score
2
```

※ zxcvbnについては、[こちらの翻訳記事](https://qiita.com/hibara/items/37ba7a1f8ba2d54ae600)が詳しいです。

### パスワード強度メーターの実装

ここから、コードを一部抜粋して解説します。

パスワード強度メーターの見た目部分はVuetifyの[Progress Linear](https://vuetifyjs.com/en/components/progress-linear/)を使っています。
`:color="score.color"`と`:value="score.value"`には、下記で定義している算出プロパティ`score`の値が入ります。

```vue:html
<v-progress-linear
  :background-opacity="opacity"
  :color="score.color"
  :value="score.value"
></v-progress-linear>
```

次にパスワード強度メーターのロジックについてです。

```js:js
computed: {    
  score() {
    // zxcvbnで入力されたパスワードの強度をチェックしています。
    const result = zxcvbn(this.password);
    
    // switch文を使って、パスワードのスコアによって、返すcolorとvalueを分岐しています。
    // ここで使っているcolorの値はVuetifyで定義されているものです。
    switch (result.score) {
      case 4:
        return {
          color: "light-blue",
          value: 100
        };
      case 3:
        return {
          color: "light-green",
          value: 75
        };
      case 2:
        return {
          color: "yellow",
          value: 50
        };
      case 1:
        return {
          color: "orange",
          value: 25
        };
      default:
        return {
          color: "red",
          value: 0
        };
    }
  }
}
```
 　
### パスワード強度のバリデーションの実装
フォームはVuetifyの[Password Input](https://vuetifyjs.com/en/components/text-fields#password-input)を利用しています。

[v-text-fieldのpropsのrules](https://vuetifyjs.com/en/components/text-fields#api)を使って、下記で定義しているバリデーションを適用しています。

```vue:html
<v-text-field
  v-model="password"
  :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
  :rules="[rules.required, rules.min, rules.strength]"
  validate-on-blur
  :type="showPassword ? 'text' : 'password'"
  label="Password"
  class="mb-6"
  @click:append="showPassword = !showPassword"
></v-text-field>
```
　
パスワード強度のバリデーションのロジック部分です。

```js:js
rules: {
  // required と min はVuetifyドキュメントのサンプルに入っていたものなので、説明を省略します。
  required: value => !!value || 'Enter a password',
  min: v => v.length >= 8 || 'Use 8 characters or more for your password',
  // パスワード強度のバリデーションです。
  // zxcvbn(v).scoreで入力値のスコアを算出して、許容するスコア （ここでは３） と比較しています。
  strength: v => zxcvbn(v).score >= 3 || 'Please choose a stronger password. Try a mix of letters, numbers, and symbols.'
},
```

### 安全性を高める
パスワードの安全性をより高めたい場合は、パスワードの長さにもバリデーションをかけると良いです。次の参考サイトでは12文字以上を推奨しています。
https://www.jpcert.or.jp/pr/stop-password.html

# おまけ
サンプルではパスワード強度のスコアごとに色を変えていますが、単色のほうがスッキリしていて良いかなと思い、単色バージョンも作りました。

<p class="codepen" data-height="576" data-theme-id="dark" data-default-tab="html,result" data-user="harashoo" data-slug-hash="xxGXYqv" style="height: 576px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vuetify Password Form With Password Strength Meter (Monochromatic)">
  <span>See the Pen <a href="https://codepen.io/harashoo/pen/xxGXYqv">
  Vuetify Password Form With Password Strength Meter (Monochromatic)</a> by harasho (<a href="https://codepen.io/harashoo">@harashoo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>