Firebase Authentication で認証時に auth/network-request-failed エラー

Firebase の認証機能、Firebase Authentication を使ってログイン機能を作ろうとおもったところ、
ログイン処理時に

Uncaught (in promise) FirebaseError: Firebase: Error (auth/network-request-failed).

となって認証が出来ないという現象に遭遇したので対処法をメモ。

原因

どうも、ログインフォームを作るときに <form>を使ってしまったり、<input type="submit"> で送信ボタンを作っているとこの現象が起こるみたいです。

ログインフォームなのでついついフォームに入れてしまいたくなりそうですが、そこに罠があったということですね。

対策

  • form 要素で囲ったり、formonSubmit イベントなどを使って認証を発火している場合は、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 系のフレームワークの場合はコンポーネントがマウント解除されるタイミングで忘れずにちゃんとイベントリスナーも削除するようにします。

参考

javascript - Firebase Project Results in "Auth/network-request-failed" error on login - Stack Overflow

git log --format=%ct:%s

:add 2023082101.md