JavaScript で Facebook ログインを使用して認証する

Facebook ログインをアプリに統合して、ユーザーが Firebase での認証に Facebook アカウントを使用できるようにすることができます。Facebook ログインを統合するには、Firebase SDK を使用してログインフローを行うか、または Facebook ログインフローを手動で行って、取得したアクセス トークンを Firebase に渡します。

始める前に

  1. Firebase を JavaScript プロジェクトに追加します
  2. Facebook for Developers サイトで、アプリのアプリ IDアプリ シークレットを取得します。
  3. Facebook ログインを有効にします。
    1. Firebase コンソールで [Auth] セクションを開きます。
    2. [Sign-in method] タブで [Facebook] を有効にし、Facebook から取得した [アプリ ID] と [アプリ シークレット] を指定します。
    3. 次に、Facebook for Developers サイトの [Product Settings] > [Facebook Login] 構成にある Facebook アプリ設定ページで、OAuth リダイレクト URImy-app-12345.firebaseapp.com/__/auth/handler など)が [OAuth redirect URIs] のうちの一つとしてリストされていることを確認します。

Firebase SDK を使用したログインフローの処理

ウェブアプリをビルドする場合、Facebook アカウントを使用して Firebase によりユーザーを認証する最も簡単な方法は、Firebase JavaScript SDK でログインフローを処理することです(Node.js または他の非ブラウザ環境でユーザーを認証する場合、ログインフローを手動で処理する必要があります)。

Firebase JavaScript SDK でログインフローを処理する手順は次のとおりです。

  1. Facebook プロバイダ オブジェクトのインスタンスを作成します。
    WebWeb
    import { FacebookAuthProvider } from "firebase/auth";
    
    const provider = new FacebookAuthProvider();
    var provider = new firebase.auth.FacebookAuthProvider();
  2. 省略可: 認証プロバイダにリクエストする追加の OAuth 2.0 スコープを指定します。スコープを追加するには、addScope を呼び出します。次に例を示します。
    WebWeb
    provider.addScope('user_birthday');
    provider.addScope('user_birthday');
    認証プロバイダ向けドキュメントをご覧ください。
  3. 省略可: 関連するカスタム OAuth パラメータを明示的に渡すことなく、プロバイダの OAuth フローをローカライズしてユーザーの使用言語にするには、OAuth フローを開始する前に Auth インスタンスの言語コードを更新します。次に例を示します。
    WebWeb
    import { getAuth } from "firebase/auth";
    
    const auth = getAuth();
    auth.languageCode = 'it';
    // To apply the default browser preference instead of explicitly setting it.
    // auth.useDeviceLanguage();
    firebase.auth().languageCode = 'it';
    // To apply the default browser preference instead of explicitly setting it.
    // firebase.auth().useDeviceLanguage();
  4. 省略可: OAuth リクエストと一緒に送信する追加のカスタム OAuth プロバイダ パラメータを指定します。カスタム パラメータを追加するには、OAuth プロバイダのドキュメントで指定されたキーと、対応する値を含むオブジェクトで初期化されたプロバイダで setCustomParameters を呼び出します。
    WebWeb
    provider.setCustomParameters({
      'display': 'popup'
    });
    provider.setCustomParameters({
      'display': 'popup'
    });
    予約済みの必須 OAuth パラメータは許可されず、無視されます。詳細については、認証プロバイダのリファレンスをご覧ください。
  5. Facebook プロバイダ オブジェクトを使用して Firebase での認証を行います。ユーザーに Facebook アカウントでログインするよう促すために、ポップアップ ウィンドウを表示するか、ログインページにリダイレクトします。モバイル デバイスではリダイレクトすることをおすすめします。
    • ポップアップ ウィンドウでログインを行う場合は、signInWithPopup を呼び出します。
      WebWeb
      import { getAuth, signInWithPopup, FacebookAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      signInWithPopup(auth, provider)
        .then((result) => {
          // The signed-in user info.
          const user = result.user;
      
          // This gives you a Facebook Access Token. You can use it to access the Facebook API.
          const credential = FacebookAuthProvider.credentialFromResult(result);
          const accessToken = credential.accessToken;
      
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // The AuthCredential type that was used.
          const credential = FacebookAuthProvider.credentialFromError(error);
      
          // ...
        });
      firebase
        .auth()
        .signInWithPopup(provider)
        .then((result) => {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
      
          // The signed-in user info.
          var user = result.user;
          // IdP data available in result.additionalUserInfo.profile.
            // ...
      
          // This gives you a Facebook Access Token. You can use it to access the Facebook API.
          var accessToken = credential.accessToken;
      
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
      
          // ...
        });
      また、Facebook プロバイダの OAuth トークンを取得することもできます。このトークンによって、Facebook API を使用して追加データをフェッチできます。

      ここではエラーの検出と対応もできます。エラーコードのリストについては、Auth のリファレンス ドキュメントをご覧ください。

    • ログインページにリダイレクトしてログインする場合は、signInWithRedirect を呼び出します。「signInWithRedirect」を使用する場合は、ベスト プラクティスに従ってください。
      WebWeb
      import { getAuth, signInWithRedirect } from "firebase/auth";
      
      const auth = getAuth();
      signInWithRedirect(auth, provider);
      firebase.auth().signInWithRedirect(provider);
      次に、ページが読み込まれたときに getRedirectResult を呼び出すことによって、Facebook プロバイダの OAuth トークンを取得することもできます。
      WebWeb
      import { getAuth, getRedirectResult, FacebookAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      getRedirectResult(auth)
        .then((result) => {
          // This gives you a Facebook Access Token. You can use it to access the Facebook API.
          const credential = FacebookAuthProvider.credentialFromResult(result);
          const token = credential.accessToken;
      
          const user = result.user;
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        }).catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // AuthCredential type that was used.
          const credential = FacebookAuthProvider.credentialFromError(error);
          // ...
        });
      firebase.auth()
        .getRedirectResult()
        .then((result) => {
          if (result.credential) {
            /** @type {firebase.auth.OAuthCredential} */
            var credential = result.credential;
      
            // This gives you a Facebook Access Token. You can use it to access the Facebook API.
            var token = credential.accessToken;
            // ...
          }
          // The signed-in user info.
          var user = result.user;
          // IdP data available in result.additionalUserInfo.profile.
            // ...
        }).catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
          // ...
        });
      ここではエラーの検出と対応もできます。エラーコードのリストについては、Auth のリファレンス ドキュメントをご覧ください。

