【Firebase】Functionsを使ってログイン機能をつくる

Firebaseでは、ログイン機能は、Authenticationプロジェクトがあるため、簡単に作成することができます。

しかし、こんな場合にはどうでしょう。

“OAuth”と、”メール/パスワード”2種類のログインプロバイダを使用する。

こんなときにFunctionsが活躍します。

Functionsとは

それでは、Functionsとは何かから見てみましょう。

公式ドキュメントには、以下のように記載されています。

Firebaseの機能とHTTPSリクエストによってトリガーされたイベントに応じてバックエンドコードを自動的に実行できるサーバーレスフレームワーク

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

要点は以下になります。

  • Googleのサーバーで関数の管理をしてくれる
  • HTTPリクエストを使用して関数を直接起動できる

Functionsの始め方

それではFunctionsを使っていきましょう!

前提として、FirebaseのプロジェクトとFirestoreは既に作成済みのものとします。

FunctionsをFirebaseコンソールの構築から、Firestoreと同じように追加します。

ちなみにFunctionsを使用するには、有料プランのBlazeにアップグレードする必要があります。

Functionsの登録を行うと、親切に手順を教えてくれます。

firebase-toolsをインストールしましょう!

$ npm install -g firebase-tools

$ firebase init

$ firebase deploy

これによりFunctionsのdeployができますが、今は実行する必要はありません。

コンソールでのFunctionsの作成ができました。

プロジェクトの初期化

CLIでfunctionsの初期化を行っていきます。

$ firebase init functions

上記コマンドが正常に完了すると、以下のディレクトリ構成のファイルが生成されます。

myproject
 +- .firebaserc    # Hidden file that helps you quickly switch between
 |                 # projects with `firebase use`
 |
 +- firebase.json  # Describes properties for your project
 |
 +- functions/     # Directory containing all your functions code
      |
      +- .eslintrc.json  # Optional file containing rules for JavaScript linting.
      |
      +- package.json  # npm package file describing your Cloud Functions code
      |
      +- index.js      # main source file for your Cloud Functions code
      |
      +- node_modules/ # directory where your dependencies (declared in
                       # package.json) are installed

生成されたファイルに手を加えていきましょう。

ファイル編集

対象ファイル

Functionsを使うために編集が必要となる対象のファイルは、以下の5つです。

  • functions/index.js
  • src/UserCreateForm.tsx
  • src/Login.tsx
  • src/firebase.ts
  • src/auth.ts

ディレクトリ構成は、本来はcompontsや、libなど使用しますが、ここでは分かりやすいようfunctionsとsrcディレクトリのみとしています。

見える部分から順番に見ていきましょう!

以下の流れの機能を作成します。

  1. src/UserCreateForm.tsx でユーザーを作成し、emailとpasswordを設定する
  2. 1で作られたデータを使ってsrc/Login.tsx でログインする

src/firebase.ts

Functionsを使えるようにするために、まずfunctiosの定義をします。

import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
import { getFunctions } from "firebase/functions";

const firebaseConfig = {
  apiKey: import.meta.env.VITE_API_KEY,
  authDomain: import.meta.env.VITE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_APP_ID,
};

const app = initializeApp(firebaseConfig);

export const db = getFirestore(app);
export const auth = getAuth(app);
export const functions = getFunctions(app, 'asia-northeast1');

src/UserCreateForm.tsx

ユーザー登録のsubmit関数内を以下に記します。

import { httpsCallable } from "firebase/functions";

  const submit = async (data: User) => {

    try {
      const createUser = httpsCallable(functions, "create_user");

      const res = await createUser({
        email: data.email,
        password: data.password,
      });

    } catch (error) {
      console.log(error);
    }
  };

httpsCallableを使ってcreate_user関数を呼び出します。

以下公式ドキュメントです。

関数を呼び出す

呼び出し元のcreate_user関数を作っているところを見てみましょう。

functions/index.js

Cloud Functions と Admin SDK モジュールをインポートします。

// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');

先程呼び出しをしたcreate_user関数の中身を記述します。

exports.create_user = functions
  .region("asia-northeast1")
  .https.onCall((data, context) => {
    const { email, password } = data;

    return new Promise((resolve, reject) => {
      app
        .auth()
        .createUser({
          email,
          password,
        })
        .then((user) => {
          resolve({ user });
        })
        .catch((error) => {
          console.log("Error creating new user:", error);
          reject(error);
        });
    });
  });

regionを指定して、onCallというものを使用します。

functions.https.onCallを使用して、dataとcontextの2つのパラメータを取得して、HTTPS 呼び出し可能関数を作成します。

そして、createUserでユーザーを作成します。

全体としては以下のようになります。

const functions = require("firebase-functions");
const admin = require("firebase-admin");

const app = admin.initializeApp();

exports.create_user = functions
  .region("asia-northeast1")
  .https.onCall((data, context) => {
    const { email, password } = data;

    return new Promise((resolve, reject) => {
      app
        .auth()
        .createUser({
          email,
          password,
        })
        .then((user) => {
          resolve({ user });
        })
        .catch((error) => {
          console.log("Error creating new user:", error);
          reject(error);
        });
    });
  });

これで、authにuserの登録ができたので、ログイン画面を作っていきましょう!

src/Login.tsx

ログインのボタン押下時のsubmit関数のみを以下に記します。

import { login_email } from "../../auth";

  const onSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    login_email(email, password);
  };

submit時に、login_emailという関数に、ログインフォームに入力したemailとpasswordを渡しています。

このlogin_email関数は、auth.tsで作成しているので最後にそこを見て終わりにしましょう!

src/auth.ts

import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase";

export const login_email = (email: string, password: string) => {
  signInWithEmailAndPassword(auth, email, password)
    .then((result) => {
      console.log("USER", result.user);
    })
    .catch((error) => {
      const errorMessage = error.message;
      alert(errorMessage);
    });
};

signInWithEmailAndPasswordを使用してauthにユーザー情報を登録します。

以上で、Functionsを使ったログイン機能を作成することができました。

お読みいただきありがとうございました!

コメント