Firebase Authentication + React 認証機能 の実装

目次

概要

今回は,Firebase Authを使用した,認証の実装方法について説明します。

以下に今回作成するために用いた、各ソフトウェアのバージョンを示します。

  • npm 6.14.9
  • node v14.15.3
  • Mac OS 10.15.7

Firebaseのセットアップについては,React+Firebase 環境構築を参考にしてください。

プロジェクトの作成

まず,create-react-appを使用して,reactアプリの雛形を生成します。

npx create-react-app firebase-auth-sample

次に,今回使用するnpmパッケージをインストールします。

  • firebase
  • firebase-ui
  • react-router
npm install firebase firebase-ui react-router

create-react-appを使用して,プロジェクトの作成を行なった場合,次のようなディレクトリ構造になっていると思います。

.
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js

この構造を以下の構造に変更します。

.
├── README.md
├── package-lock.json
├── package.json
├── .env
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── components
│ ├── App
│ │ ├── App.css
│ │ ├── App.test.js
│ │ └── index.js
│ ├── FirebaseApp
│ │ └── index.js
│ ├── Home
│ │ └── index.js
│ ├── Login
│ │ └── index.js
│ └── Other
│ └── index.js
├── constant
│ └── firebaseConfig.js
├── context
│ └── context.js
├── index.css
├── index.js
├── reportWebVitals.js
└── setupTests.js

componentディレクトリに各Componentを配置しています。各種ファイルはとりあえず空ファイルとして作成しておきます。

実装

まず,firebaseの設定を行い初期化処理を行います。

.envファイルを作成し,Firebaseの情報を追記する。

REACT_APP_API_KEY=XXXXXX
REACT_APP_AUTH_DOMAIN=XXXXXX
REACT_APP_DATABASE_URL=XXXXXX
REACT_APP_PROJECT_ID=XXXXXX
REACT_APP_STORAGE_BUCKET=XXXXXX
REACT_APP_MESSAGING_SENDER_ID=XXXXXX
REACT_APP_APP_ID=XXXXXX
REACT_APP_MEASUREMENT_ID="XXXXXX

次に,.envファイルをfirebaseConfig.jsから読み取ります。

export const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_DATABASE_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID
};

次に,index.jsについて見ていきます。

import React from 'react';
import ReactDOM from 'react-dom';
import firebase from "firebase/app";
import { FirebaseApp } from './components/FirebaseApp';

import './index.css';
import App from './components/App';
import { firebaseConfig } from './constant/firebaseConfig';
import reportWebVitals from './reportWebVitals';

firebase.initializeApp(firebaseConfig);

ReactDOM.render(
<React.StrictMode>
<FirebaseApp>
<App />
</FirebaseApp>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();

index.jsでは,firebase.initializeAppでfirebaseの初期化処理をしています。また,App コンポーネントをFirebaseAppでラップしています。このFirebaseAppはFirebaseの各種サービスをAppコンポーネントで使用するためのものです。

続いて,FirebaseAppを見ていきます。FirebaseApp コンポーネントでは、ログイン処理を行います。ここで、context api を使用しています。context api を使用することにより、propsのバケツリレーを不要になります。ここでは、user情報を格納するuser context, firebaseの情報を格納するfirebase context の2種類を使用しています。この<*.Provider>でラップされたコンポーネントでは、context api を介して値を取得することができます。

import React, { useState, useEffect } from "react";
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

import { FirebaseContext, UserContext } from '../../context/context';

export const FirebaseApp = ({ children }) => {

const [user, setUser] = useState(null);

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

useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user) {
setUser(user);
}
else {
setUser(null);
}
});

});

return (
<FirebaseContext.Provider value={{ db, auth }}>
<UserContext.Provider value={{ user }}>
{children}
</UserContext.Provider>
</FirebaseContext.Provider>
)
}

続いて、App Conponentを見ていきます。