Firebase コンソールで [1 つのメールアドレスにつき 1 つのアカウント] 設定を有効にしている場合、Firebase ユーザーが、あるプロバイダ(Google など)用にすでに存在しているメールアドレスを使って別のプロバイダ(Facebook など)にログインしようとすると、AuthCredential オブジェクト(Facebook のアクセス トークン)とともにエラー auth/account-exists-with-different-credential がスローされます。目的のプロバイダにログインするには、まず既存のプロバイダ(Google)にログインしてから、目的のプロバイダの AuthCredential(Facebook アクセス トークン)にリンクする必要があります。

signInWithPopup を使用する場合、次のようなコードによって auth/account-exists-with-different-credential エラーを処理できます。

import {
  getAuth,
  linkWithCredential,
  signInWithPopup,
  FacebookAuthProvider,
} from "firebase/auth";

try {
  // Step 1: User tries to sign in using Facebook.
  let result = await signInWithPopup(getAuth(), new FacebookAuthProvider());
} catch (error) {
  // Step 2: User's email already exists.
  if (error.code === "auth/account-exists-with-different-credential") {
    // The pending Facebook credential.
    let pendingCred = error.credential;

    // Step 3: Save the pending credential in temporary storage,

    // Step 4: Let the user know that they already have an account
    // but with a different provider, and let them choose another
    // sign-in method.
  }
}

// ...

try {
  // Step 5: Sign the user in using their chosen method.
  let result = await signInWithPopup(getAuth(), userSelectedProvider);

  // Step 6: Link to the Facebook credential.
  // TODO: implement `retrievePendingCred` for your app.
  let pendingCred = retrievePendingCred();

  if (pendingCred !== null) {
    // As you have access to the pending credential, you can directly call the
    // link method.
    let user = await linkWithCredential(result.user, pendingCred);
  }

  // Step 7: Continue to app.
} catch (error) {
  // ...
}

リダイレクト モード

このエラーはリダイレクト モードでも同様の方法で処理されますが、ページをリダイレクトする間、保留された認証情報をキャッシュに保存する必要がある点が異なります(セッション ストレージなどを使用します)。

