Firebase + ReactでGitHubのソーシャルログインを実装してみた

2022.06.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

GitHubのアクセストークンをブラウザ上でインタラクティブに取得する方法を検討していたところ、Firebaseを使用すれば簡単に実現できそうでした。

そこで今回は、Firebase + ReactでGitHubのソーシャルログインを実装してみました。

やってみた

こちらのドキュメントを参考に実装を行ってみました。

Firebase Projectの作成

Firebase Consoleで[Add project]をクリック。

プロジェクト名を指定して[Continue]をクリック。

Google Analyticsは今回は使わないので無効にして、[Create project]をクリック。

Projectを作成できました。[Continue]をクリック。

Firebase ProjectへのAppの登録

Firebase ProjectへWeb Appを登録します。

Project Overview画面中央の[Web]をクリック。

App nicknameを適当に指定して[Register app]をクリック。

表示されるfirebaseConfigの内容を控えておきます。(Project settingsで後から確認も可能です)[Continue to the console]をクリックしてProjectトップに戻ります。

ProjectにAppが登録できていますね。

GitHub Providerの追加

Firebase ProjectにGitHub Providerを追加します。これによりFirebaseのAppでの認証でGitHubのソーシャルログインを可能とします。

サイドパネルから[Authentication]を開き、[Get started]をクリック。

[Sign-in providers > Additional providers]でGitHubを選択。

[Enable]を有効にし、callback URLを控えます。

GitHubでOAuth Appsを作成します。

New OAuth Applicationを開き、[Authorization callback URL]で先ほど控えたURLを指定して[Register application]をクリック。

GitHub Appsを作成できました。Client IDとClient secrets([Generate a new client secret]をクリックして生成)を控えます。

先ほどのFirebaseの画面に戻り、控えたClient IDとClient secretsを指定して[Save]をクリック。

ProjectにGitHub Providerを追加できました。

React Appの作成

create-react-appを実行してReact Appを作成します。

$ npx create-react-app test220612 --template typescript

firebaseをインストールします。

$ npm i firebase

Firebase Appを初期化するModuleを作成します。firebaseConfigには「Firebase ProjectへのAppの登録」で控えた値を指定してください。

src/firebaseApp.tsx

import { initializeApp } from 'firebase/app';

const firebaseConfig = {
  apiKey: '<apiKey>',
  authDomain: '<authDomain>',
  projectId: '<projectId>',
  appId: '<appId>',
};

initializeApp(firebaseConfig);

src/App.tsxを次のように修正します。

src/App.tsx

import logo from './logo.svg';
import './App.css';
import './firebaseApp';

import { useEffect, useState } from 'react';
import {
  getAuth,
  Auth,
  GithubAuthProvider,
  signInWithPopup,
} from 'firebase/auth';

const OWNER = "<OWNER>"
const REPO = "<REPO>"

function App() {
  const [token, setToken] = useState<string | null>(null);
  const [auth, setAuth] = useState<Auth | null>(null);
  const [provider, setProvider] = useState<GithubAuthProvider | null>(null);

  // GitHub OAuth Provider ObjectのInstanceを作成
  useEffect(() => {
    if (provider === null) {
      const newProvider = new GithubAuthProvider();
      newProvider.addScope('repo'); // 既定ではユーザー自身のemailを取得するスコープしか付与されない。必要に応じてスコープを追加する
      setProvider(newProvider);
    }
  }, [provider]);

  // Firebase Appに対するAuth instanceを取得
  useEffect(() => {
    if (provider !== null && auth === null) {
      setAuth(getAuth());
      console.log(auth);
    }
  }, [auth, provider]);

  // ポップアップによるサインインを実施し、成功したらアクセストークンを取得する
  useEffect(() => {
    if (provider !== null && auth !== null && token === null) {
      signInWithPopup(auth, provider).then((result) => {
        const credential = GithubAuthProvider.credentialFromResult(result);
        if (credential && credential.accessToken) {
          setToken(credential.accessToken);
          console.log('token: ' + credential.accessToken);
        }
        console.log(result.user);
      });
    }
  }, [auth, provider, token]);

  // アクセストークンを使用してGitHub API(GET /Issues)へリクエストする
  useEffect(() => {
    if (token !== null) {
      fetch(`https://api.github.com/repos/${OWNER}/${REPO}/issues`, {
        headers: {
          Authorization: `token ${token}`,
          Accept: 'application / vnd.github.v3 + json',
        },
      }).then((result) => {
        result.json().then((result) => {
          console.log(result);
        });
      });
    }
  }, [token]);

  return (
    <div className='App'>
      <header className='App-header'>
        <img src={logo} className='App-logo' alt='logo' />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className='App-link'
          href='https://reactjs.org'
          target='_blank'
          rel='noopener noreferrer'
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

動作確認

React Appをローカルで起動します。

$ npm run start

すると別タブ(ブラウザによってはポップアップ)でGitHubのサインイン画面がポップアップ表示されるのでサインインします。

付与するアクセス権を確認したら認可します。

サインインが成功するとAppの画面に戻ります。コンソールに各種情報が表示されています。

UserImplを見ると、サインインしたユーザーの情報が取得できています。

Arrayを見ると、アクセストークンを使用して取得したRepository上のIssue一覧が取得できています。

補足

上手くできなかった方法

今回サインインの実装ではsignInWithPopup Methodを使用しましたが、もう一つの方法としてsignInWithRedirectで同じブラウザタブで別URLにリダイレクトしてその結果をgetRedirectResultで取得するという方法もドキュメントでは紹介されていました。

後者の方法も試してみましたが、同じブラウザタブで別URLにリダイレクトすることによりReact hooksのStateが吹っ飛んでしまうため上手く動作しませんでした。ここは実装次第な気もします。上手くできた方がいれば教えてください。

おわりに

Firebase + ReactでGitHubのソーシャルログインを実装してみました。

目的は「GitHubのアクセストークンをブラウザ上でインタラクティブに取得する」ことだったのでFirebaseを使う必要性は無かったのですが、いろいろ応用でしたし、最近Firebaseを触る機会も多いので試してみました。

参考

以上