🗂

【中編】Next.jsでログインフォームを実装する 〜firebase authentication編〜

2023/09/04に公開

概要

前編に引き続き、Next.jsのログインフォームについて中編です!
今回は、firebaseの初期設定をして、authに新規登録して、ログインすることとcloud firestoreにユーザ情報を格納するまでを実装していこうと思います!
前回の記事
前編 chakra-ui×react-hook-form編

https://zenn.dev/ko_hei/articles/28e7709e187c0a

中編 firebase authentication編 ←今回の記事です
後編 バリデーションとトリガー編

使用する技術は以下になります。

  • Next.js
  • chakra-ui
  • react-hook-form
  • firebase
    • authentication
    • cloud firestore
    • cloud functions

firebaseの初期設定

前回までで、サインイン画面、サインアップ画面の画面の実装までを終えています。
本記事ではfirebaseとの接続を設定していきます。この章では、firebaseの新規プロジェクト作成と、初期設定について説明していきます。
まずは、firebaseのコンソールから、新規プロジェクトを作成していきます。

https://firebase.google.com/?hl=ja

1. プロジェクトの作成

プロジェクト名をauth-formとして作成します(名前は任意で構いません)
「続行」ボタンを押下して、次の画面に遷移します。
次の画面以降は、初期状態で「続行」、自身のアカウントを選択して、プロジェクトを新規作成します。

2. webアプリの登録

プロジェクトが作成されたら、次のような画面が表示されます。
次に、firebaseを使用するwebアプリの登録を行います。
画像の赤枠で囲まれているアイコンを押下してwebアプリを登録していきます。

アプリ名を入力して「アプリを登録」ボタンを押下します。
※Hostingについて今回は、チェックをつけていませんが、今後利用する予定があれば、チェックをつけてください

登録すると、Firebase SDKが表示され、npm installコマンドと接続に必要なkeyが表示されると思います。
このkeyはNext.jsのプロジェクトとfirebaseの接続で使用します。他人に知られてしまうと悪用されたり、第三者がfirebaseに接続できてしまうので、扱いには注意が必要です!

3. Authenticationの有効化

それでは次に、firebase authenticationを有効化し、今回、Email/Password認証を使って実装していくので、認証プロバイダーを有効化していきましょう。
authenticationはサイドバーの「構築」タブにあるので、押下します。

「始める」ボタンでauthenticationを利用することができます。

「メール/パスワード」を選択して、有効化します。
※パスワードなしでログインもありますが、これは無効の状態にしておいてください。

これで、firebase側のauthenticationの設定は完了です!

Next.jsとfirebaseの接続

次は、設定したfirebaseのプロジェクトをNext.jsでも使用できるように設定をしていきます。
まずは、firebaseで生成したkeyをNext.jsで使用できるように環境変数として値を格納しておきます。

1. パッケージのインストール

firebaseのメソッドや型ををNext.jsで使用するために、下記のコマンドを実行して、パッケージをインストールします。

npm install firebase

2. 環境変数の設定

Next.jsプロジェクトの最上層に.envファイルを作成します。

.env
# firebase
NEXT_PUBLIC_FIREBASE_API_KEY='自身のfirebaseプロジェクトのapiKey'
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN='自身のfirebaseプロジェクトのauthDomain'
NEXT_PUBLIC_FIREBASE_PROJECT_ID='自身のfirebaseプロジェクトのprojectId'
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET='自身のfirebaseプロジェクトのstorageBucket'
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID='自身のfirebaseプロジェクトのmessagingSenderId'
NEXT_PUBLIC_FIREBASE_APP_ID='自身のfirebaseプロジェクトのappId'

2. firebase config

環境変数の準備ができたので、firebaseの初期化をしていきます!下記のフォルダ構成で実装します。

src
 ├ app
 ├ common
 └ lib
     └ firebase
         ├ apis
         │  ├ auth.ts 
         │  └ user.ts
         └ config.ts

firebaseの初期化はconfig.tsで設定します

config.ts
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore'

export const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
}

const app = initializeApp(firebaseConfig)
export const auth = getAuth(app)
export const db = getFirestore(app)

これで、firebaseとNext.jsの接続の手順が完了しました。
正常にauthに登録されるかどうか確かめてみましょう。

フォームとfirebaseの接続

実際に、authにユーザを登録する処理を実装していきます。

src
 ├ app
 ├ common
 └ lib
     └ firebase
         ├ apis
         │  ├ auth.ts //←ここに実装
         │  └ user.ts
         └ config.ts

Email/Passwordの認証メソッドをそれぞれ宣言します。(ログアウト処理も一応宣言しておきます。)

import {
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
} from 'firebase/auth'

import { auth } from '@/lib/firebase/config'

/**
 * EmailとPasswordでサインイン
 * @param email
 * @param password
 * @returns Promise<boolean>
 */
export const signInWithEmail = async (args: {
  email: string
  password: string
}): Promise<boolean> => {
  let result: boolean = false
  try {
    const user = await signInWithEmailAndPassword(
      auth,
      args.email,
      args.password
    )

    if (user) {
      result = true
    }
  } catch (error) {
    result = false
    console.log(error)
  }
  return result
}

