OmniAuth
Rails4
Authlogic

1つのモデルで複数プロバイダに対応(AuthlogicとOmniAuthの話の続き)

More than 3 years have passed since last update.

AuthlogicとOmniAuthを使って複数のアカウントと連携するで、


例えば、ここから Twitter アカウントでも認証させたくなったら。

FacebookAccount と同じ要領で追加すれば良いだけなので以下略。


とか書いたのだけれども、対応プロバイダ増やす度にモデル作るの面倒だなって思ったので、いっこのモデルで複数プロバイダ対応できるようにしたよ。

という話。


これまでのあらすじ


  • 1 User に対して、複数の認証方法(FacebookやTwitterなど)でログインさせたかった


  • devise を諦めて Authlogic 使うことにした

  • Authlogic の add on は使わず、対応サービスも豊富な OmniAuth 使うことにした

gem の導入なんかは前回の記事でできている前提。


本題:対応プロバイダ増やすたびにモデル作るの面倒だね

各サービス毎の差異は OmniAuth がうまいことラッピングしてくれるので、単一モデルでもいける気がしたのでやってみた。

コントローラからは Account.authenticate(env['omniauth.auth']) で呼び出す。


app/models/user.rb

class User < ActiveRecord::Base

acts_as_authentic

has_many :accounts
end



app/models/account.rb

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につき複数アカウント」という状態にするために必要だったんだけど、それはそれで長くなるので、今回は書かない(力尽きた)。

そうしたら、後は各プロバイダのアプリ設定さえしてしまえば、対応プロバイダは割と楽に増やしていけそうな予感。


config/initializers/omniauth_builder.rb

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 がラッピングしてくれてるとはいえ、各プロバイダ毎に取得できる項目できない項目、どんな値が入って来るか、など結構ばらばらなので気を付ける必要はある。