Help us understand the problem. What is going on with this article?

【Rails】Sorceryでfacebook認証 Sorceryの設定

はじめに

mkcertとSorceryの拡張機能を用いて、ローカル環境でfacebook認証機能を追加しました。
Sorceryのwikiが少し古かったり、facebook for developersとの連携に苦戦したので、備忘録がてら書いておこうと思います。
また、設定に不備があった場合のエラーについては別記事で書いています。

動作環境

ruby 2.6.5
Rails 5.2.3
sorcery 0.14.0
mkcert 1.4.1

前提

  • sorceryによるログイン機能
  • mkcertでのHTTPS通信を許可
  • facebook for developersで「facebookログイン」の作成

は済ませていることとします。

簡単な流れ

Sorceryのwikiにチュートリアルがあるので、基本的にはそれに従っていきます。

authenticationsテーブルを作成

$ rails g sorcery:install external --only-submodules
        gsub  config/initializers/sorcery.rb
      insert  app/models/user.rb
      create  db/migrate/20200203110653_sorcery_external.rb

コマンドを打つと、外部認証に必要なauthenticationsテーブルを作成するためのマイグレーションファイルが作られます。

db/migrate/20200203110653_sorcery_external.rb
class SorceryExternal < ActiveRecord::Migration
  def change
    create_table :authentications do |t|
      t.integer :user_id, :null => false
      t.string :provider, :uid, :null => false

      t.timestamps
    end

    add_index :authentications, [:provider, :uid]
    add_index :authentications, :user_id # user_idにindexを貼る場合は追加
  end
end
$ bundle exec rails db:migrate

Authenticationモデルの設定

$ rails g model Authentication --migration=false

先ほど既にマイグレーションファイルは作成しているので、--migration=falseオプションを付けています。

app/models/authentication.rb
class Authentication < ActiveRecord::Base
  belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
  authenticates_with_sorcery!
  has_many :authentications, dependent: :destroy
  accepts_nested_attributes_for :authentications # has_many :authenticationsより下に書く
end

ここまではfacebook認証に限らず、外部認証共通の処理となります。

sorcery.rbの設定

sorcery.rbの以下の部分のコメントアウトを外します。
blablaって何のことだ?って思ったんですが、英語で「などなど」って意味なんですね。

config/initializers/sorcery.rb
Rails.application.config.sorcery.submodules = [:external, blabla, blablu, ...] # コマンドを打つと自動的に追加される

Rails.application.config.sorcery.configure do |config|
  ...
  config.external_providers = [:facebook, blabla, ...]  # 外部認証に使用するもののみ追加
  # 例 [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack, :line]
  ...
  config.facebook.key = Rails.application.credentials.dig(:sorcery, :facebook, :key) # 1.で解説
  config.facebook.secret = Rails.application.credentials.dig(:sorcery, :facebook, :secret) # 1.で解説
  config.facebook.callback_url = 'https://localhost:3000/oauth/callback?provider=facebook' # 2.で解説
  config.facebook.user_info_path = 'me?fields=email' # 3.で解説
  config.facebook.user_info_mapping = { email: 'email' } # 3.で解説
  config.facebook.access_permissions = %w[email] # 3.で解説
  config.facebook.display = 'page'
  config.facebook.api_version = 'v6.0' # 4.で解説
  config.facebook.parse = :json
  ...

  # --- user config ---
  config.user_config do |user|
  ...
    # -- external --
    user.authentications_class = Authentication # 認証用のモデルがAuthenticationではない場合は変更する
    ...
  end
  ...
end

config.facebookに関する最初の3行は、環境によって値を変更することになると思いますが、ここでは省略します。Configなどを使ってください。

1. facebook keyとsecret

facebook keyとsecretは外部には漏らしたくないため、credentialsで管理します。
参考:Rails5.2から追加された credentials.yml.enc のキホン

$ rails credentials:edit
credentials.yml
# ここの構成は自由ですが、Rails.application.credentials.dig(:sorcery, :facebook, :key)のように対応するようにしてください。
sorcery:
  facebook:
    key: 'アプリIDの値'
    secret: 'app secretの値'

