Having a site that allows you to authenticate using a third-party service, such as Facebook, Twitter or Google, turns out to be very handy these days. One easy way to handle the authorizations in your Rails app is with the Omniauth Gem.
Omniauth allows you to integrate one or more of these providers (also called strategies) in your app. So after reading and reviewing some Omniauth articles, I decided to put the most relevant info in this post. In it, I´ll guide you on how to implement it in a Rails App using the Github and Twitter providers.
- Add the gem according with the provider you choose:Gemfile
gem 'omniauth-github' gem 'omniauth-twitter'
- Create an application with your provider:For Github go to: http://github.com/account/applications, fill the form and click ‘Create Application’
While your are on development, you can setup the URL and Callback URL to point at your local host as follows:
Url: http://localhost:3000 Callback URL: http://localhost:3000/auth/github/callback
To create a Twitter application go to: https://dev.twitter.com/apps/new
When you create a Twitter application, it doesn’t allow you to set your url and your callback url to your localhost (due to security issues), but there are a few workarounds that you can do to test your app on your localhost:
a) Take your localhost url i.e. http://localhost:3000/auth/twitter/callback and use a shortening service like http://goo.gl/, and point your application to that url, you’ll have something like this: http://goo.gl/dH7Md
b) Set your application callback to http://127.0.0.1:3000/auth/twitter/callback
c) Use pow to run your app, and then use the url like i.e http://myapp.dev/auth/twitter/callback
Once Finished, your twitter set up should look something like this:
Remember to change URL and Callback URL when going to production.
- Once you have your providers apps all set up, create an Omniauth initializer file, you will need the Client ID and Secret keys from the Github application, and the Consumer Key and Consumer Secret from your Twitter application:../initializers/omniauth.rb
ENV['GITHUB_KEY'] ||= 'Client ID' ENV['GITHUB_SECRET'] ||= 'Secret' ENV['TWITTER_KEY'] ||= 'Consumer Key' ENV['TWITTER_SECRET'] ||= 'Consumer Secret' Rails.application.config.middleware.use OmniAuth::Builder do provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'] provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] end
- Add needed routes for your authentications:../routes.rb
match "/auth/:provider/callback" => "sessions#create" match '/auth/failure', :to => 'sessions#failure' match "/signout" => "sessions#destroy", :as => :signout
- The next thing that we are going to need, if you don’t have one yet, is to set up a controller to manage your sessions:../sessions_controller.rb
class SessionsController < ApplicationController def create if session[:id] current_user.create_provider(auth_hash) else auth = Authorization.find_or_create_by_provider(auth_hash) session[:id] = auth.user_id end redirect_to '/', :notice => "Signed in!" end def destroy session[:id] = nil redirect_to root_url, :notice => "Signed out!" end def failure redirect_to '/', :alert => 'Sorry, something went wrong. Try again.' end protected def auth_hash request.env['omniauth.auth'] end end
- Now are going to need a couple of models, a User model and Authorization model. If you dont have those models yet, is time to create them! Keep in mind that we are going to need at least the following attributes:User Model
name: string email: string
Authorization Model
provider: string uid: string
Check the link to the Hash Schema at the end of this post to see all the aviable attributes
In our User model, add the create_provider method (called in the create action of our sessions controller) along with the user data hash that we need to save:
class User < ActiveRecord::Base has_many :authorizations def create_provider(auth_hash) unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"]) Authorization.create( :user => current_user, :provider => auth_hash["provider"], :uid => auth_hash["uid"] ) end end end
In our Authorization model, we need the find_or_create_by_provider method:
class Authorization < ActiveRecord::Base belongs_to :user validates :provider, :uid, presence: true def self.find_or_create_by_provider(auth_hash) unless auth = find_by_provider_and_uid(auth_hash['provider'], auth_hash['uid']) user = User.create(name: auth_hash['info']['name'], email: auth_hash['info']['email']) auth = create user:( user, provider: auth_hash['provider'], uid: auth_hash['uid'] ) end auth end end
- We are going to need a few methods in our application controller, define three helper_methods, one to find the current user by its session id and one method that redirects to the root path if the user hasn’t been authenticated yet. Also we need to add a before_filter that checks the callback url when our app is in production to avoid callback errors.
class ApplicationController < ActionController::Base before_filter :check_uri helper_method :current_user, :authenticate_user! def check_uri url = redirect_to request.protocol + 'www.' + request.host_with_port + request.fullpath if Rails.env.to_s == 'production' && !/^www/.match(request.host) && ENV['DEV'].nil? end def current_user @current_user ||= User.find_by_id(session[:id]) end def authenticate_user! if session[:id] current_user else redirect_to '/' end end end
- At last, but not least, configure the view where you want the login links, it might be something like this:If the user is not logged in
%h2 Welcome to the Omniauth Example... Login with your preferred provider! = link_to image_tag('github.png'), '/auth/github' = link_to image_tag('twitter.png'), '/auth/twitter'
if the user is logged in
%p You are logged in as #{current_user.name} = link_to 'Logout', '/signout'
Conclusions
As you can see, implementing Omniauth is pretty straight foward. I hope you found this post helpful. You can also take a look at the example app in Github
Usefull Links: