Have been recently working on a project where I was using Devise to keep user's tokens for different services. A bit different case, but still your question got me thinking for a while.
I'd bind Devise to Account model anyway. Why? Let's see.
Since my email is the only thing that can identify me as a user (and you refer to Account as the User) I would place it in accounts
table in pair with the password, so that I'm initially able do use basic email/password authentication. Also I'd keep API tokens in authentications
.
As you've mentioned, OmniAuth module needs to store provider and id. If you want your user to be able to be connected with different services at the same time (and for some reason you do) then obviously you need to keep both provider-id pairs somewhere, otherwise one will simply be overwritten each time a single user authenticates. That leads us to the Authentication model which is already suitable for that and has a reference to Account.
So when looking for a provider-id pair you want to check authentications
table and not accounts
. If one is found, you simply return an account
associated with it. If not then you check if account containing such email exists. Create new authentication
if the answer is yes, otherwise create one and then create authentication
for it.
To be more specific:
#callbacks_controller.rb
controller Callbacks < Devise::OmniauthCallbacksContoller
def omniauth_callback
auth = request.env['omniauth.auth']
authentication = Authentication.where(provider: auth.prodiver, uid: auth.uid).first
if authentication
@account = authentication.account
else
@account = Account.where(email: auth.info.email).first
if @account
@account.authentication.create(provider: auth.provider, uid: auth.uid,
token: auth.credentials[:token], secret: auth.credentials[:secret])
else
@account = Account.create(email: auth.info.email, password: Devise.friendly_token[0,20])
@account.authentication.create(provider: auth.provider, uid: auth.uid,
token: auth.credentials[:token], secret: auth.credentials[:secret])
end
end
sign_in_and_redirect @account, :event => :authentication
end
end
#authentication.rb
class Authentication < ActiveRecord::Base
attr_accessible :provider, :uid, :token, :secret, :account_id
belongs_to :account
end
#account.rb
class Account < ActiveRecord::Base
devise :database_authenticatable
attr_accessible :email, :password
has_many :authentications
end
#routes.rb
devise_for :accounts, controllers: { omniauth_callbacks: 'callbacks' }
devise_scope :accounts do
get 'auth/:provider/callback' => 'callbacks#omniauth_callback'
end
That should give you what you need while keeping the flexibility you want.