見出し画像

Next.js + Firebaseで認証機能実装

認証に使用するもの

emailとpasswordでの認証機能を作っていきます。

環境

フロント:Next(React),Bootstrap
バックエンド:Firebase

*Firebaseにアプリの登録や環境変数の設定などはたくさん記事があるのでそちらを参考にしてください。認証機能にのみフォーカスします。

認証情報共有の為のproviderの作成

import { User } from 'firebase'
import { FC, createContext, useEffect, useState } from 'react'
import { auth } from 'db'

type AuthContextProps = {
   currentUser:User | null |undefined
}

const AuthContext = createContext<AuthContextProps>({ currentUser: undefined })

const AuthProvider: FC = ({ children }) => {
   const [currentUser, setCurrentUser] = useState<User | null | undefined>(undefined)

   //authのonAuthStateChangedメソッドでuserの情報を取得しローカルステートのcurrentUserに格納。
   useEffect(() => {
       auth.onAuthStateChanged((user) => {
           setCurrentUser(user)
       })
   }, [])
   return (
       <AuthContext.Provider value={{ currentUser }}>{ children }</AuthContext.Provider>//context.providerで値を渡す
   )
}

export { AuthContext,AuthProvider }

signup.tsx


import React, { FC,useState, useEffect } from 'react';
import 'firebase/auth'
import 'firebase/firestore'
import '../lib/db'
import firebase from 'firebase';
import Router from 'next/router';


const Signup: FC = () => {
   const [name,setName] = useState<string>('')
   const [email,setEmail] = useState<string>('')
   const [password,setPassword] = useState<string>('')

   const auth = firebase.auth()
   useEffect(() => {
       auth.onAuthStateChanged((user) => {
           user && Router.push('/')
       })
   },[])
   const createUser = async (e) => {
       e.preventDefault()
       try {
           //emailとpwを使って登録。currentUserでログイン中のユーザを取得して氏名の登録も同時に行う。
           await auth.createUserWithEmailAndPassword(email, password).then(() => {
                   let currentUser = auth.currentUser;
                   if (currentUser) {
                       currentUser.updateProfile({
                           displayName:name
                       }).then(() => {
                           Router.push('/login')
                       })
                   }
           })
       } catch (err) {
           console.log(err.message)
       }
   }

   return (
       <div>
               <h2>Signup</h2>
               <form onSubmit={createUser}>
                   <div className='form-group'>
                       <label htmlFor="name">氏名:</label>
                       <input type="text" name='name' onChange={(e) => setName(e.target.value)} className='form-control' />
                   </div>
                   <div className='form-group'>
                       <label htmlFor="email">メールアドレス:</label>
                       <input type='email' name='email' onChange={(e) => setEmail(e.target.value)} className='form-control'/>
                   </div>
                   <div className='form-group'>
                       <label htmlFor="password">パスワード:</label>
                       <input type='password' name='password' onChange={(e) => setPassword(e.target.value)} className='form-control'/>
                   </div>
                   <button type="submit" className='btn btn-success'>add!</button>
               </form>
       </div>
   )
}
export default Signup

createUserWithEmailAndPasswordを使うと氏名の登録ができず苦戦したので今回記事にまとめました。
ログイン中のユーザを取得してupdateProfile()でデータの更新をしています。

login.tsx


import React, { FC,useState, useEffect } from 'react';
import 'firebase/auth'
import 'firebase/firestore'
import '../lib/db'
import firebase from 'firebase';
import Router from 'next/router';
import Link from 'next/link'

const Login: FC = () => {
   const [email,setEmail] = useState<string>('')
   const [password, setPassword] = useState<string>('')
   const auth = firebase.auth()

   useEffect(() => {
       auth.onAuthStateChanged((user) => {
           user && Router.push('/')
       })
   }, [])

   const login = async (e) => {
       e.preventDefault()
       try {
       //emailとpwでログイン
           await auth.signInWithEmailAndPassword(email, password)
           Router.push('/')
       } catch (err) {
           alert(err.message)
       }
   }
   return (
       <div>
               <h2>ログイン</h2>
               <form onSubmit={login}>
                   <div className='form-group'>
                       <label htmlFor="email">メールアドレス:</label>
                       <input type='email' name='email' onChange={(e) => setEmail(e.target.value)} className='form-control'/>
                   </div>
                   <div className='form-group'>
                       <label htmlFor="password">パスワード:</label>
                       <input type='password' name='password' onChange={(e) => setPassword(e.target.value)} className='form-control'/>
                   </div>
                   <button type="submit" className='btn btn-success'>login!</button>
               </form>
               <Link href='/signup'><a>SignUp</a></Link>
       </div>
   )
}

export default Login

index.tsx

import Link from 'next/link'
import React, { useState, useEffect } from 'react';
import '../lib/db'
import firebase from 'firebase';
import 'firebase/firestore';
import Router from 'next/router';

const IndexPage = () => {
 const [currentUser,setCurrentUser] = useState<null | object>(null)
 const auth = firebase.auth()
 const [name,setName] = useState<string|null>(null)

 useEffect(() => {
   if (auth.currentUser != null) {
     setName(auth.currentUser.displayName);
   }
 })

 useEffect(() => {
   auth.onAuthStateChanged((user) => {
     user ? setCurrentUser(user):Router.push('/login')
   })
 })
 const logout = async () => {
   try {
     await auth.signOut()
     Router.push('/login')
   } catch (error) {
     console.log(error.message)
   }
 }

 return (
   <div>
       <h1>Hello { name } 👋</h1>
         <button onClick={logout} className='btn btn-danger'>Logout</button>
   </div>
 )
}

export default IndexPage

ログインしていなかったらログインページに飛びます。
ログインユーザの名前を取得して表示させています。

参考

Next.jsとfirebaseについても書かれているのでこの本で何度も確認しながらコード書きました。本当にお世話になりました。


試行錯誤しながらなんとか完成したのでもっといいやり方あると思いますがメモとして残しておきます。
こうした方がいいなどありましたら教えてください!

サポートしていただけるとこれからも続ける励みになります! 書籍購入などに使わせていただく予定です! 何卒よろしくお願いします^^