はじめに
NuxtやNextJsでFirebase Adminを利用したアプリを作成し、Vercel上で実行しようとした。
ところがVercelにはファイルをアップロードする手段がない、一般的なFirebaseの認証方法である、サービスアカウントのJsonファイル利用した認証が利用できなかった。
漏洩リスクなどを考慮したところ、こちらのサイトの暗号化して環境変数に設定するの手順が良さそうだったので、こちらの方法の実装パターンを試してみた。
実装
1. サービスアカウントJsonの暗号化
はじめにFirebaseコンソール画面の「プロジェクトの設定>サービスアカウント>新しい秘密鍵の生成」より、秘密鍵を生成する。
暗号化にはopenssl
コマンドを利用する。先ほどダウンロードしたJsonファイルの名称をservice-account-key.json
として、暗号化を下記のコマンドで実施する。
$ openssl enc -aes-256-cbc -a -in service-account-key.json -out encrypted.txt -k passphrase -p
salt=xxxxxxxxxxxxxxxx
key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
iv=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
その後、出力された key と iv、 encrypted.txt
の内容を、それぞれ .env に設定、またはVercelに環境変数として設定する。
.env
DECRYPT_KEY=${keyの値}
DECRYPT_IV=${ivの値}
ENCRYPTED_KEY=${encrypted.txtの中身}
2. 復号処理
openssl
コマンドで暗号化した各種情報を利用し、サービスアカウントキーを復号化する処理を作成する。
decrypt.ts
import * as crypto from "crypto";
import * as fs from "fs";
/**
* 暗号化キーを複合化する処理
*/
export function decryptGCPServiceAccount() {
const algorithm = "aes-256-cbc";
// 環境変数から読み込む
const key = process.env.DECRYPT_KEY!;
const iv = process.env.DECRYPT_IV!;
const source = process.env.ENCRYPTED_KEY!;
const decipher = crypto.createDecipheriv(
algorithm,
Buffer.from(key, "hex"),
Buffer.from(iv, "hex")
);
const data = Buffer.from(source, "base64").slice(16);
const start = decipher.update(data);
const final = decipher.final();
const result = Buffer.concat([start, final]).toString("utf8");
// 複合化されたサービスアカウントのJson
return JSON.parse(result);
}
3. Firebase Adminの認証処理
2で実装した復号化処理を用いてfirebase-admin
の認証を行う。
Fire StoreとFire Storageを利用する場合の例を下記に示す。
firebase.ts
import { initializeApp, cert } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { getStorage } from "firebase-admin/storage";
import { decryptGCPServiceAccount } from "./decrypt";
// ServiceAccountKeyを複合化する
const serviceAccountJson = decryptGCPServiceAccount();
const app = initializeApp({
// 文字列をJson形式に変換し初期化する。
credential: cert(serviceAccountJson),
// FireStorageのバケット名
storageBucket: "${FIRE_STORAGE_BUCKET_NAME}",
});
// FireStoreとFriestoreStoregeのバケットを取得する。
export const firestore = getFirestore(app);
export const bucket = getStorage(app).bucket();
参考
- メールアドレスとパスワード認証は、現在のバージョンでは利用できない様子
- 最新の記事からもJsonの値をそのまま環境変数に設定する方法しかなさそう。
- 暗号化した値を貼り付けるこちらのパターンを採用