LoginSignup
14
15

More than 3 years have passed since last update.

Next.js、TypeScript、FirebaseAuthでメール認証(確認メールつき)

Last updated at Posted at 2021-02-16

環境

$ yarn -v
1.22.10

作成

Firebase側の設定

この辺の記事を参考に新規プロジェクトを作成し、Authの設定でメールを有効化。
https://webbibouroku.com/Blog/Article/firebase-auth

Next.jsプロジェクト開始

$ yarn create next-app sample_app
$ cd sample_app

TypeScriptを導入

$ touch tsconfig.json
$ yarn add --dev typescript @types/react @types/node
$ mv pages/_app.js pages/_app.tsx
$ mv pages/index.js pages/index.tsx
$ yarn dev

環境変数をセット

$ touch .env
.env(各自Firebaseで取得した値をセットする)
FIREBASE_KEY=AIzaSyC7PpE2RvbHohTN292P
FIREBASE_DOMAIN=scr.firebaseapp.com
FIREBASE_PROJECT_ID=scra
FIREBASE_STORAGE_BUCKET=stips.appspot.com
FIREBASE_SENDER_ID=10800240
FIREBASE_APP_ID=1:108200240:web:f6d1bd8762b07575
FIREBASE_MEASUREMENT_ID=G-9ZD3QCY
$ touch next.config.js
next.config.js
module.exports = {
  env: {
    FIREBASE_KEY: process.env.FIREBASE_KEY,
    FIREBASE_DOMAIN: process.env.FIREBASE_DOMAIN,
    FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
    FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
    FIREBASE_SENDER_ID: process.env.FIREBASE_SENDER_ID,
    FIREBASE_APPID: process.env.FIREBASE_APPID
  }
}

FirebaseAuthの設定

$ yarn add firebase
$ mkdir utils
$ touch utils/firebase.ts
src/utils/firebase.ts
import 'firebase/auth'
import 'firebase/firestore'

import firebase from 'firebase/app'

const config = {
  apiKey: process.env.FIREBASE_KEY,
  authDomain: process.env.FIREBASE_DOMAIN,
  databeseURL: process.env.FIREBASE_DATABASE,
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_SENDER_ID,
  appId: process.env.FIREBASE_APPID,
}
// initializeを複数回走らせない
if (firebase.apps.length === 0) {
  firebase.initializeApp(config)
}
const auth = firebase.auth()
export { auth }
$ mkdir auth
$ touch auth/AuthProvider.tsx
src/auth/AuthProvider.tsx
import { User } from 'firebase'
import { FC, createContext, useEffect, useState } from 'react'
import { auth } from '../utils/firebase'

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

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

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

  useEffect(() => {
    auth.onAuthStateChanged((user) => {
      setCurrentUser(user)
    })
  }, [])

  return (
    <AuthContext.Provider value={{ currentUser }}>
      {children}
    </AuthContext.Provider>
  )
}

export { AuthContext, AuthProvider }

ログイン画面を作成

$ touch pages/login.tsx
src/pages/login.tsx
import React, { useEffect, useState, FC } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { auth } from '../utils/firebase'

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

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

  const logIn = async (e) => {
    e.preventDefault()
    try {
      await auth.signInWithEmailAndPassword(email, password)
      router.push('/')
    } catch (err) {
      alert(err.message)
    }
  }

  return (
    <div>
      <form onSubmit={logIn}>
        <div>
          <label htmlFor="email">
            Email:{' '}
          </label>
          <input
            id="email"
            type="email"
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="password">
            Password:{' '}
          </label>
          <input
            id="password"
            type="password"
            onChange={(e) => setPassword(e.target.value)}
          />
        </div>
        <button type="submit">
          Login
        </button>
      </form>
      <Link href="/signup">
        <a>signup</a>
      </Link>
    </div>
  )
}

export default Login

サインアップ画面を作成

$ touch pages/signup.tsx
signup.tsx
import { FC, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'

import { auth } from '../utils/firebase'

const SignUp: FC = () => {
  const router = useRouter()
  const [email, setEmail] = useState<string>('')
  const [password, setPassword] = useState<string>('')

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

  const createUser = async (e) => {
    e.preventDefault()
    try {
      await auth.createUserWithEmailAndPassword(email, password)
      await auth.currentUser.sendEmailVerification()
      router.push('/sent')
    } catch (err) {
      alert(err.message)
    }
  }

  return (
    <div>
      <form onSubmit={createUser}>
        <div>
          <label htmlFor="email">
            Email:{' '}
          </label>
          <input
            id="email"
            type="email"
            onChange={(e) => setEmail(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="password">
            Password:{' '}
          </label>
          <input
            id="password"
            type="password"
            onChange={(e) => setPassword(e.target.value)}
          />
        </div>
        <button type="submit">
          SignUp
        </button>
      </form>
      <Link href="/login">
        <a>Login</a>
      </Link>
    </div>
  )
}

export default SignUp

メール送信後画面を作成

$ touch pages/sent.tsx
pages/sent.tsx
import { FC, useEffect } from 'react'
import { useRouter } from 'next/router'

import { auth } from '../utils/firebase'

const SignUp: FC = () => {
  const router = useRouter()

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

  const sendEmailVerification = async () => {
    try {
      await auth.currentUser.sendEmailVerification()
      alert('認証メールを再送しました')
      router.push('/sent')
    } catch (err) {
      alert(err.message)
    }
  }

  return (
    <div>
      <p>メール内のリンクをクリックしてユーザーを有効化してください。</p>
      <button onClick={sendEmailVerification}>再送する</button>
    </div>
  )
}

export default SignUp

トップページを作成

pages/index.tsx
import { useEffect, FC, useState } from 'react'
import { useRouter } from 'next/router'

import { auth } from '../utils/firebase'

const Home: FC = (props: any) => {
  const router = useRouter()
  const [currentUser, setCurrentUser] = useState<null | object>(null)

  useEffect(() => {
    auth.onAuthStateChanged((user) => {
      user ? setCurrentUser(user) : router.push('/login')
      !user.emailVerified && router.push('/sent')
    })
  }, [])

  const logOut = async () => {
    try {
      await auth.signOut()
      router.push('/login')
    } catch (error) {
      alert(error.message)
    }
  }

  return (
    <div>
      <pre>{currentUser && JSON.stringify(currentUser, null, 4)}</pre>
      <button onClick={logOut}>Logout</button>
    </div>
  )
}

export default Home

参考

14
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
15