// node_modules
import React from 'react';
import { Switch, Route, BrowserRouter as Router } from "react-router-dom";

// components
import { Login } from "../Login";
import { Auth } from '../Auth';
import { Home } from '../Home';
import { Other } from '../Other';


const App = () => {

return (
<Router>
<Switch>
<Route exact path="/login" component={Login} />
<Auth>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/other" component={Other} />
</Switch>
</Auth>
</Switch>
</Router>
);
}

export default App;

ここでは、/loginで、Login処理を行うコンポーネント、/でHomeコンポーネント、/otherでOtherコンポーネントを描画します。まず、Loginコンポーネントを以下に示します。

import React, { useContext } from "react";
import firebase from "firebase/app";
import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth";
import { Redirect } from "react-router";

import { FirebaseContext, UserContext } from "../../context/context";

export const Login = () => {
const { auth } = useContext(FirebaseContext);
const { user } = useContext(UserContext);

const uiConfig = {
callbacks: {
signInSuccessWithAuthResult: (result) => false,
},
signInFlow: "popup",
signInSuccessUrl: "/",
signInOptions: [
firebase.auth.EmailAuthProvider.PROVIDER_ID,
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
],
};

return (
<div>
{!!user ? (
<Redirect to={{ pathname: "/" }} />
) : (
<div>
<p>Please Sign In</p>
<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={auth} />
</div>
)}
</div>
);
};

Firebase-uiを使用して、ログイン処理を行っています。signInOptionsでどのProviderでサインイン処理を行うのかを設定しています。ここではGoogleログインとEmailログインを指定しています。

次に、Authコンポーネントを見ていきます。以下がコードです。

import React, { useContext } from "react";

import { Fragment } from 'react';
import { Redirect } from 'react-router-dom'
import { UserContext } from '../../context/context'


export const Auth = ({ children }) => {

const { user } = useContext(UserContext);
if (user) {
return <Fragment> {children} </Fragment>;
} else {
return <Redirect to="/login" />;
}

}

userが設定されている場合、Authでラップされたコンポーネントを有効にし、それ以外は、Login処理にリダイレクトするようにしています。

この処理により、ログインしていないユーザはLogin処理に強制的にリダイレクトされます。

以下、その他のコンポーネントのコードです。

Home/index.js

import React, { useContext } from 'react';
import { FirebaseContext } from '../../context/context';
import { Link } from "react-router-dom";

export const Home = () => {
const { auth } = useContext(FirebaseContext);
const logout = () => {
auth.signOut();
}
return(
<div>
<h1>Home Component</h1>
<p><Link to='/other'>Other</Link></p>
<button onClick={logout}>Logout</button>
</div>
)
}

Other/index.js

import React from 'react';
import { Link } from 'react-router-dom';

export const Other = () => {
return(
<div>
<h1>Other Component</h1>
<p><Link to='/'>Home</Link></p>
</div>
)
}

以上で、firebase-uiを使用したログイン処理が実装できました。

以上のコードでログイン処理は実装できていますが、リロードした場合、ログイン画面が表示されてしまいます。そこで、以下のコードでローディング画面を設定することでこの問題を回避します。

import React, { useState, useEffect } from "react";
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

import { FirebaseContext, UserContext } from '../../context/context';

export const FirebaseApp = ({ children }) => {

const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true)

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

useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user) {
setUser(user);
}
else {
setUser(null);
}
setIsLoading(false);
});
});

if (isLoading) {
return (
<p>loading...</p>
)
}
else {
return (
<FirebaseContext.Provider value={{ db, auth }}>
<UserContext.Provider value={{ user }}>
{children}
</UserContext.Provider>
</FirebaseContext.Provider>
)
}

}

まとめ

今回は、firebase-uiを使用してログイン処理を実装しました。

Authコンポーネントでラップしたところにコンポーネントを配置することで、ログイン後にしか描画しないようにできます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次