Ruby
Rails
Firebase
FCM

RailsからFCM(FirebaseCloudMessaging)を使ってみる<OAuth2.0利用>

サボりがちなあら便利カレンダー2018の記事アップします。
ちょっと立て込んでて...(言い訳)

内容について

クライアントの実装はほとんど触れません。
Railsで立てたAPIサーバーから、FCMへPush通知のメッセージ登録を行う部分に関する記事です。

経緯的な

スマホアプリにPush通知を実装するのにFirebaseを使うことになった。
僕は初Firebaseだったので色々調べながらだった。

ドキュメントを見る限り、HTTPを使って通知を出すものは以下の2つが存在するみたい。

(1) FCM HTTP v1 API
(2) レガシー HTTP プロトコル

これらは認証方法やscope、POSTするjsonとかが異なる。

FCMのやってみた系記事は(2)な実装をしている内容が多かったので、せっかくならナウい(死語)ほうで実装してみようかな、ということでやってみた。

環境

  • ruby 2.5.0
  • rails 5.2.0

Gem

準備

Railsでいろいろやる前に必要なことがあるのでまずそれから。

Firebaseでプロジェクトの作成

「プロジェクトID」を控えておく。

スクリーンショット.png

「プロジェクトIDなんだったっけ・・・」な場合は、ダッシュボードから「プロジェクトの設定」を開くと確認できます。

スクリーンショット.png

アプリの登録

例としてAndroidアプリを登録してみる。

クライアント側のアプリを作るのは脳が拒否し始めたので以下を利用。
github : firebase/quickstart-android

パッケージ名を入力して「アプリの登録」を押す。

スクリーンショット.png

設定ファイルがダウンロードできるのでファイルを控えておく。

スクリーンショット 2018-05-23 16.57.54.png

これで準備は整った

ので、以下の作業後に実行してみる

  • 控えた「プロジェクトID」を、ソース中の変数url[プロジェクトID]部分に突っ込む
  • ダウンロードしたファイルをRailsのルートディレクトリに置く( app/ とかと同じ階層)
  • token にPush通知対象の端末IDを突っ込む
    • 前述したquickstart-androidをビルドして実機で動かした時に、LOG TOKENボタンを押して取得できる値が端末IDに該当する
scope = 'https://www.googleapis.com/auth/firebase.messaging'
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: File.open("#{Rails.root.to_s}/google-services.json"),
    scope: scope
)

access_token = authorizer.fetch_access_token!

url = "https://fcm.googleapis.com/v1/projects/[ プロジェクトID ]/messages:send"

request_body = {
    message: {
        token: "[ 通知を出したい端末ID ]",
        data: {
            data1: "データ1",
            data2: "データ2",
            data3: "データ3"
        },
        notification: {
            title: "タイトル",
            body: "本文"
        }
    }
}

conn = Faraday.new(url: url)
response = conn.post do |req|
    req.headers['Content-Type'] = 'application/json'
    req.headers['Authorization'] = "Bearer " + access_token["access_token"].to_s
    req.body = request_body.to_json
end

解説

認証部分

APIサーバーなどからGoogleAPIを利用するのにはサービスアカウントを使う。

今回はFirebaseプロジェクトに紐づいたサービスアカウントを用いて、APIサーバーからFCMサーバーへのアクセス許可を得ている。

ダウンロードしたjsonファイルにサービスアカウントの情報が記載されているため、jsonファイルを読み込み、その情報をscopeに投げることで、「よし!通れ!」的に有効期間が短いOAuth2.0トークンをくれる。

個人のGoogleアカウントを使ってOAuth2.0認証でよしなにする場合は、リフレッシュトークンを使ってトークンの更新を云々みたいなのがあるが、サービスアカウントを使う場合はそういうのはない。

メッセージ登録部分

urlにはFirebaseのプロジェクトIDが含まれていて、そのプロジェクトに対してメッセージを登録 -> 登録したアプリに通知出せる、みたいな感じ。

リクエストヘッダーには取得したアクセストークンを含める必要がある。
Authorization : Bearer [取得したアクセストークン] な形ですね。

request_body とかいうメッセージのオブジェクトに関してはこいつを見てくれ(つ´・ω・)つ[ドキュメント]

アプリに組み込んだ時の具体的な使い方

以下のケースを想定してた

  • グループチャットでの通知
    • ラインのグループ的なのを想定してほしい
    • 誰かが発言したら所属メンバーみんなに通知がいくやつ
  • なんかお知らせ的な通知
    • 〜が開始されました、とか、運営からのお知らせ的なのとか

想定していたFCMの使い方

メッセージオブジェクトの中にtokenとあるが、通知対象の選択肢がいくつかあり、便利そうなのがtopic

Pub/Subな処理になるんだけど、ユーザー登録時にユーザーごとに一意なトピックを作成し、ログイン時にSub開始するようにすると、スマホだろうがタブレットだろうがユーザーが利用する端末を気にすることなく通知が出せる(はず)。

また、グループチャット的な機能があったとしたら、グループごとにトピックを作成して、所属するユーザーはグループのトピックもSubしておけば通知が受け取れる(はず)。

問題点

  • トピックが作成されるまで最大24時間かかる
    • ちなみにテスト時は数時間かかった
  • スマホアプリ側で常にいくつものトピックをSubしていることによる負荷
    • 試してないからどの程度かわからない

 
「トピック使うといい感じじゃん(ウキウキ」って感じだったんですが、最大24時間かかる時点で諦めました。
スクリーンショット 2018-07-12 17.03.02.png

想定していたケースを実現しようとすると

  • お知らせの通知

    • ユーザー登録時に端末IDもユーザーに紐づけてDBに保存する
      • 複数端末で利用されるとすると、ログイン時に毎回端末IDを更新するとかしないといけない?
      • ただこれだと、複数端末で同時にアプリを開いていた場合は、最後にログインした端末にしか通知が届かない
      • そしてうまくやりたいと思ってもやれないので、そのうち僕は考えるのをやめた
  • グループチャットの通知

    • 誰かがメッセージを投げる度に、グループ所属メンバー一覧を取得し、各メンバーの端末IDを指定してメッセージ登録
    • 問題ないっちゃないが、複数端末の利用を考えると、うまくやりたいと思っても(略

補足

  • リアルタイム性みたいなのが必要ないならトピックは使っても良いと思う(トピックはあらかじめ用意しておく)

最後に

FCMというかFirebaseがめっちゃ熱いっぽいので、一通りの機能使ってみたいと思います。
「最高や!!!」ってのがあればまた覚え書きとして残そうかな、と。