Firebase の認証機能、Firebase Authentication を使ってログイン機能を作ろうとおもったところ、
ログイン処理時に
Uncaught (in promise) FirebaseError: Firebase: Error (auth/network-request-failed).
となって認証が出来ないという現象に遭遇したので対処法をメモ。
原因
どうも、ログインフォームを作るときに <form>
を使ってしまったり、<input type="submit">
で送信ボタンを作っているとこの現象が起こるみたいです。
ログインフォームなのでついついフォームに入れてしまいたくなりそうですが、そこに罠があったということですね。
対策
form
要素で囲ったり、form
のonSubmit
イベントなどを使って認証を発火している場合は、form
要素をやめてdiv
などにしてしまった上で、ログインボタンのクリックイベントなどで発火するように変更する
とりあえずこれだけで auth/network-request-failed
エラーは出なくなります。
ただ、<form></form>
で囲うとフォームに入力した後 Enter キーを押すだけでフォームを送信できるという地味に便利なメリットがありましたがこれが使えなくなってしまいます。
意外とこれ、要望があったりするのでこちらにも対応できるようにします。
Enter キーで送信できるようにする
form
要素の中で Input 要素にフォーカスされてるときに Enter キーを押すと Submit が発火して、パスワード入力 + Enter でログインできていたのですが、 form
要素から出してしまったためにこれが出来なくなってしまいました。
でもできれば入力した後マウスに持ち替えてログインボタンを押さないでも Enter キーでログインしたい……!
ということで対応していきます。
サンプルコード
今回はシンプルに signInWithEmailAndPassword
を使ったメールアドレスとパスワードによる認証なので、メールアドレスとパスワードの入力欄 (input 要素)にイベントリスナーを仕込んでキー入力を監視し、Enter キーの入力があった場合にログイン処理を発火させることで実現させます。
以下、Vue.js でのサンプルです。
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { useAuth } from '@/composables/useAuth'
const { signIn } = useAuth()
const userInputs = {
email: '',
password: ''
}
const handleLogIn = async () => {
await signIn(userInputs.email, userInputs.password)
}
const inputEmail = ref<HTMLElement|null>(null)
const inputPassword = ref<HTMLElement|null>(null)
const enterKeyHandler = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
handleLogIn()
}
}
onMounted(() => {
if (inputEmail.value instanceof HTMLElement) {
inputEmail.value.addEventListener('keydown', enterKeyHandler)
}
if (inputPassword.value instanceof HTMLElement) {
inputPassword.value.addEventListener('keydown', enterKeyHandler)
}
})
onBeforeUnmount(() => {
if (inputEmail.value instanceof HTMLElement) {
inputEmail.value.removeEventListener('keydown', enterKeyHandler)
}
if (inputPassword.value instanceof HTMLElement) {
inputPassword.value.removeEventListener('keydown', enterKeyHandler)
}
})
</script>
<template>
<div class="login-form">
<input
ref="inputEmail"
v-model="userInputs.email"
type="text"
placeholder="email"
>
<input
ref="inputPassword"
v-model="userInputs.password"
type="password"
placeholder="password"
>
<button @click="handleLogIn">
LOGIN
</button>
<p>
or <router-link :to="{ name: 'PageCreateAccount' }">register</router-link>
</p>
</div>
</template>
メールアドレスとパスワード、それぞれの input 要素を ref で捕まえてそこにイベントリスナーを仕込みました。
リッスンするイベントは keydown
です。
すると、登録する Function の引数で KeyboardEvent
が届くようになります。
どのキーが押されたかは、e.key
でとれるので、これが Enter ならログイン処理を実行、という風にしました。
以前はここが
e.keyCode
でキーコードを使って判定してたのですが今は Deprecated らしいので、e.key
を使います。
また、VirtualDOM 系のフレームワークの場合はコンポーネントがマウント解除されるタイミングで忘れずにちゃんとイベントリスナーも削除するようにします。