Facebook アカウントを使用して Firebase での認証を行うために、Facebook ログイン JavaScript SDK を使用してログインフローを処理することもできます。

  1. デベロッパー向けドキュメントに沿って、アプリに Facebook ログインを統合します。必ず Facebook アプリ ID を使用して Facebook ログインを構成します。
    <script src="//connect.facebook.net/en_US/sdk.js"></script>
    <script>
      FB.init({
        /**********************************************************************
         * TODO(Developer): Change the value below with your Facebook app ID. *
         **********************************************************************/
        appId: '<YOUR_FACEBOOK_APP_ID>',
        status: true,
        xfbml: true,
        version: 'v2.6',
      });
    </script>
  2. Facebook の認証状態に対するリスナーも設定します。
    FB.Event.subscribe('auth.authResponseChange', checkLoginState);
  3. Facebook ログインを統合したら、Facebook ログインボタンをウェブページに追加します。
    <fb:login-button
      data-auto-logout-link="true"
      scope="public_profile,email"
      size="large"
    ></fb:login-button>
  4. Facebook の認証状態のコールバックで、Facebook の認証応答から取得した認証トークンを Firebase 認証情報と交換して、Firebase にログインします。
    WebWeb
    import { getAuth, onAuthStateChanged, signInWithCredential, signOut, FacebookAuthProvider } from "firebase/auth";
    const auth = getAuth();
    
    function checkLoginState(response) {
      if (response.authResponse) {
        // User is signed-in Facebook.
        const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
          unsubscribe();
          // Check if we are already signed-in Firebase with the correct user.
          if (!isUserEqual(response.authResponse, firebaseUser)) {
            // Build Firebase credential with the Facebook auth token.
            const credential = FacebookAuthProvider.credential(
                response.authResponse.accessToken);
    
            // Sign in with the credential from the Facebook user.
            signInWithCredential(auth, credential)
              .catch((error) => {
                // Handle Errors here.
                const errorCode = error.code;
                const errorMessage = error.message;
                // The email of the user's account used.
                const email = error.customData.email;
                // The AuthCredential type that was used.
                const credential = FacebookAuthProvider.credentialFromError(error);
                // ...
              });
          } else {
            // User is already signed-in Firebase with the correct user.
          }
        });
      } else {
        // User is signed-out of Facebook.
        signOut(auth);
      }
    }
    function checkLoginState(response) {
      if (response.authResponse) {
        // User is signed-in Facebook.
        var unsubscribe = firebase.auth().onAuthStateChanged((firebaseUser) => {
          unsubscribe();
          // Check if we are already signed-in Firebase with the correct user.
          if (!isUserEqual(response.authResponse, firebaseUser)) {
            // Build Firebase credential with the Facebook auth token.
            var credential = firebase.auth.FacebookAuthProvider.credential(
                response.authResponse.accessToken);
    
            // Sign in with the credential from the Facebook user.
            firebase.auth().signInWithCredential(credential)
              .catch((error) => {
                // Handle Errors here.
                var errorCode = error.code;
                var errorMessage = error.message;
                // The email of the user's account used.
                var email = error.email;
                // The firebase.auth.AuthCredential type that was used.
                var credential = error.credential;
                // ...
              });
          } else {
            // User is already signed-in Firebase with the correct user.
          }
        });
      } else {
        // User is signed-out of Facebook.
        firebase.auth().signOut();
      }
    }
    ここではエラーの検出と対応もできます。エラーコードのリストについては、Auth のリファレンス ドキュメントをご覧ください。
  5. また、不要な再認証を避けるために、Facebook ユーザーがすでに Firebase にログインしていないかをチェックする必要があります。
    WebWeb
    import { FacebookAuthProvider } from "firebase/auth";
    
    function isUserEqual(facebookAuthResponse, firebaseUser) {
      if (firebaseUser) {
        const providerData = firebaseUser.providerData;
        for (let i = 0; i < providerData.length; i++) {
          if (providerData[i].providerId === FacebookAuthProvider.PROVIDER_ID &&
              providerData[i].uid === facebookAuthResponse.userID) {
            // We don't need to re-auth the Firebase connection.
            return true;
          }
        }
      }
      return false;
    }
    function isUserEqual(facebookAuthResponse, firebaseUser) {
      if (firebaseUser) {
        var providerData = firebaseUser.providerData;
        for (var i = 0; i < providerData.length; i++) {
          if (providerData[i].providerId === firebase.auth.FacebookAuthProvider.PROVIDER_ID &&
              providerData[i].uid === facebookAuthResponse.userID) {
            // We don't need to re-auth the Firebase connection.
            return true;
          }
        }
      }
      return false;
    }

