VIVITAソフトウェアエンジニアの板本@itamotoです。
Firebaseを利用して、サーバー無しでお手軽に動くものをサクッと作ることは良くあるケースだと思います。
プロトタイプを作って、そのまま使い続けるものがいくつか出てきたので、それらを認証を共有して連携させる試みです。
WEBサーバーを別途追加した構成でシングルサインオンを試してみました。
処理の流れ
Firebase側の設定
Authentication
今回はGoogleアカウントを使った認証を使います。
Cloud Firestore
データベースのルールには以下を設定
認証したユーザだけが読み書きできるデータを作ります。
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read, write: if (request.auth.uid == userId); } } }
以下のようにデータを作成しました。
ドキュメント部分にはuidを設定していますが、後述するフロントエンドで実際にログインをしてfirebase上に登録されたユーザー情報を確認しています。
(本来であれば書き込み機能があれば良いので、事前にデータ作成は必要ないです。)
フロントエンド
access tokenの取得
アプリ1でaccess tokenを取得する処理
import firebase from 'firebase'; const provider = new firebase.auth.GoogleAuthProvider(); provider.addScope('https://www.googleapis.com/auth/contacts.readonly'); this.$store.getters.firebase .auth() .signInWithPopup(provider) .then(result => { console.log(result.credential.accessToken); // access token }) .catch(function(error) { console.error(errorCode + ' : ' + errorMessage); });
アプリ2からaccess token付きでAPIをリクエスト
import axios from 'axios'; const response = await axios.get( 'http://[serverHost]/users/me/profile', { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}` } } ); console.log(response);
サーバーサイド
firestoreからユーザーの情報を取得して返すAPI
access tokenを受け取ってuidを抽出
const fbAdmin = require('firebase-admin'); const restify = require('restify'); let fbDefault; let fbUser; ~~~~~~ const firebaseInitializeApp = async uid => { if (!uid) { if (!fbDefault) { fbDefault = fbAdmin.initializeApp( { credential: fbAdmin.credential.applicationDefault(), databaseURL: 'https://xxxxxx.firebaseio.com', databaseAuthVariableOverride: { uid: uid } }, 'userRole' ); } } else { if (!fbUser) { fbUser = fbAdmin.initializeApp({ credential: fbAdmin.credential.applicationDefault(), databaseURL: 'https://xxxxxx.firebaseio.com' }); } } }; server.get('/users/me/profile', async (req, res, next) => { allowCors(res); try { const uid = await tokenCheck(req.header('Authorization')); await firebaseInitializeApp(uid); const userSnapshot = await fbUser .firestore() .collection('users') .doc(uid) .get(); res.send(200, { result: 'ok', id: uid, nickname: userSnapshot.get('nickname') }); return next(); } catch (error) { return next( new InternalError({ toJSON: () => ({ errors: [{ message: 'InternalError' }] }) }) ); } }); const tokenCheck = async token => { firebaseInitializeApp(); const decodedToken = await fbDefault.auth().verifyIdToken(token); const firestore = fbDefault.firestore(); return decodedToken.uid; };
まとめ
Firebaseを使ったアプリをどんどん連携していきたいところですが、
シングルサインオンで次々つなげて行く場合、Authenticationの元になるFirestoreに複数のアプリのデータを集約しなければならないのが懸念です。
今の所Cloud Firesoreはプロジェクトで1つしか作れないですしね。
今後試してみてより良い方法があれば、またまとめてみようと思います。