QuasarFramework(Vue.js) + Firebase で、ユーザーのパスワード・Emailを更新する

Firebaseを使って、ユーザーのパスワードやEmailなどの重要情報を更新する機能を実装してみます。QuasarFramework(Vue.js)を使って実装しています。

# 参考
https://firebase.google.com/docs/auth/web/manage-users?hl=ja

# 前提
Vue.js, QuasarFrameworkなどで、Firebaseに作成しているアプリなどがある。
まだの方は、以下の記事など参照にしていただけますと幸いです。
https://qiita.com/yassyskywalker/items/d74ea3b0834145df5473

# パスワードを変更する（現在のパスワードを覚えている）

パスワードを更新するためのフォームを作成します。
firebaseの公式サイトですすめられていますが、再認証のプロセスをかませるのがポイントのようです。
* UserオブジェクトのEmailAuthProviderメソッドを使って、credentialを取得する
* reauthenticateWithCredential()に、credentialを渡して、再認証する

```html
<template lang="html">
<div class="row">
  <div class="col-xs-12 col-sm-12">
    <!-- プロフィールフォーム -->
    <div class="row justify-center">
      <div class="col-6">
        <q-field
          label="現在のパスワード"
        >
          <q-input
            v-model="currentPassword"
            type="password"
            max-length='30'
          />
        </q-field>
      </div>
    </div>
    <div class="row justify-center">
      <div class="col-6">
        <q-field
          label="新しいパスワード"
        >
          <q-input
            v-model="newPassword1"
            type="password"
            max-length='30'
          />
        </q-field>
      </div>
    </div>
    <div class="row justify-center">
      <div class="col-6 text-center">
        <q-btn color="primary" class="q-btn-margin" @click="sendResetMail">パスワードを変更する</q-btn>
      </div>
    </div>
  </div>
</div>
</template>

<script>
import {
  Alert,
  QBtn,
  QField,
  QInput
} from 'quasar'
import firebase from 'firebase'
import 'quasar-extras/animate/bounceInLeft.css'
import 'quasar-extras/animate/bounceOutRight.css'

export default {
  name: 'myPageProfile',
  components: {
    QBtn,
    QField,
    QInput
  },
  data () {
    return {
      currentPassword: '',
      newPassword1: ''
      validation: {
        currentPassword: true,
        newPassword1: true
      },
      errorLabel: {
        currentPassword: '必須項目です',
        newPassword1: ''
      }
    }
  },
  computed: {
  },
  methods: {
    sendResetMail () {
      const user = firebase.auth().currentUser
      // 再認証のcredentialを取得する
      const credential = firebase.auth.EmailAuthProvider.credential(
        user.email,
        this.currentPassword
      )
      // credentialをreauthenticateWithCredentialに渡す
      user.reauthenticateWithCredential(credential).then(() => {
        // 再認証に成功した場合のみ、updatePasswordメソッドを実行する
        user.updatePassword(this.newPassword1).then(() => {
          Alert.create({
            html: 'パスワードを更新しました。再度ログインをお願いします。',
            color: 'positive',
            enter: 'bounceInLeft',
            leave: 'bounceOutRight'
          })
          this.logout()
        }).catch((error) => {
          console.log(error)
        })
      // reauthenticateWithCredentialに失敗したケース
      }).catch((error) => {
        this.validation.currentPassword = false
        this.errorLabel.currentPassword = 'パスワードが違います。'
        Toast.create.positive({
          html: '認証に失敗しました。再度パスワードのご確認をお願いします。',
          timeout: 2500
        })
      })
    },
    logout () {
      firebase.auth().signOut()
        .then(() => {
          this.$router.replace('<ログインフォームへのroute name>')
        })
    }
  }
}
</script>
```

# パスワードを忘れてしまった場合などにメールで再設定リンクを送付する。
これは、sendPasswordResetEmailを利用すると手軽に設定できます。

