FirebaseとReactで認証実装

# FirebaseとReactを使って認証機能実装
プログラミング初心者が認証機能を実装したので備忘録として残しておきます。

##使ったもの

* react-router
* Firebase Authentication
* Hooks

##Firebaseの設定
まずFirebaseでプロジェクトを作成し、Authenticationを有効化します。
今回は"メール/パスワード"を使いました。
そして、プロジェクトのSettingsから自分のappsのFirebase SDK snippetのConfigをコピーします。
以下のような形で取得できると思います。

```
const firebaseConfig = {
　apiKey: "your_key",
　authDomain: "your_app_id.firebaseapp.com",
　databaseURL: "https://your_app_id.firebaseio.com",
　projectId: "your_app_id",
　storageBucket: "your_storage_bucket",
　messagingSenderId: "sender_id",
　appId: "your_app_id"
};
```

これを、自分のアプリのルートディレクトリに.envファイルを作って以下の形式で保存します。

```:/.env
REACT_APP_FIREBASE_KEY=your_key
REACT_APP_FIREBASE_DOMAIN=your_app_id.firebaseapp.com
REACT_APP_FIREBASE_DATABASE=https://your_app_id.firebaseio.com
REACT_APP_FIREBASE_PROJECT_ID=your_app_id
REACT_APP_FIREBASE_STORAGE_BUCKET=your_storage_bucket
REACT_APP_FIREBASE_SENDER_ID=sender_id
```

##React側の設定
Reactアプリケーションはcreate-react-appか何かでセッティングしておいてください。
必要なパッケージをインストールします。

```
$ npm install --save firebase react-router react-router-dom
($ yarn add firebase react-router react-router-dom)
```

Firebaseにログインするのと、余分ですがFirestoreに接続する設定は以下です。

```javascript:/src/utils/firebase.js

import firebase from "firebase";

firebase.initializeApp({
  // Authentication infomation
  apiKey: process.env.REACT_APP_FIREBASE_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
  databaseURL: process.env.REACT_APP_FIREBASE_DATABASE,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID
})

const auth = firebase.auth()
const db = firebase.firestore()

export {auth, db}
```
create-react-appでは.envファイルの中身をprocess.envで取れるようになっているらしいです。

## Context作成

```react:/src/contexts/Auth.js
import React, { createContext, useState, useEffect } from 'react'
import { auth } from '../utils/firebase'


export const AuthContext = createContext();

export const AuthProvider = ({children}) => {
	const [currentUser, setCurrentUser] = useState(null)
	
	const signup = async (email, password, history) => {
		try {
			await auth.createUserWithEmailAndPassword(email, password);
			history.push("/");
		}catch(error){
			alert(error);
		}
	}

	const signin = async (email, password, history) => {
		try{
			await auth.signInWithEmailAndPassword(email, password);
			history.push("/");
		}catch(error){
			alert(error);
		}
	}

	const signout = async () => {
		await auth.signOut()
	}
	
	useEffect(() => {
		auth.onAuthStateChanged(setCurrentUser);
	}, [])

	return (
		<AuthContext.Provider value={{currentUser, signup, signin, signout}}>
			{children}
		</AuthContext.Provider>
	)
}
```

こんな感じで認証系のContextができました。

## Routerの設定
SignInとSignUpのページ以外はサインインしないと見れないようにしたいので、PrivateRouterを作ります。

```react:/src/pages/PrivateRouter.js
import React, { useContext } from "react";
import { Route } from "react-router-dom";
import { AuthContext } from "../contexts/Auth";
import SignIn from "./SignIn";

const PrivateRouter = ({ component: RouteComponent, ...options}) => {
  const { currentUser } = useContext(AuthContext);
  const Component = currentUser ? RouteComponent: SignIn;

  return <Route {...options} component={Component} />;
};

export default PrivateRouter;
```
これで、currentUserがnullの場合はSignInに飛びます。
SignInページはあとで作ります。

次に、App.jsにこのPrivateRouterを使ってRoutingを設定していきます。

```react:/src/App.js
import React from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";

import { AuthProvider } from "./contexts/Auth";
import PrivateRoute from "./pages/PrivateRouter";
import Home from "./pages/Home";
import SignIn from "./pages/SignIn";
import SignUp from "./pages/SignUp";

export default function App() {
  return (
    <AuthProvider>
      <Router>
        <div>
          <PrivateRoute exact path="/" component={Home} />
          <Route exact path="/signin" component={SignIn} />
          <Route exact path="/signup" component={SignUp} />
        </div>
      </Router>
    </AuthProvider>
  );
}
```
Homeは、認証後に表示したいページです。

## サインイン、サインアップページ
簡単に作っていこうと思います。

```react:/src/pages/SignIn.js
import React, { useContext } from "react";
import { withRouter } from "react-router";
import { AuthContext } from "../contexts/Auth";

const SignIn = ({ history }) => {
  const { signin } = useContext(AuthContext);
  const handleSubmit = event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    signin(email.value, password.value, history);
  };

  return (
    <div>
      <h1>Sign in</h1>
      <form onSubmit={handleSubmit}>
        <label>
          Email
          <input name="email" type="email" placeholder="Email" />
        </label>
        <label>
          Password
          <input name="password" type="password" placeholder="Password" />
        </label>
        <button type="submit">Sign in</button>
      </form>
    </div>
  );
};

export default withRouter(SignIn);
```

```react:/src/pages/SignUp.js
import React, { useContext } from "react";
import { withRouter } from "react-router";
import { AuthContext } from "../contexts/Auth";

const SignUp = ({ history }) => {
  const { signup } = useContext(AuthContext);
  const handleSubmit = event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    signup(email.value, password.value, history);
  };

  return (
    <div>
      <h1>Sign up</h1>
      <form onSubmit={handleSubmit}>
        <label>
          Email
          <input name="email" type="email" placeholder="Email" />
        </label>
        <label>
          Password
          <input name="password" type="password" placeholder="Password" />
        </label>
        <button type="submit">Sign Up</button>
      </form>
    </div>
  );
};

export default withRouter(SignUp);
```

これで、signin,signup関数を使ってサインインとサインアップができるようになりました。
サインアウトは以下で済むので簡単ですね。

```react
<button onClick={signout}>Sign out</button>
```

これで認証を実装することができました。

## 感想
今回は承認機能を実装してみましたがfirebaseを使うとかなり簡単にできたように思います。
このくらいだとreduxを使わず、ContextAPIを使うほうが良さそうですね。
これからバックエンドとの連携やtypescriptの導入なんかもやってみたいですね！

## 参考
[【React】 Firebaseを使用して認証機能の実装](https://qiita.com/k-penguin-sato/items/6e892231922b360a8659)
[ReactHooks + Firebase(Authentication, Firestore)でTodoアプリ作る](https://qiita.com/k_tada/items/ed05d14458d1ddfcefae)