概要
Cloud Functions for Firebase を用いることで、ヘッダの値をもとにFirebase hostingへのアクセスを制限してみます。
前提
動作環境は、Microsoft Windows 10 Homeです。
Node.jsはインストールしてある前提です。(v12.14.1)
準備
Firebase CLIインストール
npm install -g firebase-tools
でFirebase CLIをインストールします。
Firebase hostingプロジェクトを作成
firebase login
でローカルマシンをFirebaseに接続させます。
firebase projects:list
でCLI が正しくインストールされていて、アカウントにアクセスしていることをテストします。
C:\Users\nanna>firebase projects:list
√ Preparing the list of your Firebase projects
┌──────────────────────┬───────────────────┬────────────────┬──────────────────────┐
│ Project Display Name │ Project ID │ Project Number │ Resource Location ID │
├──────────────────────┼───────────────────┼────────────────┼──────────────────────┤
│ angular-try │ angular-try-8c667 │ 422372883147 │ us-central │
└──────────────────────┴───────────────────┴────────────────┴──────────────────────┘
1 project(s) total.
firebase init
でFirebaseプロジェクトを追加します。
C:\Users\nanna\firebase-try>firebase init
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
C:\Users\nanna\firebase-try
? Are you ready to proceed? Yes
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. Hosting: Configure and deploy Firebase Hosting sites
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: Create a new project
i If you want to create a project in a Google Cloud organization or folder, please use "firebase projects:create" instead, and return to this command when you've created the project.
? Please specify a unique project id (warning: cannot be modified afterward) [6-30 characters]:
apikey-control
? What would you like to call your project? (defaults to your project ID)
√ Creating Google Cloud Platform project
√ Adding Firebase resources to Google Cloud Platform project
=== Your Firebase project is ready! ===
Project information:
- Project ID: apikey-control
- Project Name: apikey-control
Firebase console is available at
https://console.firebase.google.com/project/apikey-control/overview
i Using project apikey-control (apikey-control)
=== Hosting Setup
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.
? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? No
+ Wrote public/404.html
+ Wrote public/index.html
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
i Writing gitignore file to .gitignore...
+ Firebase initialization complete!
コマンド実施すると、Firebaseコンソールにプロジェクトが追加されます。
また、以下のようにローカルにファイルが生成されます。
C:\USERS\NANNA\FIREBASE-TRY
│ .firebaserc
│ .gitignore
│ firebase.json
│
└─public
404.html
index.html
Cloud Functions を初期化
firebase init functions
でCloud Functions を初期化します。
C:\Users\nanna\firebase-try>firebase init functions
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
C:\Users\nanna\firebase-try
Before we get started, keep in mind:
* You are initializing in an existing Firebase project directory
? Are you ready to proceed? Yes
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
i .firebaserc already has a default project, using apikey-control.
=== Functions Setup
A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.
? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? No
+ Wrote functions/package.json
+ Wrote functions/index.js
+ Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes
> protobufjs@6.9.0 postinstall C:\Users\nanna\firebase-try\functions\node_modules\protobufjs
> node scripts/postinstall
npm notice created a lockfile as package-lock.json. You should commit this file.
added 258 packages from 206 contributors and audited 258 packages in 10.445s
30 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
+ Firebase initialization complete!
成功すると、新たにfunctions
ディレクトリが生成されます。
C:.
│ .firebaserc
│ .gitignore
│ firebase.json
│
├─functions
│ .gitignore
│ index.js
│ package-lock.json
│ package.json
│
└─public
404.html
index.html
取得するコンテンツを配置
今回、コンテンツ取得を目的とした通信が正当なものではない場合、ステータスコード400で400.html
を返すようにします。
また、コンテンツとして、file-A.html
とfile-B.html
を配置しておきます。
これらをCloud Functionsから見れるように、以下のように配置します。(クラウド上にデプロイするときには、Firebase Hostingの資材とCloud Functionsの資材は別々のところに行く。functions
配下(Cloud Functions)とpublic
配下(Firebase Hosting)は別々の場所にデプロイされるイメージ)
C:.
│ .firebaserc
│ .gitignore
│ firebase.json
│
├─functions
│ │ .gitignore
│ │ index.js
│ │ package-lock.json
│ │ package.json
│ │
│ └─public
│ 400.html
│ 404.html
│ file-A.html
│ file-B.html
│
└─public
404.html
index.html
使わない資材を削除
今回は、Firebase Hostingは実質ドメイン取得だけのために使う構成にしました。
レスポンスは、Cloud Functions側でfunctions/public
配下の資材をもとに作成するので、Firebase Hosting側のpublic
配下の資材は使わないので削除します。
その結果、下記のような構成になります。
C:.
│ .firebaserc
│ .gitignore
│ firebase.json
│
├─functions
│ │ .gitignore
│ │ index.js
│ │ package-lock.json
│ │ package.json
│ │
│ └─public
│ 400.html
│ 404.html
│ file-A.html
│ file-B.html
│
└─public
動作するように編集
functions/index.js
functions/index.js
を以下のように編集し、コンテンツ取得リクエストのパスとapikey
ヘッダが想定と符合するものであればステータスコード200でコンテンツファイルを返すようにします。
また、この機能をfilter
と名付けています。
const functions = require('firebase-functions');
const fs = require('fs');
const path = require('path');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.filter = functions.https.onRequest((request, response) => {
const requestPath = request.path;
const matchFile = data.find(d => d.path === requestPath);
let statusCode;
let fileName;
if (typeof matchFile === "undefined") {
statusCode = 404;
fileName = "404";
} else {
if (matchFile.apikey.some(k => k === request.headers.apikey)) {
statusCode = 200;
fileName = requestPath;
} else {
statusCode = 400;
fileName = "400";
}
}
let fileContent;
fileContent = fs.readFileSync(path.resolve('public') + '/' + fileName + '.html');
const html = fileContent.toString();
response.status(statusCode).send(html);
});
const data = [
{
path: "/file-A",
apikey: [
"apikey1",
"apikey2",
"apikey3"
]
},
{
path: "/file-B",
apikey: [
"apikey3"
]
}
]
firebase.json
firebase.json
を以下のように編集し、該当のドメイン配下に来た通信には、filter
を通すようにします。
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "filter"
}
]
}
}
結果
下記のページにデプロイしています。
https://apikey-control.web.app/
https://apikey-control.web.app/file-A
への通信のヘッダにapikey: apikey1
orapikey: apikey2
orapikey: apikey3
を付ければ、晴れてfile-Aが見れます。
https://apikey-control.web.app/file-B
へは、apikey: apikey3
のみ許可しています。
Talend API TesterやBurp Suiteなどを使って、apikeyを付与しながら確かめることができると思います。
Talend API Testerが一番手軽かもしれません。
その他
firebase.json のrewritesについて
firebase.json
の下記の部分で、すべてのリクエストをfilter
にかけることができると思っていたのですが、ルート直下?(今回だと、https://apikey-control.web.app/
)のアクセスについて、public/index.html
がある場合は、まずindex.html
にリダイレクトに行くような動作になるようでした。
https://stackoverflow.com/questions/44871075/redirect-firebase-hosting-root-to-a-cloud-function-is-not-working
"rewrites": [
{
"source": "**",
"function": "filter"
}
]
firebase serve の挙動が不可解な時がある
firebase serveを実施することで、ローカルにて挙動を確認できるのですが、私のローカルでhttps://apikey-control.web.app/
にアクセスすると、https://apikey-control.web.app/404
にリダイレクトされるという挙動になりました。
原因は不明なのですが、deploy後のクラウド環境では再現しなかったです。
参考
https://github.com/nannany/firebase-cloud-functions
https://firebase.google.com/docs/hosting/functions
https://qiita.com/otakky/items/1363e7b4c706dc9cd096