DenoでFirebaes Admin Node.js SDKを動かす(一敗)

結論

  • おおむね正常に動作するように見える
  • カスタム認証周りは動作しないことがある

何をしたかったか

Denoもそろそろ成熟してきたと聞くので、適当に触ってみようと思った。せっかくなので Hello world 的な無味乾燥なものではなく何かWebサービスを作ることにした。Webサービスの認証を自前で作るのは面倒だしセキュリティリスクも抱え込むことになるので、そこはサクッとFirebase Authenticationに任せることにした。

今回はGitHubAPIを良い感じに呼び出してゴニョゴニョするサービスを作ることにしたので、OAuth部分はDenoで自前で書いてGitHubのアクセストークンを取得しつつ、セッション管理やユーザー管理はFirebase Authenticationのカスタム認証を用いてシュッとやる作戦。

何ができたか

Firebase Admin Node.js SDK の読み込み

Denoは1.28からnpmのライブラリをインポートできるようになっている。なので、次のように書けばFirebase Admin Node.js SDKをインポートして初期化することができる。

import { initializeApp } from "npm:firebase-admin/app";
import { getAuth } from "npm:firebase-admin/auth";

initializeApp();
const auth = getAuth();

初期化時点では何のエラーも出ず、無事に完了。試しに initializeApp() の返り値をログに出力してみたが、Node.jsで動かしたときと同じように、正常に初期化できているように見える。

カスタムトークンの生成

先述の通り、今回はFirebase Authenticationのカスタム認証を使いたいので、Denoでカスタムトークンを生成する必要がある。

const customToken = await auth.createCustomToken("GitHubから取得したユーザーのUID");

これも問題無くクリア。

何ができなかったか

IDトークンの検証

Firebase Authenticationのカスタム認証では、クライアント(ブラウザ側)で取得したIDトークンをサーバー(Deno側)に送り付け、サーバでFirebase Admin Node.js SDKを用いてそのIDトークンが適切なトークンかどうか検証する必要がある。

この検証は次のように行うことができる。

const decodedToken = await auth.verifyIdToken(token);

しかし、Denoではこの処理が失敗する。エラーメッセージは次の通り。

Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.

IDトークンが不正、ということを言っているので、Node.js と Deno で時刻の扱いが違うのか(時刻が違うと iatexp に対する検証がブレるので不正扱いになることがある)と思ったが、何をどういじっても解決しない。

原因

デバッガを使って掘ってみたところ、単純にDenoがNode.jsの crypto.createPublicKey を実装していないことが原因と判明。そりゃ動かないわけだ。

Firebase Admin Node.js SDKjsonwebtoken に依存しており、 jsonwebtoken は verify.js で crypto.createPublicKey を呼び出している。

対策

Firebase Authentication のIDトークンの検証は、SDKを使わなくてもできることが公式ドキュメントに明記されている。DenoにもJWTをよしなに扱えるライブラリがあるので、これを使えば良いと思われる(が、これから試すのでシュッと動くかは不明)。