Firebase(WebApp)アプリ開発の連載記事第四弾。長らく投稿できておりませんでしたが、今回のAuthenticationとHostingの説明をして完結とします。※前回記事はコチラ

1.Firebase Authenticate

FirebaseのAuthenticate機能を使ってユーザー管理を行うことができます。Sign-In methodから認証方式を選択できます。

認証方式は、メール/パスワード、電話番号、Google、Facebook、Twitter、Github、Yahoo、Microsoft、Appleなどほとんどの有名プロバイダーと連携ができます。今回はGoogleによるOAuth認証を有効にしてみます。

① OAuth認証の有効

OAuthを有効にするためには、承認済みドメインにGCPのドメインを追加する必要があります。これは、表示している画面がFirebaseのAPIが実行可能であることを設定するということです。
今回は、CloudShellのウェブでプレビューボタンで表示された画面にたいして、FirebaseのAPI実行を許可したいので、プレビューを表示している状態で、URLのドメイン部分をコピーして、Firebaseの設定画面にコピーペーストします。

ドメイン部分(cloudshell.devまで)をコピーしたら、次は、FirebaseのコンソールのAuthenticationから、「認証済みドメイン」の「ドメインを追加」でコピーしたURLをペーストします。

一覧に追加されたら完了です。

② Firebase UI Authの追加

ログインボタン、クリック処理などを独自に処理を作り込むことはできるのですが、ログインボタンなどのUI用のライブラリがあるのでそれを使いかんたんに実装します。
前回のプログラムに以下のJavascriptとCSSを追加してください。

<script src=”https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.js”></script>
<link type=”text/css” rel=”stylesheet” href=”https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.css” />

③ ログイン状態のチェック

画面表示時点で、ログインが行われているかどうかをチェックして、ログインしていなければログインボタンのみを表示し、ログインしていれば前回のプログラムの表示と同様にテキストボックスとデータベースの内容を表示するようにします。

未ログイン時には、Googleでログインと記載されたボタンが表示されます。

④ ログイン判定と表示の切り替え

ログイン判定は、onAuthStateChangedのコールバック関数の引数 user の状態でログイン状態を判定できます。userにユーザー情報が指定されている場合、ログイン状態になります。それ以外の場合は未ログインとなります。
画面を表示した時点で、上記のイベントを検知し、ログインが行われているかどうかを判定して、先に表示したログインボタンの表示を切り替えます。

firebase.auth().onAuthStateChanged((user) => {  if (user) {
    // ログイン状態の場合
  } else {
    // 未ログイン状態の場合
  }
});

⑤ Firebase UI Authの初期化

未ログイン時、Firebase UI Authを初期化し、ログインボタンを表示します。

以下の設定をJavaScript オブジェクト形式で指定します。

  • signInSuccessUrl: ログイン完了時のリダイレクト先
  • signInOptions: 利用する認証機能
  • tosURL: 利用規約のURL(任意)
  • privacyPolicyUrl: プライバシーポリシーURL(任意)

// ログイン完了時のリダイレクト先
signInSuccessUrl: ‘index.html’,
// 利用する認証機能
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID
],
// 利用規約のURL(任意で設定)
tosUrl: ‘http://example.com/kiyaku/’,
// プライバシーポリシーのURL(任意で設定)
privacyPolicyUrl: ‘http://example.com/kiyaku/’
}

その後、SDKを初期化したあとに以下のロジックを追加します。

// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
ui.start(‘#firebaseui-auth-container’, uiconfig);

startの第一引数は画面上に表示するボタンを紐付けするHTMLエレメントを指定します。

上記の場合はfirebaseui-auth-containerのIDを持つエレメントにFirebaeUIAuthを紐付けます。

<div id=”firebaseui-auth-container”></div>

⑥ ログアウト処理

firebase.auth().signOut()メソッドを呼び出すことで、Firebase Authのユーザーセッションからログアウトすることができます。
上記はPromiseオブジェクトを返却するので、then()メソッドを使うことで、ログアウト後の処理をイベント実行することができます。

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

ログアウト後になにか処理をしたい場合は、thenメソッドの中に処理をかきます。ちなみに、ログアウトすると、onAuthStateChangedが発火するので、ログアウトした後には、未ログイン状態のonAuthStateChangedのコールバックが実行されるので、ログインボタンが表示されます。

⑦ ソースコード

今回のソースコードを以下に記載します。

<html>
    <! – The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-database.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-auth.js"></script>

    <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" />
    <script src="https://www.gstatic.com/firebasejs/ui/3.5.2/firebase-ui-auth__ja.js"></script>

    <! – TODO: Add SDKs for Firebase products that you want to use
        https://firebase.google.com/docs/web/setup#available-libraries -->
    <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-analytics.js"></script>


    <script>

        // Your web app's Firebase configuration
        var firebaseConfig = {
            <あなたの環境を設定してください>
        };

        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        //firebase.analytics();

        console.log(firebase)

        firebase.auth().onAuthStateChanged((user) => {
            if (user) {
                // User is signed in, see docs for a list of available properties
                // https://firebase.google.com/docs/reference/js/firebase.User
                var uid = user.uid;
                // ...
            } else {
                // User is signed out
                // ...
                console.log("no login")
                var uiConfig = {
                    // ログイン完了時のリダイレクト先
                    signInSuccessUrl: 'index.html',

                    // 利用する認証機能
                    signInOptions: [
                        firebase.auth.GoogleAuthProvider.PROVIDER_ID
                    ],

                    // 利用規約のURL(任意で設定)
                    tosUrl: 'http://example.com/kiyaku/',
                    // プライバシーポリシーのURL(任意で設定)
                    privacyPolicyUrl: 'http://example.com/kiyaku/'
                };

                var ui = new firebaseui.auth.AuthUI(firebase.auth());
                ui.start('#firebaseui-auth-container', uiConfig);                
            }
        });

        // データベースの更新イベントを受け取る
        firebase.database().ref('data').on("value", function(data) {
            if (data) {
                // リスト型のデータを受け取る
                var values = data.val()

                // 表示先のHTMLタグをクリアする
                var posts = document.getElementById("posts")
                posts.innerHTML = '';

                // 表示するHTMLを作成する
                var ul =  document.createElement("ul")
                for(var key in values) {
                    var li = document.createElement("li")
                    li.appendChild(document.createTextNode(values[key] + " "))

                    // ボタンの作成、キー値の埋め込み
                    var button = document.createElement("button")
                    button.dataset.key = key
                    button.appendChild(document.createTextNode("削除"))
                    button.addEventListener("click", deleteData)
                    li.appendChild(button)

                    ul.appendChild(li)
                }

                // HTMLを更新
                posts.appendChild(ul)
            }
        })

        // データ登録処理
        function writeUserData() {
            // 入力値を取得
            var element = document.getElementById("message")
            var value = element.value;

            // 入力値が存在している場合にデータベースに登録
            if(value){
                firebase.database().ref('data').push(value);
                alert("登録しました");
            }

            // 表示をクリア
            element.value = "";
        }

        // データ削除処理
        function deleteData() {
            if(this.dataset.key){
                firebase.database().ref('data').child(this.dataset.key).remove()
                alert("削除しました。");
            }
        }

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

        }

    </script>

    <h1>Hello, Firebase.</h1>
    <div id="firebaseui-auth-container"></div>

    <input id="message" type="text"></input>
    <button onclick="writeUserData()">投稿</button>
    <button onClick="signOut()">ログアウト</button>
    <div id="posts"></div>
</html>

2.Firebase Hosting

サーバー等を用意しなくても、HTMLファイルを世界に公開できる仕組みです。数コマンドですぐに成果物をリリースすることができます。

① 初期化

FirebaseのプロジェクトにHostingを適用します。カレントディレクトリがプロジェクトディレクトリ以外の場合、以下のコマンドで移動しましょう。

cd firebase_sample

その後、以下のコマンドで初期化を実行します。

firebase init hosting

以降、firebaseコマンドが質問をしてくるので、それに答える形で初期化していきます。

Please select an option: Use an existing project:すでに存在するプロジェクトに適用する

Select a default Firebase project for this directory:コンソールで作成済みのプロジェクトを選択

What do you want to use as your public directory:公開するディレクトリを選択。VueJSなど他のフレームワークを使っている場合はそれに合わせる。今回はカレントディレクトリを公開するので .

Configure as a single-page app:SPAで構成するか。(どのURLにアクセスしても、index.htmlにリダイレクトする)yesを選択

Set up automatic builds and deploys with GitHub:GitHub連携するか。今回はNo

File ./index.html already exists. Overwrite?:デフォルトのHTMLを生成する。今回は上書きされてはこまるので、No

以上で完成です。この構成は使っているフレームワークによって異なるため、適切に設定しましょう。たとえばpublic directoryは通常、publicフォルダやdistフォルダなど、ソースコードとは別の場所に配置されることが通例です。今回はソースコードがイコールアプリになるため、.(カレントディレクトリ)を指定しています。

上記の設定が終わったら、

firebase deploy

を実行します。これで、index.htmlがfirebaseのホスティングにデプロイされます。最後にデプロイ先のURLが表示されるので、アクセスしてみます。

3.まとめ

以上で、Firebaseの標準的な機能を利用して、サンプルアプリケーションを作ってみました。
他にもアプリケーションを作成するのに必要な機能がたくさん用意されています。このように、予め用意されている機能を用いることで、自分たちで作る領域を小さくして、ビジネスロジックやUIなどの体験に関わる部分に注力することができるようになると思います。

これからも、このような開発者の負担を減らすテクノロジーについて注力していきたいと思います。