Omniauth with Ruby on Rails, Devise, and Twitter
In short, omniauth lets your users log in to your website through a third party like Twitter or Facebook. In this post, I’ll show you how to set up Omniauth with Ruby on Rails, Devise, and Twitter.
Prerequisites:
- You have Devise already installed with User model on a Rails app
- You have a Twitter account
Step 1: Add omniauth-twitter to Gemfile and run bundle
Gemfile
gem 'omniauth-twitter'
$ bundle install
Step 2: Migrations
$ rails g migration AddColumnsToUsers provider uid twitter_handle $ rake db:migrate
Step 3: Omniauth Callbacks Controller
You can either create this file or use the $ rails g devise:controllers
command to generate it.
/app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def all user = User.from_omniauth(request.env["omniauth.auth"]) if user.persisted? flash.notice = "Signed in!" sign_in_and_redirect user else session["devise.user_attributes"] = user.attributes redirect_to new_user_registration_url end end alias_method :twitter, :all end
Step 4: Users Model
/app/models/user.rb
class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable devise :omniauthable, omniauth_providers: [:twitter] def self.from_omniauth(auth) where(provider: auth.provider, uid: auth.uid).first_or_create do |user| user.twitter_handle = auth.info.nickname user.email = auth.info.email user.password = Devise.friendly_token[0,20] end end def self.new_with_session(params, session) if session["devise.user_attributes"] new(session["devise.user_attributes"], without_protection: true) do |user| user.attributes = params user.valid? end else super end end def password_required? super && provider.blank? end def email_required? super && provider.blank? end def update_with_password(params, *options) if encrypted_password.blank? update_attributes(params, *options) else super end end end
Step 5: Create App on apps.twitter.com
- Go to apps.twitter.com
- Click “Create New App”
- Fill in the required fields
- Also fill in the callback URL. This is very important as the log in will not work if you leave this blank. If you don’t have a domain yet, you can use localhost:3000
http://photocritic.herokuapp.com/users/auth/twitter
or
http://localhost:3000/users/auth/twitter
Step 6: API ID and Secret
Add the newly created API ID and API Secret to devise.rb. config/initializers/devise.rb
config.omniauth :twitter, 'API_ID', 'API_SECRET'
Step 7: Edit the routes file
config/routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
Step 8: Sign Up and Log In Links with Logged In Indicator
app/views/layouts/application.html.erb
<ul> <% if user_signed_in? %> <li><%= link_to "Settings", edit_user_registration_path, :data => {:bypass => true} %></li> <li><%= link_to "Sign Out", destroy_user_session_path, :method => :delete, :data => {:bypass => true} %></li> <% else %> <li><%= link_to "Login", new_user_session_path %></li> <li><%= link_to "Sign in with Twitter", user_omniauth_authorize_path(:twitter) %></li> <li><%= link_to "Sign Up", new_registration_path(:user) %></li> <% end %> </ul> <% if user_signed_in? %> <% if current_user.provider == "twitter" %> <p class="text-right">Signed in through twitter as <%= current_user.twitter_handle %></p> <% else %> <p class="text-right">Signed in as <%= current_user.email %></p> <% end %> <% end %>
Step 9: Change User Table to Accept Null Values for Email Column
We need to do this because Twitter doesn’t send an email back which will cause a database error when creating new users.
$ rails g migration ChangeUserEmailToNullTrue
class ChangeUsersEmailToNullTrue < ActiveRecord::Migration def change change_column_null :users, :email, true end end
$ rake db:migrate
Conclusion
So what exactly does all that code do? Here is a quick rundown of the omniauth process.
- User clicks Sign in with Twitter
- User gets redirected to Twitter log in page
- User enters credentials
- If valid, Twitter sends a callback to the URL we specified earlier with a payload containing user info (including the Twitter handle)
- In the self.omni_auth method, we find or create the user
- There is additional logic in the User model to check if omniauth provider is nil. If so, it means that the user is signing up through our system and we require a password. Otherwise, skip the validation if there is a provider.
That’s it.