facebook for developersからマイアプリにアクセスし、「ダッシュボード」下の「設定」→「ベーシック」の画面から「アプリID」と「app secret」を確認し、記述してください。
IDは15桁の数字、secretは英数字のハッシュとなっています。
スクリーンショット 2020-02-05 21.49.51.png

2. callback_url

HTTPS化をしているため、デフォルトの"http://0.0.0.0:3000/oauth/callback?provider=facebook"ではエラーが出ます。
SSL証明書の発行されているhttps://localhost:3000/oauth/callback?provider=facebookをここに記述します。
そして、「Facebookログイン」「設定」の「有効なOAuth リダイレクトURI」にも同じものを登録します。
スクリーンショット 2020-02-05 20.19.23.png

3. facefookから取得するデータ

デフォルトでfacebookから取得できるデータは以下の通りです。

id
first_name(名)
last_name(姓)
middle_name(ミドルネーム)
name(フルネーム)
name_format(デフォルトは{last}{first}
picture(プロフィール画像)
short_name(設定されていない場合はフルネーム)
アクセス許可のリファレンス

それ以外のデータを取得したい場合は、config.facebook.access_permissionsに記述します。
emailを取得するためにはfacebook側の手続きは要りませんが、それ以外にはアプリレビューが必要です。

Email、姓名、プロフィール画像を取得してUserに入れる場合はこのようになります。

config.facebook.user_info_path = 'me?fields=email,first_name,last_name,picture.type(large)'
# facebookから取得するデータの受け取り方
config.facebook.user_info_mapping = { email: 'email', first_name: 'first_name', last_name: 'last_name', remote_avatar_url: 'picture/data/url' }
# facebook側の属性名とUserモデルの属性を対応させる
config.facebook.access_permissions = %w[email]
# デフォルト以外のデータを取得する場合はここに書く

プロフィール画像の取得方法は他と少し違うので注意が必要です。
参考:Sorcery + CarrierWave で Facebook 認証時に大きめのアイコン画像を保存する

4. APIのバージョン

facebook APIのバージョンは「Facebookログイン」の「クイックスタート」から「ウェブ」「2. JavaScript用Facebook SDKを設定する」で確認できます。
スクリーンショット 2020-02-05 21.59.06.png

参考:自分が管理するFacebookアプリのAPIバージョン確認方法
少しレイアウトが変わっていますが、こちらの方法でも確認できます。

Oauth処理を行うコントローラを作成

$ rails g controller Oauths oauth callback
app/controllers/oauths_controller.rb
class OauthsController < ApplicationController
  skip_before_action :require_login # applications_controllerでbefore_action :require_loginを設定している場合

  def oauth
    login_at(auth_params[:provider])
  end

  def callback
    provider = auth_params[:provider]
    if (@user = login_from(provider))
      redirect_to root_path, notice: "#{provider.titleize}でログインしました"
    else
      begin
        @user = create_from(provider)
        reset_session
        auto_login(@user)
        redirect_to root_path, notice: "#{provider.titleize}でログインしました"
      rescue StandardError
        redirect_to root_path, alert: "#{provider.titleize}でのログインに失敗しました"
      end
    end
  end

  private

  def auth_params
    params.permit(:code, :provider)
  end
end

基本的にはチュートリアルの通りです。多少、Rails5の書き方に直します。

ログインボタンを追加

login_page.html.erb
<%= link_to 'Login with Facebook', auth_at_provider_path(provider: :facebook) %>

ルーティングの追加

config/routes.rb
post "oauth/callback", to: "oauths#callback"
get "oauth/callback", to: "oauths#callback"
get "oauth/:provider", to: "oauths#oauth", as: :auth_at_provider
          oauth_callback POST   /oauth/callback(.:format)                                                                oauths#callback
                          GET    /oauth/callback(.:format)                                                                oauths#callback
         auth_at_provider GET    /oauth/:provider(.:format)                                                               oauths#oauth

おわりに

ご指摘などがございましたら、コメントか編集リクエストでお願いいたします。

aiandrox
過去と未来の自分のために書く。タイポがお友達
https://blog.aiandrox.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away