Node.js アプリケーションで Firebase により認証する手順は、次のとおりです。

  1. Facebook アカウントを持つユーザーでログインし、そのユーザーの Facebook アクセス トークンを取得します。たとえば、手動でログインフローを処理するセクションの説明に沿ってブラウザでユーザーをログインします。ただしクライアント アプリでアクセス トークンを使用するのではなく、Node.js アプリケーションにアクセス トークンを送信します。
  2. ユーザーの Facebook アクセス トークンを取得した後、アクセス トークンを使用して認証情報オブジェクトをビルドし、その認証情報でユーザーにログインします。
    WebWeb
    import { getAuth, signInWithCredential, FacebookAuthProvider } from "firebase/auth";
    
    // Sign in with the credential from the Facebook user.
    const auth = getAuth();
    signInWithCredential(auth, credential)
      .then((result) => {
        // Signed in 
        const credential = FacebookAuthProvider.credentialFromResult(result);
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The AuthCredential type that was used.
        const credential = FacebookAuthProvider.credentialFromError(error);
        // ...
      });
    // Sign in with the credential from the Facebook user.
    firebase.auth().signInWithCredential(credential)
      .then((result) => {
        // Signed in       
        var credential = result.credential;
        // ...
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
        // ...
      });

Chrome 拡張機能で Firebase による認証を行う

Chrome 拡張機能アプリを作成する場合は、画面外ドキュメントのガイドをご覧ください。

プロジェクトの作成時に、Firebase は次のようなプロジェクト固有のサブドメインをプロビジョニングします。https://my-app-12345.firebaseapp.com

これは、OAuth ログインのリダイレクト メカニズムとしても使用されます。このドメインは、サポートされているすべての OAuth プロバイダに許可される必要があります。この場合、ユーザーが Facebook にログインしている間、アプリケーションにリダイレクトする前に [Continue to: https://my-app-12345.firebaseapp.com] のようにドメインを含むメッセージが表示される可能性があります。

サブドメインが表示されないようにするには、Firebase Hosting でカスタム ドメインを設定します。

  1. Hosting 用にドメインを設定するの手順 1~3 を行います。ドメインの所有権を確認すると、Hosting はカスタム ドメイン向けに SSL 証明書をプロビジョニングします。
  2. Firebase コンソールで、認可済みドメインのリストにカスタム ドメイン auth.custom.domain.com を追加します。
  3. Facebook のデベロッパー コンソールまたは OAuth 設定ページで、リダイレクト ページの URL(https://auth.custom.domain.com/__/auth/handler)を許可リストに登録します。これにより、カスタム ドメインでアクセス可能になります。
  4. JavaScript ライブラリを初期化するときに、authDomain フィールドにカスタム ドメインを指定します。
    var config = {
      apiKey: '...',
      // Changed from 'PROJECT_ID.firebaseapp.com'.
      authDomain: 'auth.custom.domain.com',
      databaseURL: 'https://PROJECT_ID.firebaseio.com',
      projectId: 'PROJECT_ID',
      storageBucket: 'PROJECT_ID.firebasestorage.app',
      messagingSenderId: 'SENDER_ID'
    };
    firebase.initializeApp(config);

次のステップ

ユーザーが初めてログインすると、新しいユーザー アカウントが作成され、ユーザーがログイン時に使用した認証情報(ユーザー名とパスワード、電話番号、または認証プロバイダ情報)にアカウントがリンクされます。この新しいアカウントは Firebase プロジェクトの一部として保存され、ユーザーのログイン方法にかかわらず、プロジェクトのすべてのアプリでユーザーを識別するために使用できます。

  • アプリでユーザーの認証ステータスを把握するには、Auth オブジェクトにオブザーバーを設定することをおすすめします。これによって、ユーザーの基本的なプロフィール情報を User オブジェクトから取得できます。ユーザーを管理するをご覧ください。

  • Firebase Realtime DatabaseCloud Storageセキュリティ ルールでは、ログイン済みユーザーの一意のユーザー ID を auth 変数から取得し、それを使用して、ユーザーがアクセスできるデータを制御できます。

既存のユーザー アカウントに認証プロバイダの認証情報をリンクすることで、ユーザーは複数の認証プロバイダを使用してアプリにログインできるようになります。

ユーザーのログアウトを行うには、signOut を呼び出します。

WebWeb
import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});
firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});