Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

React + Firebaseで認証機能を実装してみよう

More than 1 year has passed since last update.

Ateam Lifestyle Advent Calendar 2019の2日目は
株式会社エイチームライフスタイル 名古屋開発部のタツミが担当します。
フロントエンドの開発をメインでやっています。
駆け出しのエンジニアですが、よろしくお願いします。

はじめに

チームでWebサービスを開発するときにフロントエンドとバックエンドの開発担当に分かれるのはよくある光景ですが、開発のスピードやチームのリソースが求められた時にバックエンドまで開発するのは大変です。

バックエンドのサービス開発に時間を取られずに、フロントエンドの開発に専念したい。
その解決策の1つとして、Googleが提供しているFirebaseというツールの機能の1つを使ってバックエンドの開発をせずに認証機能を作っていきたいと思います。

FirebaseはiOS/AndroidアプリからWebサービスまでバックエンドを必要とするサービスで活用することができます。

認証機能の概要

Firebaseではデータベース、ストレージサービス、ホスティングサービス、分析ツール、プッシュ通知などWeb/アプリ開発で活用できる様々なサービスを提供しています。
今回はFirebaseの認証機能の1つであるFirebase Authenticationを使って、React認証機能を実装していきたいと思います。

説明の進め方

本記事は題材ごとに全部で以下、4パートに分けて説明します。
Reactの基礎は理解している程で話を進めます。

  • Part1: フォームを作成しよう
  • Part2: Firebaseの認証機能を有効にしよう
  • Part3: ログイン機能を実装しよう
  • Part4: ログアウト機能を実装しよう

Part1: フォームを作成しよう

https://firebase.google.com/
↑にアクセスして、Firebaseのアカウントを作成してください。

create-react-appで新しいReactプロジェクトを作成してください。

認証用のコンポーネントを作成します。
新たにsrc/components/Auth/index.jsを作成してください。
簡単にメールアドレスとパスワードの基本的なフォームを作成します。

※ このファイルに認証機能を全て実装します。実装は例ですので適宜、適切なファイルに実装してください。

src/components/Auth/index.js
import React, { Component } from 'react'

class Auth extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleChange(event) {
    // テキストボックスの値を変更する度に、変数に値が格納される
    this.setState({ [event.target.name]: event.target.value })
  }

  handleSubmit(event) {
    // 後半のパートでemailとpasswordをfirebaseに登録する処理を追加
    event.preventDefault()
  }

  render() {
    return (
      <div>
        <form method="post" onSubmit={this.handleSubmit}>
          <div>
            <input type="text" name="email" onChange={this.handleChange} /><br/>
            <input type="password" name="password" onChange={this.handleChange} />
          </div>
          <button>SUBMIT</button>
        </form>
      </div>
    )
  }
}

export default Auth

メールアドレスとパスワードのonChangeイベントに設定したhandleChangeアクションでテキストボックスの値が変わる度にstateに更新された値が格納されます。

後半のパートのなかで、handleSubmitアクションで格納されたstateをFirebase Authenticationに登録していく処理を追加する方法を説明します。

作成した認証コンポーネントを描画するために、App.jsを変更します。

src/App.js
import React from 'react'
import './App.css'
import Auth from './components/Auth'

function App() {
  return (
    <div className="App">
      <Auth />
    </div>
  )
}

export default App

Part2: Firebaseの認証機能を有効にしよう

ここからログイン機能を実装していきます。

Part1で作ったFirebaseのアカウントでログインして、コンソールへ移動を選択します。
firebase_1.png

新しいプロジェクトを作成する必要があるので、プロジェクトを追加を選択してください。

firebase_2.png

プロジェクト名は何でも良いのですが、今回は認証テストとしてauth-testと命名して続行を選択します。

firebase_3.png

google analyticsと連携させたい場合は「このプロジェクトでGoogle アナリティクスを有効にする」にチェックを入れますが、今回は連携させないのでチェックを外します。プロジェクトを作成を選択してください。

firebase_4.png

無事にauth-testプロジェクトが作成されました。
左のメニューからAuthenticationを選択して、ログイン方法を設定を選択してください。

firebase_5.png

様々な認証方法を選ぶことができますが、今回はメールアドレスとパスワードで認証機能を作っていきたいのでメール/パスワードの右の鉛筆マークを選択してください。

firebase_6.png

メールアドレスとパスワードの認証を有効にします。
今回はパスワードの設定を必須にしたいので、パスワードなしでログインは無効にしておきます。
保存を選択します。

firebase_7.png

以上で、メール/パスワードの設定が有効になりました。

firebase_8.png

Part3: ログイン機能を実装しよう

Firebase AuthenticationのAPIを使って、フロントエンドにログイン機能を実装します。
認証が完了するとトークンが発行されますが、トークンをstateに保存するとリロードした時にトークン情報が消えてしまうので、localStorageに保存する方法を実装していきます。
Firebase APIと通信する必要があるので、axiosを使っていきます。

SignUpとSignIn機能を実装するボタン<button>を設置します。
Part2で作ったフォームの続きから実装していきます。
ボタンをクリックする度に、switchAuthModeHandlerが発火してisSignUpフラグのon, offの切替を行います。

src/components/Auth/index.js
import React, { Component } from 'react'

class Auth extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.state = {
   // signUpとsignInフラグ
      isSignUp: true
    }
  }

  handleChange(event) {
    this.setState({ [event.target.name]: event.target.value })
  }

  handleSubmit(event) {
    event.preventDefault()
  }

 // クリックの毎にsignUpとsignInの切替を行う
 switchAuthModeHandler = () => {
    this.setState({ isSignUp: !this.state.isSignUp })
  }

  render() {
    return (
      <div>
        <form method="post" onSubmit={this.handleSubmit}>
          <div>
            <input type="text" name="email" onChange={this.handleChange} /><br/>
            <input type="password" name="password" onChange={this.handleChange} />
          </div>
          <button>SUBMIT</button>
        </form>
    {/* signUp、signInボタン */}
        <button onClick={this.switchAuthModeHandler}>
          SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'}
        </button>
      </div>
    )
  }
}

