こんにちは、id:daido1976 です。もうすぐ 30 歳になります。
Pusher の Private channels と認証周りの処理が公式ドキュメントを読んだだけでは全然わからなかったので、ライブラリの実装を追いかけてみました。
結論
実装を追っていくと公式ドキュメントに載っている以下の図の通りになっていました。(当たり前)
コード例
以下のコード例は抜粋ですが、Rails サーバとの通信を仮定しているので、ライブラリは pusher-js と pusher-http-ruby を利用する前提です。
クライアント(JavaScript)
const pusher = new Pusher("app_key", { auth: { headers: { // トークンベースで認証を行う場合、ここに何らかの認証情報を載せる // ... } } }); // User ごとの Private チャンネルを作成し、Subscribe する場合 const channel = pusher.subscribe(`private-${userId}`); // 以下イベントに bind する処理などが続く...
サーバ(Rails)
# Routing Rails.application.routes.draw do # ... post '/pusher/auth', to: 'pusher_auth#create' end
# Controller class PusherAuthController < ApplicationController def create # クライアントから渡ってきた認証情報を使ってユーザを識別するコードは省略 if current_user # Pusher のクライアント作成のコードは省略 response = pusher_client.authenticate(params[:channel_name], params[:socket_id]) render json: response else render json: {}, status: 403 end end end
ライブラリの実装を追いかけた
以下、ライブラリのコードを読む際に少しハマったので先にお伝えしておきます。
pusher-js は Web、 React Native など複数のランタイムのビルドを生成できるようになっています。(ビルド時に webpack によって依存関係の解決が切り替わるようになっている)1
プラットフォームに依存しない共通部分の実装は core/
以下に、プラットフォームに依存する実装は runtimes/
以下に置いてあるのでご留意ください。
それでは、公式ドキュメントの図に沿ってライブラリの実装を追っていきましょう。
1. WebSocket のコネクションを確立する
公式ドキュメントの図でいうと 1~3 まで。
コネクションの確立は Runtime.setup の中でやっています。(これは実装者が Pusher オブジェクトを new した時に呼ばれる)
具体的には上記の Runtime.setup
の中で Pusher.ready が呼ばれ(実装はここ)、その中で Pusher#connect が呼ばれてコネクションが確立されます。
※ 内部の処理は深かったので省略しますが、最終的には公式ドキュメントの図の通り pusher:connection_established
イベントが発火して connected な状態になります。(Web のための Runtime.setup
は こちら から始まりますので、興味のある方は追ってみてください)
2. App Server の /pusher/auth
に POST リクエストして認証トークンを取得
公式ドキュメントの図でいうと 4~6 まで。
コネクションが確立されたら Pusher#subscribe
の ここの条件分岐 に入り、*.Channel#authorize が呼ばれて App Server (コード例でいう Rails サーバ)の /pusher/auth
2に POST リクエスト3して、App Server 側では ここ で認証トークン(authentication_string
) を詰めて、 *.Channel#authorize の コールバック に返ってきます。
ちなみにチャンネル名を見て Private かどうかを調べてるのは ここ です。
※ Public channels の場合は ここ で認証をスキップしています。
3. 認証トークンをライブラリ内でセットして当該チャンネルを Subscribe するリクエストを Pusher に送る
公式ドキュメントの図でいうと 7。
認証トークンは ここ で勝手にセットされ、 pusher:subscribe
のイベントが Pusher に送られます。(最終的には ここ で Pusher にデータが送られている)
参考
- Authenticating users | Pusher docs
- GitHub - pusher/pusher-js: Pusher Javascript library | owner=@leesio
- GitHub - pusher/pusher-http-ruby: Ruby library for Pusher Channels HTTP API | owner=@annzenkina
-
https://github.com/pusher/pusher-js#core-vs-platform-specific-code↩
-
authEndpoint
は何も指定しなければ/pusher/auth
になります。(ここでセットされてる)↩ -
App Server 側に
POST /pusher/auth
してるのは次のような流れ。Private チャンネルなので、#authorize
したら PrivateChannel#authorize が呼ばれます。PusherAuthorizer#authorize の中で Runtime#getAuthorizers が呼ばれ、 この ajax 関数 が実行されます。↩