はじめに
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は英数字のハッシュとなっています。
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」にも同じものを登録します。
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を設定する」で確認できます。
参考:自分が管理する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