Skip to content

Instantly share code, notes, and snippets.

@seanaedmiston
Created August 21, 2011 10:53
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save seanaedmiston/1160463 to your computer and use it in GitHub Desktop.
Save seanaedmiston/1160463 to your computer and use it in GitHub Desktop.
Devise Omniauthable
...
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
config.authentication_keys = [ :login ]
...
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
#config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
config.omniauth :twitter, 'yourid', 'yoursecret', {:client_options => {:ssl => {:ca_path => '/etc/ssl/certs'}}}
if Rails.env == "production"
config.omniauth :facebook, 'yourid', 'yoursecret', {:client_options => {:ssl => {:ca_path => '/etc/ssl/certs'}}}
else
config.omniauth :facebook, 'yourid', 'yoursecret', {:client_options => {:ssl => {:ca_path => '/etc/ssl/certs'}}}
end
...
class User < ActiveRecord::Base
...
has_many :authentications, :dependent => :destroy
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
#:confirmable, :lockable
# Allow people to log in with username (from oauth provider) or email
attr_accessor :login
# Setup accessible (or protected) attributes for your model
attr_accessible :login, :email, :name, :password, :password_confirmation, :remember_me
def password_required?
(authentications.empty? || !password.blank?) && super
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"]
user.email = data["email"]
end
end
end
#Override Devise's update with password to allow registration edits without password entry
def update_with_password(params={})
params.delete(:current_password)
self.update_without_password(params)
end
# Update user record and create or update authentication record
def set_token_from_hash(auth_hash, user_hash)
self.update_attribute(:name, user_hash[:name]) if self.name.blank?
self.update_attribute(:email, user_hash[:email]) if self.email.blank?
token = self.authentications.find_or_initialize_by_provider_and_uid(auth_hash[:provider], auth_hash[:uid])
token.update_attributes(
:name => auth_hash[:name],
:link => auth_hash[:link],
:token => auth_hash[:token],
:secret => auth_hash[:secret]
)
end
protected
# From Devise docs to allow name or email as login
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(name) = :value OR lower(email) = :value", { :value => login.downcase }]).first
end
end
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
oauthorize "Facebook"
end
def twitter
oauthorize "Twitter"
end
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
private
def oauthorize(kind)
omniauth = request.env['omniauth.auth']
@user = User.includes(:authentications).merge(Authentication.where(:provider => omniauth['provider'], :uid => omniauth['uid'])).first
if @user # if user exists and has used this authentication before, update details and sign in
@user.set_token_from_hash(provider_auth_hash(kind, omniauth), provider_user_hash(kind, omniauth))
sign_in_and_redirect @user, :event => :authentication
elsif current_user # if user exists then new authentication is being added - so update details and redirect to
current_user.set_token_from_hash(provider_auth_hash(kind, omniauth), provider_user_hash(kind, omniauth))
redirect_to edit_user_registration_url
else # create new user and new authentication
user = User.new
user.password = Devise.friendly_token[0,20]
user.authentications.build(provider_auth_hash(kind, omniauth))
if user.save :validate => false # validate false handles cases where email not provided - such as Twitter
sign_in_and_redirect(:user, user)
else # validate false above makes it almost impossible to get here
session["devise.#{kind.downcase}_data"] = provider_auth_hash(kind,omniauth).merge(provider_user_hash(kind,omniauth))
redirect_to new_user_registration_url
end
end
end
def provider_auth_hash(provider, hash)
# Create provider specific hash's to populate authentication record
case provider
when "Facebook"
{
:provider => hash['provider'],
:uid => hash['uid'],
:name => hash['extra']['user_hash']['name'],
:link => hash['extra']['user_hash']['link'],
:token => hash['credentials']['token'],
:secret => nil
}
when "Twitter"
{
:provider => hash['provider'],
:uid => hash['uid'],
:name => hash['user_info']['nickname'],
:link => hash['user_info']['urls']['Twitter'],
:token => hash['credentials']['token'],
:secret => hash['credentials']['secret']
}
end
end
def provider_user_hash(provider, hash)
# Create provider specific hash's to populate user record if appropriate
case provider
when "Facebook"
{
:name => hash['extra']['user_hash']['name'],
:email => hash['extra']['user_hash']['email']
}
when "Twitter"
{
:name => hash['user_info']['name']
}
end
end
end
@deevis
Copy link

deevis commented May 11, 2014

Nice gist, but what are the :authentications referenced via has_many in user.rb?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment