Techioz Blog

Rails API + React クライアントでのユーザー偽装の実装

概要

そこで、pretender、user_impersonate2、switch_user などの宝石を見つけました。それらはすべて、Devise などのシステムや「モノリス」Rails アプリの current_user を切り替えるという同様の目標を達成しているようです。

React クライアントが Rails アプリと通信しています。管理ページは Rails で直接実装され (ビューです)、クライアントは分離されています。現在、クライアントは、device-jwt 経由で access_token を提供する Devise ルートに対して POST リクエストを作成し、ブラウザのローカルストレージにトークンを保存します。

管理者がユーザーとしてログインし、管理者 (Rails) ページからクライアント (React) ページにリダイレクトできるようにするための推奨方法はありますか?できればフロントエンドのコードへの変更は最小限に抑えますが、それでも十分対応できます。

共有ルート ドメイン経由で Cookie を共有することも考えましたが、それはセキュリティ上の問題のような気がします。

Rails アプリで行われたトークンの変更をクライアント アプリに「リッスン」させる方法、またはクライアントの観点から現在のユーザーを変更する同様の方法をどのようにすればよいかわかりません。

解決策

これがオススメとまではいきませんが、私がとった基本的な考え方をお話します。

JWT を構築するときに、トークンからユーザーを識別できるようにするいくつかの重要なユーザー情報を含めます。 Device-jwt のドキュメントを見ると、最も単純な解決策は次のようなものになります。

def jwt_payload
  { uid: id }
end

次に、JWT の作成に使用したシークレットを共有できることを確認する必要があります。私は 12 要素のファンなので、このソリューションではこのようなものに環境変数を使用します。

最後に、ヘッダーから JWT を読み取り、復号化してユーザーを検索します。これは、ユーザーのデータベースを共有するか、ユーザーを複製することを前提としています。これを ApplicationController に含まれるモジュールに組み込みました。私たちのものを適応させると、上記のペイロードは次のようになります。

module JwtTokenable
  private

  def token_user
    user_id = token_payload.dig(:uid)
    # CUSTOMIZE BELOW
    User.find(user_id) if user_id
  end

  def token_payload
    @payload ||= JWT.decode(access_token, ENV.fetch('JWT_SECRET', ''), true, { algorithm: 'HS512' })
                    .first
                    .deep_symbolize_keys
  rescue JWT::DecodeError
    return {}
  end

  def access_token
    @access_token ||= authorization_header.split.last
  end

  def authorization_header
    request.headers['Authorization'] || ''
  end
end

偽装の最後のステップは、「CUSTOMIZE BELOW」コメントがある場所です。このコードは、ID でユーザーを検索するだけです。その時点で、前述した gem の 1 つを利用して、ユーザーまたはなりすまし対象者を返す必要があるかどうかを判断できる可能性があります。