AuthlogicとOmniAuthを使って複数のアカウントと連携するで、
例えば、ここから Twitter アカウントでも認証させたくなったら。
FacebookAccount と同じ要領で追加すれば良いだけなので以下略。
とか書いたのだけれども、対応プロバイダ増やす度にモデル作るの面倒だなって思ったので、いっこのモデルで複数プロバイダ対応できるようにしたよ。
という話。
これまでのあらすじ
- 1 User に対して、複数の認証方法(FacebookやTwitterなど)でログインさせたかった
- devise を諦めて Authlogic 使うことにした
- Authlogic の add on は使わず、対応サービスも豊富な OmniAuth 使うことにした
gem の導入なんかは前回の記事でできている前提。
本題:対応プロバイダ増やすたびにモデル作るの面倒だね
各サービス毎の差異は OmniAuth がうまいことラッピングしてくれるので、単一モデルでもいける気がしたのでやってみた。
コントローラからは Account.authenticate(env['omniauth.auth'])
で呼び出す。
class User < ActiveRecord::Base
acts_as_authentic
has_many :accounts
end
class Account < ActiveRecord::Base
belongs_to :user
scope :by, ->(provider) { where(provider: provider) }
scope :by_uid, ->(provider, uid) { by(provider).where(uid: uid) }
# 認証情報からアカウントを用意する
#
# @param [AuthHash] auth
# see https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
# @return [Account] 認証したアカウント情報
# この時点ではまだsaveされていない
def self.authenticate(auth)
account = by_uid(auth.provider, auth.uid).first_or_initialize
account.attributes = {
token: auth.credentials.token,
secret: auth.credentials.secret,
nickname: auth.info.nickname,
name: auth.info.name,
image_url: auth.info.image,
profile_url: auth.info.urls.fetch(auth.provider.to_s.camelize),
}
if auth.credentials.expires_at
account.expires_at = Time.zone.at(auth.credentials.expires_at)
end
account
end
前回の記事では、このメソッド内で User.create までやっていたのだけれども、今回それはやめてた。
なので、呼び出した側で user
についての処理をよしなにした上で save
する必要がある。
これは新規登録とかログインとか連携追加とかの対応のための変更。
そもそもの目的だった「1Userにつき複数アカウント」という状態にするために必要だったんだけど、それはそれで長くなるので、今回は書かない(力尽きた)。
そうしたら、後は各プロバイダのアプリ設定さえしてしまえば、対応プロバイダは割と楽に増やしていけそうな予感。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook,
Rails.application.secrets.facebook_app_id,
Rails.application.secrets.facebook_app_secret
provider :twitter,
Rails.application.secrets.twitter_api_key,
Rails.application.secrets.twitter_api_secret,
{
image_size: 'bigger',
}
end
Omniauth がラッピングしてくれてるとはいえ、各プロバイダ毎に取得できる項目できない項目、どんな値が入って来るか、など結構ばらばらなので気を付ける必要はある。