JavaScript
Firebase
FirebaseDay 10

Firebase Hosting と Cloud Function を組み合わせた時APIが外から呼ばれ放題な問題とかどうしようかなと思った話

最近サーバサイドの開発から始めるのがだるいのでサーバレスでやってみようという気持ちがあり(もちろんサーバレスならではの辛さが存在することは知っている)、ちょうど良さそうな Firebase Hosting と Cloud Function でちまちま遊んでます。

目指すものはまずは会員登録とかいらないで、動的にAPIで情報を引っ張ってきて表示するだけのサービスです。

Cloud Function のURLは誰でもアクセスできる

WebアプリケーションっぽくするにはAPIから動的に情報を取ってきてそれに応じてビューを描画するのが良かろうと思ったのですが、Cloud Function の関数はURLを知っていれば誰でもアクセスできる状態になっているのが微妙でした。セキュリティグループとかぱっと調べた感じなさそう。

普通に何気なく関数を作ると、

https://自分のプロジェクト.cloudfunctions.net/helloWorld

みたいなURLが発行されて、そのURLを知っていれば誰でも実行できちゃうのは微妙ですね。外から呼ばれ放題で課金していたら料金が無駄に増える予感がします。できれば firebase hosting でホスティングしているサイトからJSを使ってアクセスしてくる場合のみ許可したい感があります。

匿名認証

そこでどうしようかと考えたのですが、firebase には匿名認証という機能があり、そこで認証されたユーザのみ許可すればいけるのではないかと思いました。

ホスティングされているサイトから以下のJSを読むようにします。

anonymous.js
firebase.auth().signInAnonymously().catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
});

//現在認証しているユーザを取得する
firebase.auth().onAuthStateChanged(function(user) {
  // tokenを取得して、ajaxで通信する
  user.getIdToken().then(function(token) {
    var request = {
      method: 'GET',
      url: './helloWorld',
      beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Bearer ' + token); }
    };

    $.ajax(request).then(function(data){
      console.log(data)
    })
  });
})

function側では以下のような感じにします。

functions.js
const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  // ここで認証されているかどうかを確認する
  if (request.headers.authorization) {
    response.send("ok");
  } else {
    response.send("non auth");
  }
});

これでサイトからのアクセスからは「ok」が返り、Curlなどでアクセスした場合は「non auth」が帰ってきます。アクセスされはするものの、処理を行わないように弾くことはできるようになりました。やりたいことはできた感じ。やったね。

とはいえ微妙感

しかしなんというかこう、アプリケーション側で頑張らないといけないのがなんともいけていない感じがします。もっといい方法があるけど、見つけられていない可能性のほうが大きい感。

あとこのままだと匿名ユーザがどんどん増えていくのが微妙です。匿名ユーザが増えっぱなしでも特に問題ないならそのままでもいいかなと思ったりするけどどうなのでしょうか。

外部のAPIとか使いたいWebアプリケーションを作る場合はどうにかしてfirebase側に匿名情報をセットしたいのでCloud functionを使わないと行けない気がするのだけど、もっといい方法がありそうなきがします。引き続き調べていきたいところです。