Rails
Facebook
koala

[Facebookログイン]Railsでプロフィール画像を取得したい

はじめに

Railsで開発しているAPIアプリケーションでFacebookのプロフィール画像の取得をやってみます。

環境

  • Mac OSX
  • Rails 5.2
  • ruby 2.5.1
  • Facebook Graph API v3.0

RailsからGraph APIへのアクセスはkoalaを、FacebookのAPIキーなどの管理にはfigaroを使っています。

前提

  • FacebookDevelopに登録する
  • 新規アプリを作成しAppIDSecretKeyを取得する
  • API側はknockを使ってJWTトークンを返す

準備

アクセスキーの設定

config/application.yml
FB_APP_ID: '取得したAppID'
FB_SECRET_KEY: '取得したSecretKey'

処理の流れ

  1. アプリケーションにはFacebookのユーザーアクセストークンがPOSTされる
  2. トークンの有効性を検証する
  3. トークンからGraph APIにアクセスして必要なユーザー情報を取得する
  4. 取得したユーザー情報を保存する

実装

今回はcontrollerとserviceクラスで実装しました。
色々やり方があると思いますが、以下サンプルです。

app/controllers/facebook_controller.rb
class FacebookUserTokenController < ApplicationController
  before_action :authenticate

  def create
    render json: auth_token, status: :created
  end

  private

  def authenticate
    unless entity.present?
      raise Knock.not_found_exception_class
    end
  end

  def auth_token
    if entity.respond_to? :to_token_payload
      Knock::AuthToken.new payload: entity.to_token_payload
    else
      Knock::AuthToken.new payload: { sub: entity.id }
    end
  end

  def entity
    @entity ||=
      if FacebookService.valid_token?(auth_params[:access_token])
        data = FacebookService.fetch_data(auth_params[:access_token])
        User.find_or_create_by(uid: data['id'], provider: 'facebook') do |user|
          user.nickname = data['name']
          user.first_name = data['first_name']
          user.last_name = data['last_name']
          user.email = data['email']
          user.image_url = data.dig('picture', 'data', 'url')
          user.password = SecureRandom.urlsafe_base64
        end
      end
  end

  def auth_params
    params.require(:auth).permit :access_token
  end
end
app/services/facebook_service.rb
class FacebookService
  def self.valid_token?(access_token)
    status = false
    begin
      # We need to check if the access_token is valid for our FB APP. Source: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken
      debug_token = Koala::Facebook::API.new(access_token).debug_token(app_access_token_info['access_token'])
      status = true if debug_token['data']['is_valid']
    ensure
      return status
    end
  end

  def self.fetch_data(access_token)
    Koala::Facebook::API.new(access_token).get_object('me', fields: 'name,first_name,last_name,email,picture') if valid_token?(access_token)
  end

  def self.app_access_token_info
    @app_access_token ||= Koala::Facebook::OAuth.new.get_app_access_token_info
  end
end

controllerでServiceクラスを呼び出してトークンの検証やユーザー情報の取得をする処理を呼び出しています。
保存はfind_or_create_byを使っています。
プロフィール画像は

user.image_url = data.dig('picture', 'data', 'url')

で取得しています。

ここでhttps://hogehoge.jpgとかが返ってくればこの話は終わりでした

image_urlを見てると

puts user.image_url
# https://lookaside.facebook.com/platform/profilepic/?asid=xxxxxxxxxxxxxxxxx&height=50&width=50&ext=xxxxxxx&hash=xxxxxxxxxxx

普通に.jpgとかのURLかと思ったら違いました。
このリンクにアクセスするとブラウザだとプロフィール画像がダウンロードされました。
CDNからASIDに仕様変更したみたいです。
imgタグでも表示できましたが、このURLをこのまま保存するのもなんだかなあと・・・

画像をダウンロードして保存する

アプリケーション側で画像を保存するようにしてみます。

  1. 取得したURLからダウンロードして画像ファイルを作成する
  2. S3などにアップロードしてURLを保存する

このあたりはまた続き書きます。

参考

https://damelog.com/sns/facebook/urls-based-on-app-scoped-id-disabled/
https://stackoverflow.com/questions/2515931/how-can-i-download-a-file-from-a-url-and-save-it-in-rails