This page looks best with JavaScript enabled
⚠️

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

 ·   ·  ☕ 6 分で読めます
✏️

はじめに

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テーブルを作成

1
2
3
4
$ 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テーブルを作成するためのマイグレーションファイルが作られます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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オプションを付けています。

1
2
3
class Authentication < ActiveRecord::Base
  belongs_to :user
end
1
2
3
4
5
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って何のことだ?って思ったんですが、英語で「などなど」って意味なんですね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
1
2
3
4
5
# ここの構成は自由ですが、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に入れる場合はこのようになります。

1
2
3
4
5
6
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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の書き方に直します。

ログインボタンを追加

1
<%= link_to 'Login with Facebook', auth_at_provider_path(provider: :facebook) %>

ルーティングの追加

1
2
3
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
Share on

END
END
@aiandrox

 
目次