作成日:
サイトにアクセスした際のログイン画面、サインアップ画面と認証機能を作りたい。そして、認証に成功したユーザだけ Top 画面に遷移できるようにする。
今回は、react で作成したアプリにログイン画面と認証機能を追加するまでを行いたいと思います。また、今回利用するログイン画面などのソースコードはこちらのコードを参照してください。
Google が提供しているユーザ情報を管理し、ユーザ認証機能が簡単に利用できるサービスです。ドキュメントには以下のように書かれています。
Firebase Authentication には、バックエンド サービス、使いやすい SDK、アプリでのユーザー認証に使用できる UI ライブラリが用意されています。Firebase Authentication では、パスワード、電話番号、一般的なフェデレーション ID プロバイダ(Google、Facebook、Twitter)などを使用した認証を行うことができます。
$ npx create-react-app firebase-authentication-app //firebase-authenticationa-appの箇所は自由に変更してください
$ yarn add firebase //^9.6.6
$ yarn add react-router-dom //^6.2.1
$ yarn add styled-components //^5.3.3
REACT_APP_FIREBASE_KEY="~~~~~~~~~~"
REACT_APP_FIREBASE_DOMAIN="~~~~~~~~~~"
REACT_APP_FIREBASE_PROJECT_ID="~~~~~"
REACT_APP_FIREBASE_STORAGE_BUCKET="~~~~~~~~~~~"
REACT_APP_FIREBASE_SENDER_ID="~~~~~~~~~~~"
firebase.js
import firebase from "firebase/compat/app"; import "firebase/compat/auth"; firebase.initializeApp({ apiKey: process.env.REACT_APP_FIREBASE_KEY, authDomain: process.env.REACT_APP_FIREBASE_DOMAIN, 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 firebaseAuth = firebase.auth(); export { firebaseAuth };
useFirebaseAuth.js
import { useState, useCallback, useEffect } from "react"; import { firebaseAuth } from "../firebase"; const useFirebaseAuth = () => { const [currentUser, setCurrentUser] = useState(firebaseAuth.currentUser); const [isAuthenticated, setIsAuthenticated] = useState(false); const [isLoading, setIsLoading] = useState(true); useEffect(() => { let unmounted = false; firebaseAuth.onAuthStateChanged((user) => { if (user) { user.getIdTokenResult(true).then((result) => { if (!unmounted) { setIsAuthenticated(true); setCurrentUser(user); setIsLoading(false); } }); } else if (!unmounted) { setIsAuthenticated(false); setCurrentUser(""); setIsLoading(false); } }); return () => { unmounted = true; }; }, []); const cleanUpAuthentication = () => { setIsAuthenticated(false); setCurrentUser(""); setIsLoading(true); }; const signup = useCallback((email, password) => { firebaseAuth .createUserWithEmailAndPassword(email, password) .catch((err) => alert(err)); }, []); const login = useCallback((email, password) => { firebaseAuth .signInWithEmailAndPassword(email, password) .catch((err) => alert(err)); }, []); const logout = useCallback(() => { firebaseAuth.signOut(); cleanUpAuthentication(); }, []); return { signup, login, logout, currentUser, isAuthenticated, isLoading, }; }; export default useFirebaseAuth;
Login.js
import { useState } from "react"; import { Navigate, Link } from "react-router-dom"; import styled from "styled-components"; import useFirebaseAuth from "./useFirebaseAuth"; const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const { login, isAuthenticated } = useFirebaseAuth(); const isInvalid = password === "" || email === ""; if (isAuthenticated) { return <Navigate to="/" />; } return ( <Body> <Title>ログイン</Title> <Email> Email: <input value={email} type="email" onChange={(e) => setEmail(e.target.value)} /> </Email> <Password> Password: <input value={password} type="password" onChange={(e) => setPassword(e.target.value)} /> </Password> <LoginButton disabled={isInvalid} type="submit" onClick={() => login(email, password)} > ログイン </LoginButton> <HorizontalLine /> <p>アカウントがない方はこちら</p> <Link to="/signup"> <TransitionCreateAccount data-testid="signup-button"> アカウント作成はこちら </TransitionCreateAccount> </Link> </Body> ); }; const Body = styled.div` display: flex; flex-direction: column; align-items: center; padding-bottom: 20px; `; const Title = styled.div` font-weight: bold; font-size: 30px; `; const Email = styled.div` font-size: 24px; margin: 40px 0 0 60px; input { font-size: 24px; height: 43px; width: 200px; } `; const Password = styled.div` font-size: 24px; margin: 20px; input { font-size: 24px; height: 43px; width: 193px; } `; const LoginButton = styled.button` color: #3366ff; font-size: 24px; width: 340px; height: 56px; background: #faf9f9; border: solid 1px #3366ff; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); margin: 32px; `; const HorizontalLine = styled.hr` width: 100%; `; const TransitionCreateAccount = styled.button` color: #faf9f9; font-size: 24px; width: 340px; height: 56px; background: #3366ff; border: solid 1px #0f9ada; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); margin: 20px; :active { border: solid 1px #03a9f4; box-shadow: none; text-shadow: none; } `; export default Login;
SignUp.js
import { useState } from "react"; import { Navigate, Link } from "react-router-dom"; import styled from "styled-components"; import useFirebaseAuth from "./useFirebaseAuth"; const SignUp = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const { signup, isAuthenticated } = useFirebaseAuth(); if (isAuthenticated) { return <Navigate to="/" />; } return ( <Body> <Title>アカウント作成</Title> <Email> Email: <input value={email} type="email" onChange={(e) => setEmail(e.target.value)} /> </Email> <Password> Password: <input value={password} type="password" onChange={(e) => setPassword(e.target.value)} /> </Password> <SubmitButton onClick={() => signup(email, password)}> アカウント作成 </SubmitButton> <HorizontalLine /> <p>すでにアカウントがある方はこちら</p> <Link to="/login"> <TransitionCreateAccount data-testid="signup-button"> ログインはこちら </TransitionCreateAccount> </Link> </Body> ); }; const Body = styled.div` display: flex; flex-direction: column; align-items: center; padding-bottom: 20px; `; const Title = styled.div` font-weight: bold; font-size: 30px; `; const Email = styled.div` font-size: 24px; margin: 40px 0 0 60px; input { font-size: 24px; height: 43px; width: 200px; } `; const HorizontalLine = styled.hr` width: 100%; `; const Password = styled.div` font-size: 24px; margin: 20px; input { font-size: 24px; height: 43px; width: 200px; } `; const SubmitButton = styled.button` color: #3366ff; font-size: 24px; width: 340px; height: 56px; background: #faf9f9; border: solid 1px #3366ff; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); margin: 20px; `; const TransitionCreateAccount = styled.button` color: #faf9f9; font-size: 24px; width: 340px; height: 56px; background: #3366ff; border: solid 1px #0f9ada; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); margin: 20px; `; export default SignUp;
Top.js
import useFirebaseAuth from "./auth/useFirebaseAuth"; const Top = () => { const { logout } = useFirebaseAuth(); return ( <> <h1>ログイン完了</h1> <button onClick={() => logout()}>ログアウト</button> </> ); }; export default Top;
App.js
import { Route, Navigate, Routes } from "react-router-dom"; import useFirebaseAuth from "./auth/useFirebaseAuth"; import Top from "./Top.js"; import Login from "./auth/Login"; import SignUp from "./auth/SignUp"; function App() { const { isAuthenticated, isLoading } = useFirebaseAuth(); if (isLoading) return <p>loading...</p>; return ( <> <Routes> <Route path="/" element={isAuthenticated ? <Top /> : <Navigate to="/login" />} /> <Route exact path="/login" element={<Login />} /> <Route exact path="/signup" element={<SignUp />} /> </Routes> </> ); } export default App;
index.js
import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; import { BrowserRouter } from "react-router-dom"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode>, document.getElementById("root") );
$ yarn start
次に、http://localhost:3000/ にアクセスしてください。まだログインしていないので http://localhost:3000/login にリダイレクトされると思います。
まだアカウントを作成していないので、「アカウント作成はこちら」からアカウントを作成しましよう。自由にメールアドレスと パスワードを入力してください。(例 Emai:tete@stst.com、Password:adadaminmin1234)
「アカウントを作成」をクリックすると、アカウントが作られログイン状態になり Top 画面に遷移します。
ログアウトボタンを押すとログイン画面に遷移します。
そして、先ほど登録したメールアドレスとパスワードを入力し、「ログイン」を押すと Top 画面に遷移できます。
これで一通りの機能の確認ができました。もし、どこかでエラーが発生してうまく動作しなかったら、package.json から firebase のバージョンを確認してみてください。今回私は、^9.6.6 を利用してのですが^9 以降に仕様が変わっている場所があります。
今回は、Firebase Authentiation を利用してユーザ認証機能をアプリに追加しました。このサービスを利用することで簡単に認証機能を実装することができました。また、今回紹介させていただいたコードは他のコードに埋め込みやすいように書いたつもりなので、ぜひご自身のコードに組み込んでください。
By kiyo