public
Last active

Setup for Devise + Omniauth

  • Download Gist
01. Gemfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14
gem 'pg'
group :development do
gem 'ruby-debug'
end
gem 'rake', '~> 0.8.7'
gem 'devise'
gem 'oa-oauth', :require => 'omniauth/oauth'
gem 'omniauth'
gem 'haml'
gem 'dynamic_form'
gem 'sass'
gem 'jquery-rails'
gem 'cancan'
gem 'uuidtools'
02. Generate migration and add authorization model
1 2 3 4
rails generate devise:install
rails generate devise user
rails g migration add_name_to_users name:string
rails g model authorization provider:string uid:string user_id:integer token:string secret:string name:string link:string
_devise_create_users.rb
Ruby
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
# /db/migrate/<timestamp>_devise_create_users.rb
 
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
t.encryptable
t.confirmable
t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
t.token_authenticatable
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
add_index :users, :confirmation_token, :unique => true
add_index :users, :unlock_token, :unique => true
add_index :users, :authentication_token, :unique => true
end
 
def self.down
drop_table :users
end
end
authorization.rb
Ruby
1 2 3 4 5 6 7
# app/models/authorization.rb
 
class Authorization < ActiveRecord::Base
belongs_to :user
end
development.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/environments
 
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
HOST = 'localhost:3000'
ADMIN_EMAIL_FROM = ""
PONY_VIA_OPTIONS = {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => '',
:password => '',
:authentication => :plain,
:domain => "localhost.localdomain" }
devise.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11
# app/config/initializers/devise.rb
 
Devise.setup do |config|
...
config.sign_out_via = :get
...
config.omniauth :facebook, "KEY", "SECRET"
config.omniauth :twitter, "KEY", "SECRET"
config.omniauth :linked_in, "KEY", "SECRET"
...
end
omniauth_callbacks_controller.rb
Ruby
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
require 'uuidtools'
def facebook
oauthorize "Facebook"
end
def twitter
oauthorize "Twitter"
end
def linked_in
oauthorize "LinkedIn"
end
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
private
 
def oauthorize(kind)
@user = find_for_ouath(kind, env["omniauth.auth"], current_user)
if @user
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => kind
session["devise.#{kind.downcase}_data"] = env["omniauth.auth"]
sign_in_and_redirect @user, :event => :authentication
end
end
 
def find_for_ouath(provider, access_token, resource=nil)
user, email, name, uid, auth_attr = nil, nil, nil, {}
case provider
when "Facebook"
uid = access_token['uid']
email = access_token['extra']['user_hash']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'], :secret => nil, :name => access_token['extra']['user_hash']['name'], :link => access_token['extra']['user_hash']['link'] }
when "Twitter"
uid = access_token['extra']['user_hash']['id']
name = access_token['user_info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'], :secret => access_token['credentials']['secret'], :name => name, :link => "http://twitter.com/#{name}" }
when 'LinkedIn'
uid = access_token['uid']
name = access_token['user_info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'], :secret => access_token['credentials']['secret'], :name => name, :link => access_token['user_info']['public_profile_url'] }
else
raise 'Provider #{provider} not handled'
end
if resource.nil?
if email
user = find_for_oauth_by_email(email, resource)
elsif uid && name
user = find_for_oauth_by_uid(uid, resource)
if user.nil?
user = find_for_oauth_by_name(name, resource)
end
end
else
user = resource
end
auth = user.authorizations.find_by_provider(provider)
if auth.nil?
auth = user.authorizations.build(:provider => provider)
user.authorizations << auth
end
auth.update_attributes auth_attr
return user
end
 
def find_for_oauth_by_uid(uid, resource=nil)
user = nil
if auth = Authorization.find_by_uid(uid.to_s)
user = auth.user
end
return user
end
 
def find_for_oauth_by_email(email, resource=nil)
if user = User.find_by_email(email)
user
else
user = User.new(:email => email, :password => Devise.friendly_token[0,20])
user.save
end
return user
end
def find_for_oauth_by_name(name, resource=nil)
if user = User.find_by_name(name)
user
else
user = User.new(:name => name, :password => Devise.friendly_token[0,20], :email => "#{UUIDTools::UUID.random_create}@host")
user.save false
end
return user
end
 
end
registrations_controller.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
class RegistrationsController < Devise::RegistrationsController
 
def update
if params[resource_name][:password].blank?
params[resource_name].delete(:password)
params[resource_name].delete(:password_confirmation) if params[resource_name][:password_confirmation].blank?
end
# Override Devise to use update_attributes instead of update_with_password.
# This is the only change we make.
if resource.update_attributes(params[resource_name])
set_flash_message :notice, :updated
# Line below required if using Devise >= 1.2.0
sign_in resource_name, resource, :bypass => true
redirect_to after_update_path_for(resource)
else
clean_up_passwords(resource)
render_with_scope :edit
end
end
end
routes.rb
Ruby
1
devise_for :users, :path => "accounts", :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", :registrations => "registrations" }
user.rb
Ruby
1 2 3 4 5 6 7 8 9
# app/models/user.rb
 
class User < ActiveRecord::Base
 
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :omniauthable
attr_accessible :email, :name, :password, :password_confirmation, :remember_me
has_many :authorizations, :dependent => :destroy
 
end

I searched long and hard for a good multi-provider example of Devise with Omniauth (using Devise's omniauthable). Great to have finally found one.!!

Glad to hear it!

On Aug 9, 2011, at 6:33 AM, seanaedmistonreply@reply.github.com wrote:

I searched long and hard for a good multi-provider example of Devise with Omniauth (using Devise's omniauthable). Great to have finally found one.!!

Reply to this email directly or view it on GitHub:
https://gist.github.com/993566

If you try this code in rails 3.1 you need to change file omniauth_callbacks_controller.rb line 95 from

user.save false

to

user.save :validate => false

Very awesome. Quick tip - as of now you'll need to specify the omniauth gem version explicitly as the 1.0 is incompatible with devise at the moment.

gem 'omniauth', '0.3.2'

See https://github.com/intridea/omniauth/issues/496 for more info.

Also FWIW :lockable is in the migration but not on the User model.

This is for omniauth with version < 1.0. For those of you who wants to use new omniauth 1.0 with devise, follow strategies on

https://github.com/intridea/omniauth/wiki/List-of-Strategies

Line 27: session["devise.#{kind.downcase}_data"] = env["omniauth.auth"]
Line 28: sign_in_and_redirect @user, :event => :authentication

Line 27 should go after Line 28, because Devise resets the session when the user is signed in.

The migration provided here won't work on Devise 2.0+. See https://github.com/plataformatec/devise/wiki/How-To:-Upgrade-to-Devise-2.0-migration-schema-style for details on how to upgrade. I think this tut might be a little outdated from the get-go.

user.rb:5 needs also to have :omniauth_providers => [:facebook, :twitter, :gplus] and etc. in the devise line in the model.

Do you really want rememberable and database_authenticatable? do those make sense to use with omniauth?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.