/**
 * EmailとPasswordでサインアップ
 * @param username
 * @param email
 * @param password
 * @returns Promise<boolean>
 */
export const signUpWithEmail = async (args: {
  email: string
  password: string
}): Promise<boolean> => {
  let result: boolean = false
  try {
    const user = await createUserWithEmailAndPassword(
      auth,
      args.email,
      args.password
    )
    if (user) {
      result = true
    }
  } catch (error) {
    result = false
    console.log(error)
  }
  return result
}

/**
 * ログアウト処理
 * @returns Promise<boolean>
 */
export const logout = async (): Promise<boolean> => {
  let result: boolean = false

  await signOut(auth)
    .then(() => {
      result = true
    })
    .catch((error) => {
      console.log(error)
      result = false
    })

  return result
}

auth処理が実装できたら、ログインフォームで入力された値を使って、auth処理を呼び出していきたいと思います。

signin/page.tsx
..省略..
/** サインイン画面
 * @screenname SignInScreen
 * @description ユーザのサインインを行う画面
 */
export default function SignInScreen() {
  const { handleSubmit, register } = useForm<formInputs>()

  const [show, setShow] = useState<boolean>(false)

  const onSubmit = handleSubmit(async (data) => {
    /** ここから処理追加 */
    signInWithEmail({ email: data.email, password: data.password }).then(
      (res: boolean) => {
        if (res) {
          console.log('ログイン成功')
        } else {
          console.log('ログイン失敗')
        }
      }
    )
  })
  return (..省略..)
}

ログインが成功したら、コンソールに「ログイン成功」、失敗したら、「ログイン失敗」の文言が表示されます。
続いて、サインアップ画面に処理を追加していきます。

signup/page.tsx
..省略..
/** サインアップ画面
 * @screenname SignUpScreen
 * @description ユーザの新規登録を行う画面
 */
export default function SignUpScreen() {
  const { handleSubmit, register } = useForm<formInputs>()

  const [password, setPassword] = useState(false)
  const [confirm, setConfirm] = useState(false)

  const onSubmit = handleSubmit(async (data) => {
    /** ここから処理追加 */
    signUpWithEmail({ email: data.email, password: data.password }).then(
      (res: boolean) => {
        if (res) {
          console.log('新規登録成功')
        } else {
          console.log('新規登録失敗')
        }
      }
    )
  })

  const passwordClick = () => setPassword(!password)
  const confirmClick = () => setConfirm(!confirm)

  return (..省略..)
}

サインアップでも、同様にonSubmitメソッドで新規登録処理を呼び出します。
これで、フォームとfirebaseのauth処理との接続ができました!
実際に画面を操作して、ユーザが新規登録されるか、そして、作成したユーザでログインできるかを確かめてみましょう!

下記の入力情報で、新規作成を実施してみます。

コンソールに「新規登録成功」と表示されています!※2回押しちゃってんで、2回目はエラーが出てます。

firebase consoleを確認してみます!ユーザが追加されていることがわかります!

これでfirebase authenticationにユーザ登録完了です!
では、追加された後にユーザ情報をcloud firestoreに格納する処理を追加しましょう!
まずは、firestoreを有効化します!「データベース作成」から、本番環境、地域を東京で選択してfirestoreを使用できるように設定します

firestoreは初期設定で、セキュリティルールの設定で、権限がない状態にあるので、まずは、フル権限で設定しておきます。

ここまで設定できたら、サインアップ処理にコードを追加します!

apis/auth.tsx
/**
 * EmailとPasswordでサインアップ
 * @param username
 * @param email
 * @param password
 * @returns Promise<boolean>
 */
export const signUpWithEmail = async (args: {
  email: string
  password: string
}): Promise<boolean> => {
  let result: boolean = false
  try {
    const user = await createUserWithEmailAndPassword(
      auth,
      args.email,
      args.password
    ).then(async (userCredential) => {
      /** thenから追加します */
      /** cloud firestoreのコレクション */
      const colRef = doc(db, 'users', userCredential.user.uid)
      /** document追加 */
      await setDoc(colRef, {
        uid: userCredential.user.uid,
        email: userCredential.user.email,
      })
      return userCredential.user
    })
    if (user) {
      result = true
    }
  } catch (error) {
    result = false
    console.log(error)
  }
  return result
}

別ユーザの情報で、再度新規登録してみます

コンソール、authentication、firestoreで確認してみましょう!

コンソール

authentication

firestore

全て新規登録されていますね!これで、新規作成処理は完了です!次はログイン処理を確認してましょう!

ログイン処理

下記の情報でフォームを入力します。

コンソールで「ログイン成功」が表示されました!これで完了です!

おわりに

これでフォームからユーザの新規登録、ログイン処理までの流れを実装できました!
auth周りの最低限の機能はこれで全てになります!ログアウトについても、処理自体は掲載しているので、ボタンを配置して、処理を呼び出せばログアウトすることができます。
後編では、authのエラーハンドリングやバリデーションエラーについて実装、説明していきます!

最後まで読んでいただきありがとうございました!

Discussion