```html
<template lang="html">
  <div>
    <div class="layout-padding row justify-center background-whitesmoke items-center">
      <div class="col-xs-12 col-sm-7 col-lg-5">
        <q-card class="q-card-background-white">
          <q-card-main>
            <div class="row">
              <div class="col-sm-10 offset-sm-1 text-center">
                <img src="~assets/quasar-logo-full.svg" class="logo-sized">
              </div>
            </div>
            <div class="row">
              <div class="col-sm-10 offset-sm-1 text-left">
                <h5>パスワードをリセットする</h5>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-10 offset-sm-1 text-left">
                <p style="font-size: 12px;">メールアドレスを入力してください。</p>
              </div>
            </div>
            <div class="row">
              <div class="col-sm-10 offset-sm-1">
                <q-field>
                  <q-input v-model="email" float-label="メールアドレス" color="primary" type="email"/>
                </q-field>
              </div>
            </div>
            <div class="row" style="padding-top: 20px;">
              <div class="col-sm-10 offset-sm-1 text-right">
                <q-btn color="primary" style="font-weight: bold;" @click="resetPassword">パスワードをリセットする</q-btn>
              </div>
            </div>
            <br>
            <div class="row">
              <p style="color:#027be3;font-size:12px;"><router-link :to="{ name: 'Signup' }">新規アカウント作成</router-link></p>
            </div>
            <div class="row">
              <p style="color:#027be3;font-size:12px;"><router-link :to="{ name: 'Login' }">すでにアカウントをお持ちの方はこちら</router-link></p>
            </div>
          </q-card-main>
        </q-card>
      </div>
    </div>
  </div>
</template>

<script>
import {
  Alert,
  QBtn,
  QCard,
  QCardMain,
  QField,
  QInput
} from 'quasar'
import firebase from 'firebase'
import 'quasar-extras/animate/bounceInLeft.css'
import 'quasar-extras/animate/bounceInRight.css'
import 'quasar-extras/animate/bounceOutRight.css'

export default {
  name: 'resetpassword',
  components: {
    QBtn,
    QCard,
    QCardMain,
    QField,
    QInput
  },
  data () {
    return {
      email: ''
    }
  },
  methods: {
    resetPassword () {
      firebase.auth().sendPasswordResetEmail(this.email).then(
        () => {
          Alert.create({
            html: 'ご登録のメールアドレスに再設定のリンクを送付いたしました。',
            color: 'positive',
            enter: 'bounceInLeft',
            leave: 'bounceOutRight'
          })
          this.$router.replace('/')
        },
        (err) => {
          Alert.create({
            html: 'ご登録のアドレスがございません。再度メールアドレスのご確認をお願いします。',
            color: 'negative',
            enter: 'bounceInLeft',
            leave: 'bounceOutRight'
          })
          console.log(err)
        }
      )
    }
  },
  computed: {
  }
}
</script>

<style lang="stylus">
/* .q-card-background-white
  background: white;

.background-whitesmoke
  background: whitesmoke;
  height: 100vh;

.logo-sized
  width: 150px;
  margin: 20px; */
</style>
```

フォームから送信して、再設定リンクが送付されれば成功です！
ただ、再設定のフォームが英語になってしまってるので、それを修正する場合は、以下のように設定するようです。
https://firebase.google.com/docs/auth/custom-email-handler?hl=ja

これも今度やってみようかと思います。

# メール アクションで状態 / 続行 URL を渡す
パスワードやE-mailなどの再設定に特定のURLを渡すことが出来ます。

## 承認済みドメインを設定する。
> 続行 URL を安全に渡すには、URL のドメインが Firebase コンソールでホワイトリストに登録されている必要があります。それには、[Authentication] セクションで、[OAuth リダイレクト ドメイン] のリストにこのドメインを追加します（まだ存在しない場合）。

OAuthリダイレクトドメインは、承認済みドメインの箇所のようです。デフォルトで、localhostは設定されてるので、そのままにしておきます。


## firebase.auth.ActionCodeSettings インスタンスを指定する

> パスワードの再設定メールまたは確認メールを送信する際は、firebase.auth.ActionCodeSettings インスタンスを指定する必要があります。このインターフェースでは、次のパラメータを指定できます。

今回は、さきほどの、sendPasswordResetEmailを例にして設定してみます。引数として、emailと ActionCodeSettingsインスタンスを渡すことで、続行URLを実現できるようです。
https://firebase.google.com/docs/reference/js/firebase.auth.Auth

```javascript
...
resetPassword () {
  const actionCodeSettings = {
    url: 'http://localhost:8080/',
    handleCodeInApp: false
  }
  firebase.auth().sendPasswordResetEmail(
    this.email, actionCodeSettings)
    .then(
      () => {
        Alert.create({
          html: 'ご登録のメールアドレスに再設定のリンクを送付いたしました。',
          color: 'positive',
          enter: 'bounceInLeft',
          leave: 'bounceOutRight'
        })
        this.$router.replace('/')
      },
      (err) => {
        Alert.create({
          html: 'ご登録のアドレスがございません。メールアドレスのご確認をお願いします。',
          color: 'negative',
          enter: 'bounceInLeft',
          leave: 'bounceOutRight'
        })
        console.log(err)
      }
    )
},
...
```

パスワードを再設定した後に、指定したページが表示されたら成功です！！

# まとめ
Firebaseだとメール認証、パスワード再設定などのデリケートな部分も、比較的手軽に実装できて、素晴らしいですね！ますますファンになりそうです。