こんにちは、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を触る機会も多いので試してみました。
参考
以上