RubyでだってFirebase Admin SDKは作れる！！

# はじめに

この記事は、[Hamee Advent Calendar 2019](https://qiita.com/advent-calendar/2019/hamee)の15日目のエントリです。
最近はRailsの傍らTerraformをごにょごにょしています。Terraformなんもわからん。

# 最初に結論

タイトルは言い過ぎました。正確には
**RubyでもFirebase AuthenticationからAdmin SDKのようにユーザーの取得や更新はできる！！**
（最後にUserManagementの一部機能のサンプルコードあり）

※今回は一部機能しか実装していませんが、がんばればAdmin SDKは作れます。

# Ruby+Firebase使ってます

みなさんFirebase使ってますか？

私はここ一年くらいで初めて、とあるプロジェクトでFirebaseを使ってみて、いまさらながらあまりの便利さと機能の豊富さに震えました。認証もプッシュ通知もFaaSもデータベースもなんでもあるやん・・・すごい（語彙力）:open_mouth:

こんなすばらしいプロダクトを利用できることに感謝しながら、Railsなサーバーと連携する形でFirebaseを使っていたんですが、あるとき機能改修でuidを使ってRubyからFirebase Authenticationにいるユーザー情報を取得したいケースがでてきました。

# Gemがない

さて実装しようと思ったら、ないんですよ。Firebase公式のAdminSDKにRuby実装が。まあ、Rubyのエコシステムなら3rd PartyのGemくらいあるやろーと思って探してみたところ、あれ・・・ない？

[firebase-ruby](https://github.com/oscardelben/firebase-ruby)というGemはあるんですが、[Firebase Database REST API](https://firebase.google.com/docs/reference/rest/database)のラッパーになっていて、Authenticationへのアクセス機能は持っていない。[firebase-auth](https://github.com/hungdh0x5e/firebase-auth)といういかにもな名前のGemも、idToken（Firebaseの認証トークン）を使えばユーザー情報の取得はできるのですが、uidを使ってユーザー情報が取得できないので今回のやりたいことに合わず断念。

# 諦めかけていたそのとき・・・

不本意だけど、あまり時間もかけていられないし公式のAdminSDKがあるnodeを間に立てて、Rails -> node -> Firebaseという構成でアクセスする実装を進めていました。

ちょうどアドカレネタを探していたところだったのもあって、諦めきれずに調べていたら、[How to access account info of firebase auth?](https://github.com/googleapis/google-api-ruby-client/issues/782)といういかにもそれっぽいissueを発見。issue内で取得できたよーって書いてあるじゃないですか！！！

結果、[google-api-client](https://github.com/googleapis/google-api-ruby-client)を使えばRubyからでもAdminSDK相当のことができそうなことがわかり、実際に試してみたところ無事にuidでユーザーが取得できました。プロジェクトにもフィードバックして無駄にnodeを経由することを回避できたのでした。

:muscle::muscle::muscle::muscle::muscle:アドカレ駆動開発:muscle::muscle::muscle::muscle::muscle:

前置きが長くなりましたが、以下手順とサンプルコードです。

# 前準備

1. [サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja)を参考に、サービスアカウント用の秘密鍵ファイルを生成・ダウンロードしておく
1. [google-api-client](https://github.com/googleapis/google-api-ruby-client) Gemをインストールしておく

# サンプルコード

```ruby
require 'google/apis/identitytoolkit_v3'

# 前準備の1で用意したjsonファイルのパス
service_account_key_json = "/path/to/your/serviceAccountKey.json"

# Googleの認証用サービスの初期化
service = Google::Apis::IdentitytoolkitV3::IdentityToolkitService.new

# 認証用クレデンシャルの生成
service.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open(service_account_key_json),
  scope: [
    # 認可が必要なスコープを列挙する。今回はidentitytoolkitのみ
    # ここにFirebaseの各種サービスを指定することでUserManagement以外も利用可能
    'https://www.googleapis.com/auth/identitytoolkit',
  ].join(' ')
)

# 取得したいFirebaseのUIDを定義（予めFirebaseUIDはRuby側で取得してある想定）
uid = 'target firebase uid'

# 認証サービスへのリクエストの生成（uidでのユーザー情報の取得）
# ※引数が配列な点に注意（複数uidを渡せるが今回は1つとしている）
request = Google::Apis::IdentitytoolkitV3::GetAccountInfoRequest.new(local_id: [uid])

# リクエスト実行
account = service.get_account_info(request)

# ユーザーの取得
firebase_user = account.users[0]

# メールアドレス
firebase_user.email
```

FirebaseのGemで探していたから見つからなかっただけで、FirebaseもGoogleのサービスの一つなんだからGoogleのGemという切り口で探してみたところたどり着けたのでした。視点の切り替え大事。

あと、諦めなくてよかった:raised_hands:

# ちなみに更新もできます

uid以外にも電話番号やメールアドレスでのユーザー取得や更新もできます。

```ruby:Gemfile
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'google-api-client'
```

```ruby:firebase_admin/auth/client.rb
require 'google/apis/identitytoolkit_v3'

module FirebaseAdmin
  module Auth
    class Client
      def initialize(service_account_key_json_path)
        @service = Google::Apis::IdentitytoolkitV3::IdentityToolkitService.new
        @service.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
          json_key_io: File.open(service_account_key_json_path),
          scope: [
            'https://www.googleapis.com/auth/identitytoolkit',
          ].join(' ')
        )
      end

      def get_user(uid:)
        get_account_info(local_id: [uid])&.users&.first
      end

      def update_user(uid:, params:)
        update_params = { local_id: uid }.merge(params)
        request = Google::Apis::IdentitytoolkitV3::SetAccountInfoRequest.new(update_params)
        @service.set_account_info(request)
      end

      def get_account_info(params)
        request = Google::Apis::IdentitytoolkitV3::GetAccountInfoRequest.new(params)
        @service.get_account_info(request)
      end
    end
  end
end
```
こんなファイルを用意しておいて

```ruby
require_relative 'firebase_admin/auth/client'
service_account_key_json_path = '/path/to/serviceAccountKey.json'

client = FirebaseAdmin::Auth::Client.new(service_account_key_json_path)

# メールアドレスでの検索
account = client.get_account_info(email: [検索したいメールアドレス])
# 電話番号での検索
account = client.get_account_info(phone_number: [検索したい電話番号])

# メールアドレスの更新
uid = '更新対象のUID'
client.update_user(uid: uid, params: { email: 'update.email@example.com' })
```

とすればUID以外での検索や更新なども可能です。

Firebase Authenticationに認証は任せているが、Railsからメールは送りたいといった場合でも問題なく実現できます。Rubyだからって諦めなくていいんです。

# まとめ

~~素直に公式のAdminSDK実装がある言語でFirebaseを使おう~~
RubyからでもFirebaseAdminSDK相当の機能は実現できる:tada:

今回紹介したのはUserManagementの一部だけでしたが、scopeの指定をすれば他の機能も実現できそうなので、時間を見つけて実装してみようと思います。

# 参考リンク

* [サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja)
* [How to access account info of firebase auth?](https://github.com/googleapis/google-api-ruby-client/issues/782)
* [google-api-client](https://github.com/googleapis/google-api-ruby-client)
* [Unofficial Firebase Admin SDK for PHP](https://github.com/kreait/firebase-php)