Vercelに Firebase Admin SDK の秘密鍵の情報を設定する
ローカル環境での開発などではFirebaseのコンソールから作成・ダウンロードした秘密鍵(JSONファイル)をルートなどに配置して以下のように読み込んで秘密鍵を使える。。
const admin = require("firebase-admin");
if (!admin.apps.length) {
const serviceAccount = require("path/to/serviceAccountKey.json");
firebaseApp = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: process.env.FIREBASE_DATABASE_URL,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});
} else {
firebaseApp = admin.app();
}
しかしこの秘密鍵は当然公開リポジトリ内に入れられないし、プライベートのリポジトリでも危険だと感じる。そもそもGitコミットにこういった環境変数系を含めるのは気持ち悪いしやはり危険なため、GitHubへのpush経由でデプロイされるVercelには(.gitignore
に入れてコミットに含まれないようにしてるので)アップロードすることができなかった。
だからこそ環境変数というものがあり、next.jsのスターターなどにもはじめからdotenvが入っているのだが、この秘密鍵のJSONファイルをどのように環境変数に変換していいか分からなかった。
(Vercel CLI を使って秘密鍵をGitHub経由させずにアップロードしようとしたが、そんな方法はないようだったし何より上記とやってることは変わらないナンセンスで危険な方法だと思います‥。)
結論
FSA_PROJECT_ID=myproject-123
FSA_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n
FSA_CLIENT_EMAIL=firebase-adminsdk-yyy@myproject-123.iam.gserviceaccount.com
変数はこの3つだけでいいようです‥。ダブルクォーテーションやシングルクォーテーションで囲むとまた挙動が変わってくるかもしれない。またFirebase Authを使うときは追加の環境変数が必要かもしれません。
import flamelink from "flamelink/app";
import "flamelink/cf/content";
import "flamelink/cf/storage";
let firebaseApp;
if (typeof window === "undefined") {
console.log('run on server-side.')
const admin = require("firebase-admin");
if (!admin.apps.length) {
firebaseApp = admin.initializeApp({
// require()で読み込むんじゃなくて
credential: admin.credential.cert({ // cert()の中に直接JSON形式で代入
projectId: process.env.FSA_PROJECT_ID,
privateKey: process.env.FSA_PRIVATE_KEY.replace(/\\n/g, '\n'),
clientEmail: process.env.FSA_CLIENT_EMAIL,
}),
databaseURL: process.env.FIREBASE_DATABASE_URL,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
});
} else {
firebaseApp = admin.app();
}
} else {
console.log('run on client-side')
const firebase = require("firebase/app");
require("firebase/firestore");
require("firebase/storage");
if (!firebase.apps.length) {
firebaseApp = firebase.initializeApp({
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.FIREBASE_DATABASE_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
});
} else {
firebaseApp = firebase.app();
}
}
export const app = flamelink({ firebaseApp, dbType: "cf" });
ポイントはprivateKeyにおいては\n
の改行が悪さするようで.replace(/\\n/g, '\n')
を付けてやらないといけないこと。
ちなみに上記のコードはFirebaseのCMSであるFlamelinkのSDKを使っていて、かつSSRを行うNext.jsはサーバーサイドとクライアントサイドの両方で同じコードが走る関係でAdmin SDKが必要だったので、それぞれで使うSDKを分岐させる形になっています。
Vercel側に環境変数を設定
これはご存知と思いますが、localで使ってた.env
は当然gitignoreされてるので、Vercel(にデプロイするためのGitHubリポジトリ)にpushしても自動で環境変数を設定してはくれません。コンソール(もしくはVercel CLI)のSettings>Environment Variables から手動で1個ずつ設定して終わりです。
参考
AUTHに関しては以下のIssueが参考になりそう
ただし今回はこの'" "'
でprivateKeyを囲むというやり方はうまくいかなかった
Discussion