こんにちは。freee APIチームでエンジニアをしているkotegawaです。
去年のAdvent CalendarではAPIのスキーマ駆動開発についての記事を書いて、APIの基盤を整える話をしました。 おかげさまでfreeeの公開APIも当時からendpoint数が40個以上増え、今では freee アプリストア というfreee APIを使ったアプリのプラットフォームに60以上のアプリが並ぶほどの盛況ぶりになっています。
今回はそんなfreeeのAPIを使ってAPIチームがFirebaseを使って自らアプリを作ってみた話をしてみます。
この記事はfreee Developers Advent Calendar 2019の21日目の記事です。
会計のWebアプリを作った背景
そもそも本体のアプリを作ろうとした背景は2つあります。
1. freeeのAPIを使った業務アプリのユースケースを提供したい
freeeのAPIは10月にも31種類のendpointが追加されるなど、かなりのペースで増えています。 現状のfreee APIには「勘定科目を更新できるAPI」「取引の支払行作るAPI」「税区分の一覧を取得できるAPI」など、渋いラインナップが揃っていますが、 一方で会計という業務ドメインの複雑な性質上、リソースベースで多くのRESTfulなAPIを提供していても、「具体的にどんなアプリを作ればいいのかわからない」「それぞれのAPIの使いみちがわからない」というフィードバックをもらうことがよくありました。
そんなことなら、自分たちでfreee APIを使った業務アプリを開発して価値提供しつつ、そのアプリをサンプルユースケースとしてアプリストアに公開しようというのがアプリを作ろうという判断になった直接的な理由です。
2. モノリシックになりつつある本体リポジトリとは切り離して、素早く仮説検証したい
freeeは今や、個人事業主から上場企業の方々まで、幅広いユーザーに使って頂いています。 幅広いユーザーに価値を届けられているというのは大変誇らしい一方で、すべてのユーザーに同じインターフェースや仕様で満足していただくのは日々難しくなっているのも現実です。
たとえば前受/前払金の管理や外貨を使った取引登録・試算管理など、事業所の規模や業種によって運用もまちまちです。 これらの機能を実装するには今までfreeeにはなかったデータの概念を持ち込む必要がありますが、リリースから7年近くが経過している会計freeeのリポジトリで、データ構造を変更したりアプリケーションロジックを大きく変更しようとすると、大きなコストがかかってしまいます。
そこで、クイックに仮説検証サイクルを回してより多くのユーザーにハマるUI/UX・仕様を探る手段としても、本体のプロダクトとは完全に疎にアプリを作るのは合理性があるという判断になりました。
何故Firebaseを選択したか
社内のプロダクトは基本的にAWS上で作られていますが、今回スモール業務アプリをクイックに作成する上で「freee本体とは疎に作る」という前提があったため、技術選定に制約がない中、Firebase/GCPの利用を選択しました。
その最も大きな理由としては、Webアプリ構築コストの低さと認証機能の充実度にあります。
Webアプリ構築コストの低さ
FirebaseでのWebアプリはサーバーレスで低コストで構築できます。(下記構成図参照)
それぞれの構成要素をかんたんに解説すると、
- Hosting・・・静的ファイル(HTML, Javascript, CSS)をデプロイ・ホストできる。カスタムドメインの設定も可能
- Cloud Functions・・・サーバーレスで動作するfunction(今回はnode.jsで書きました)
- Cloud Firestore・・・NoSQLのデータベース(ネストできるKVSのイメージ)。DynamoDBなど他のNoSQL DBと同じように、やはり多少の慣れは必要
- Cloud Storage・・・高耐久性、高可用性のオブジェクトストレージ
FirebaseのSDKやリファレンスはかなり充実しているのでこれらの構成要素の繋ぎこみやデプロイもローコストで実現できました。
サーバーを立ててミドルウェアの設定を書いてネットワークの設定をして・・・という従来私が経験していたフルスクラッチのWebアプリ開発と比較して、かなりコストが低くなったのを感じました。
充実した認証機能
Firebaseの特徴の一つとして、認証機能が充実している点が挙げられます。
その中でも今回は、「カスタム認証」という、Firebaseの認証サーバーをから特定のタイミングでログイン用のトークンを発行できる機能を使いました。
freeeでは現時点ではOpenId Connectのような認証基盤を持っていないため、freeeと疎にアプリを作ろうとすると、通常アプリ用のID/Passwordを持つか何か他のログインプロバイダを利用する必要があります。
ただこのカスタム認証を使えば、アプリの見かけ上、「freeeアカウントを使ってアプリにログイン」という動きを実現することができます。
この認証の仕組みをざっくりと解説すると、以下のようになります。
- アプリにやってきたユーザーが未ログインなら、freeeの認可画面にリダイレクトさせる
- 認可後、freeeのアクセストークンの取得が確認できたら、認証用のCloud FunctionsのURLにリダイレクトさせる
- Cloud FunctionsでFirebaseのカスタムトークンを取得し、そのトークンが有効であればログイン中のセッションとみなす
かんたんに図にまとめてみました。
このカスタム認証を利用するために、ユーザーはわざわざアプリ用のID/Passwordを準備しなくても済むようになり、アプリ側でもログイン情報の管理が不要になります。
カスタム認証に関しては、LINEなどを例にしたサンプルコードも公開されているので、大いに参考になりました。
運用の課題
credentialな情報はFirebase上でどう管理するか?
freee APIを利用しているアプリの特性上、freeeのAPIをコールする上でアクセストークン、リフレッシュトークンの利用が必須です。
これらの情報はアプリ上で永続化しておかないと、APIコールのたびにユーザーに認可してもらわないといけなくなってしまいます。とはいえ、アクセストークン、リフレッシュトークンをそのままFirebaseに保存しておくわけにはいかないので、暗号化には少し工夫が必要でした。
多くのアクセス数があまり想定されないアプリであれば、 Cloud KMS(Key Management Service)を使って暗号鍵の管理をするという手段もありますが、$0.03/10,000 オペレーションという従量課金制を見て少し足がすくみ、他の手段を考えることにしました。
Cloud KMSの代替案
今回採用した手段は、このようなものです。
- 暗号化・復号化の鍵をCloud Storageに保管し、そのStorageには特定のCloud Functionsしかアクセスできないようにする
- 暗号化のkeyはGoogle Cloud Scheduler(≒定期実行できるCloud Functions)で定期的にKey Rotateする
このあたりのロジックは、後述するサンプルアプリのリポジトリにも反映させているので、興味があれば参考にしてみてください。
GCPプロジェクトではユーザーに対してもCloud Functionsなどの機能に対してもService Accountという役割を紐付けて特定の権限を付与したり外したりできるので、そのへんをうまくコントロールしていくのがポイントになります。
デプロイの運用
FirebaseのCLIはかなり充実していて、ログインからプロジェクトの作成・選択、デプロイまでワンコマンドで実行可能です。
特にデプロイに関してはhostingとCloud Functionsを同時にデプロイできるなど、projectの構成要素を「1アプリ」としてまとめて本番にアップロードすることができるので、開発をしていくうえではとても便利です。
一方で、本番環境のアプリも誰でも手元のCLIからデプロイができてしまう状況を作ってしまうのはリスキーなので、CIからしかデプロイができないようにするといった仕組み作りが必要になります。
firebase SDK for freeeとサンプルアプリを作りました
後日正式なリリース告知がありますが、firebase SDK for freeeとそのSDKを使ったサンプルアプリをβ版としてリリースしました。
Firebase SDK for freeeにはカスタム認証を用いたログイン実装やfreee APIをコールするwrapperが含まれており、そのロジックを用いてサンプルアプリを実装しています。 Firebaseやfreee APIに興味がある方がいれば、是非使ってみてください。
明日はバリバリの攻撃者の発想でfreeeのセキュリティを守っている @liva さんによる脆弱性診断と管理の話です。お楽しみに!