export default Auth

続いて、認証メソッドを作成します。
Firebaseの認証機能APIのエンドポイントを紹介します。
APIを使うにはAPIキーを入力する必要があるので、キーを先に確認します。

firebase_9.png

firebase_10.png

SignUp用のエンドポイントとSignIn用のエンドポイントを確認します。
https://firebase.google.com/docs/reference/rest/auth#section-create-email-password
↑ URLにアクセスします

SignUp用エンドポイント
API_1.png

SignInのエンドポイント
API_2.png

各エンドポイントの?key=[API_KEY]の部分に先ほど確認したAPIキーを入力して使います。

認証メソッドの続きを実装します。

src/components/Auth/index.js
import React, { Component } from 'react'
import axios from 'axios'

class Auth extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.state = {
      isSignUp: true,
      token: null,
      error: ''
    }
  }

  handleChange(event) {
    this.setState({ [event.target.name]: event.target.value })
  }

  handleSubmit(event) {
    this.auth()
    event.preventDefault()
  }

  switchAuthModeHandler = () => {
    this.setState({ isSignUp: !this.state.isSignUp })
  }

  auth = () => {
    // 認証データ
    const authDate = {
      email: this.state.email,
      password: this.state.password,
      returnSecureToken: true
    }
    // signIn用のAPIキー
    let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APIキーを入力'
    // signUp用のAPIキー
    if (this.state.isSignUp) {
      url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=APIキーを入力'
    }
    axios.post(url, authDate)
      .then(response => {
        // 返ってきたトークンをローカルストレージに格納する
        localStorage.setItem('token', response.data.idToken)
      })
      .catch(error => {
        // Firebase側で用意されているエラーメッセージが格納される
        this.setState({ error: error.response.data.error.message })
      })
  }

  render() {
    return (
      <div>
        <form method="post" onSubmit={this.handleSubmit}>
          <div>
          <input type="text" name="email" onChange={this.handleChange} /><br/>
            <input type="password" name="password" onChange={this.handleChange} />
          </div>
          <div>
            {this.state.error}
          </div>
          <button>SUBMIT</button>
        </form>
        <button onClick={this.switchAuthModeHandler}>
          SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'}
        </button>
      </div>
    )
  }
}

export default Auth

これでログイン機能の実装は完了です。
サインアップすると新しくユーザが作られているのを確認することができます。
パスワードは6文字以上でないと登録できないので注意してください。

firebase_11.png

サインアップまたは、サインインするとlocalStrageにtokenが保存されているのを確認することができます。

token.png

(localStorage.getItem('token'))でtokenが保存されている(ログインしている)場合に任意の処理を実行できます。

if (localStorage.getItem('token')) {
  // ログインしている場合に任意のメソッドを実行
}

Part4: ログアウト機能を実装しよう

ログアウトは簡単です。
ログインの時にlocalStrageに保存したtokenを削除するだけです。

src/components/Auth/index.js
import React, { Component } from 'react'
import axios from 'axios'

class Auth extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.state = {
      isSignUp: true,
      token: null,
      error: ''
    }
  }

  handleChange(event) {
    this.setState({ [event.target.name]: event.target.value })
  }

  handleSubmit(event) {
    this.auth()
    event.preventDefault()
  }

  switchAuthModeHandler = () => {
    this.setState({ isSignUp: !this.state.isSignUp })
  }

  auth = () => {
    const authDate = {
      email: this.state.email,
      password: this.state.password,
      returnSecureToken: true
    }
    let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APIキーを入力'
    if (this.state.isSignUp) {
      url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=APIキーを入力'
    }
    axios.post(url, authDate)
      .then(response => {
        localStorage.setItem('token', response.data.idToken)
      })
      .catch(error => {
        this.setState({ error: error.response.data.error.message })
      })
  }

  logout = () => {
    // トークンの削除
    localStorage.removeItem('token')
  }

  render() {
    return (
      <div>
        <form method="post" onSubmit={this.handleSubmit}>
          <div>
          <input type="text" name="email" onChange={this.handleChange} /><br/>
            <input type="password" name="password" onChange={this.handleChange} />
          </div>
          <div>
            {this.state.error}
          </div>
          <button>SUBMIT</button>
        </form>
        <button onClick={this.switchAuthModeHandler}>
          SWITCH TO {this.state.isSignUp ? 'SignIn' : 'SignUp'}
        </button><br/>
        {/* ログインしている場合だけログアウトボタンが表示される */}
        {localStorage.getItem('token') &&
          <button onClick={this.logout}>LOGOUT</button>}
      </div>
    )
  }
}

export default Auth

実装後の画面イメージ
・ サインイン、サインアップするとlocalStrageにトークンが保存される
・ リロードしてもトークンは保持し続ける
・ ログアウトを選択するとトークンが削除される

operation2.gif

まとめ

長くなりましたが、いかがでしたでしょうか?
バックエンドサービスを何らかのフレームワークを使って実装していれば、標準の認証機能を導入できるとは思いますが、自前でデータベースを用意したり、個人情報をどう管理するかを考える必要があります。
Firebase Authenticationを使うことでこれらの問題を意識することなく、認証機能を実装することができました。
Firabaseでは、導入部分でも紹介したように様々な機能が用意されているので、ぜひ活用してみてください。

Ateam Lifestyle Advent Calendar 2019の3日目は、@chardenがお送りします!!どんなネタを用意してくるのか楽しみです!!
"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away