Advertisement

How to Use Omniauth to Authenticate your Users

by

I hate signing up for websites. I’ve already signed up for so many, using different usernames, that going back to one of them and trying to remember my credentials is sometimes impossible. These days, most sites have begun offering alternative ways to sign up, by allowing you to use your Facebook, Twitter or even your Google account. Creating such an integration sometimes feels like a long and arduous task. But fear not, Omniauth is here to help.

Omniauth allows you to easily integrate more than sixty authentication providers, including Facebook, Google, Twitter and GitHub. In this tutorial, I’m going to explain how to integrate these authentication providers into your app.


Step 1: Preparing your Application

Let’s create a new Rails application and add the necessary gems. I’m going to assume you’ve already installed Ruby and Ruby on Rails 3.1 using RubyGems.

rails new omniauth-tutorial

Now open your Gemfile and reference the omniauth gem.

gem 'omniauth'

Next, per usual, run the bundle install command to install the gem.


Step 2: Creating a Provider

In order to add a provider to Omniauth, you will need to sign up as a developer on the provider’s site. Once you’ve signed up, you’ll be given two strings (sort of like a username and a password), that needs to be passed on to Omniauth. If you’re using an OpenID provider, then all you need is the OpenID URL.

If you want to use Facebook authentication, head over to developers.facebook.com/apps and click on “Create New App”.

Facebook New App

Fill in all necessary information, and once finished, copy your App’s ID and Secret.

Facebook Secret

Configuring Twitter is a bit more complicated on a development machine, since they don’t allow you to use “localhost” as a domain for callbacks. Configuring your development environment for this kind of thing is outside of the scope of this tutorial, however, I recommend you use Pow if you’re on a Mac.


Step 3: Add your Providers to the App

Create a new file under config/initializers called omniauth.rb. We’re going to configure our authentication providers through this file.

Paste the following code into the file we created earlier:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook, YOUR_APP_ID, YOUR_APP_SECRET
end

This is honestly all the configuration you need to get this going. The rest is taken care of by Omniauth, as we’re going to find in the next step.


Step 4: Creating the Login Page

Let’s create our sessions controller. Run the following code in your terminal to create a new sessions controller, and the new, create, and failure actions.

rails generate controller sessions new create failure

Next, open your config/routes.rb file and add this:

get   '/login', :to => 'sessions#new', :as => :login
match '/auth/:provider/callback', :to => 'sessions#create'
match '/auth/failure', :to => 'sessions#failure'

Let’s break this down:

  • The first line is used to create a simple login form where the user will see a simple “Connect with Facebook” link.
  • The second line is to catch the provider’s callback. After a user authorizes your app, the provider redirects the user to this url, so we can make use of their data.
  • The last one will be used when there’s a problem, or if the user didn’t authorize our application.

Make sure you delete the routes that were created automatically when you ran the rails generate command. They aren't necessary for our little project.

Open your app/controllers/sessions_controller.rb file and write the create method, like so:

def create
  auth_hash = request.env['omniauth.auth']

  render :text => auth_hash.inspect
end

This is used to make sure everything is working. Point your browser to localhost:3000/auth/facebook and you’ll be redirected to Facebook so you can authorize your app (pretty cool huh?). Authorize it, and you will be redirected back to your app and see a hash with some information. In between will be your name, your Facebook user id, and your email, among other things.


Step 5: Creating the User Model

The next step is to create a user model so users may sign up using their Facebook accounts. In the Rails console (rails console), create the new model.

rails generate model User name:string email:string

For now, our user model will only have a name and an email. With that out of the way, we need a way to recognize the user the next time they log in. Keep in mind that we don’t have any fields on our user’s model for this purpose.

The idea behind an application like the one we are trying to build is that a user can choose between using Facebook or Twitter (or any other provider) to sign up, so we need another model to store that information. Let’s create it:

rails generate model Authorization provider:string uid:string user_id:integer

A user will have one or more authorizations, and when someone tries to login using a provider, we simply look at the authorizations within the database and look for one which matches the uid and provider fields. This way, we also enable users to have many providers, so they can later login using Facebook, or Twitter, or any other provider they have configured!

Add the following code to your app/models/user.rb file:

has_many :authorizations
validates :name, :email, :presence => true

This specifies that a user may have multiple authorizations, and that the name and email fields in the database are required.

Next, to your app/models/authorization.rb file, add:

belongs_to :user
validates :provider, :uid, :presence => true

Within this model, we designate that each authorization is bound to a specific user. We also set some validation as well.


Step 6: Adding a Bit of Logic to our Sessions Controller

Let’s add some code to our sessions controller so that it logs a user in or signs them up, depending on the case. Open app/controllers/sessions_controller.rb and modify the create method, like so:

def create
  auth_hash = request.env['omniauth.auth']

  @authorization = Authorization.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
  if @authorization
    render :text => "Welcome back #{@authorization.user.name}! You have already signed up."
  else
    user = User.new :name => auth_hash["user_info"]["name"], :email => auth_hash["user_info"]["email"]
    user.authorizations.build :provider => auth_hash["provider"], :uid => auth_hash["uid"]
    user.save

    render :text => "Hi #{user.name}! You've signed up."
  end
end

This code clearly needs some refactoring, but we’ll deal with that later. Let’s review it first:

  • We check whether an authorization exists for that provider and that uid. If one exists, we welcome our user back.
  • If no authorization exists, we sign the user up. We create a new user with the name and email that the provider (Facebook in this case) gives us, and we associate an authorization with the provider and the uid we’re given.

Give it a test! Go to localhost:3000/auth/facebook and you should see “You’ve signed up”. If you refresh the page, you should now see “Welcome back”.


Step 7: Enabling Multiple Providers

The ideal scenario would be to allow a user to sign up using one provider, and later add another provider so he can have multiple options to login with. Our app doesn’t allow that for now. We need to refactor our code a bit. Change your sessions_controlller.rb’s create method to look like this:

def create
  auth_hash = request.env['omniauth.auth']

  if session[:user_id]
    # Means our user is signed in. Add the authorization to the user
    User.find(session[:user_id]).add_provider(auth_hash)

    render :text => "You can now login using #{auth_hash["provider"].capitalize} too!"
  else
    # Log him in or sign him up
    auth = Authorization.find_or_create(auth_hash)

    # Create the session
    session[:user_id] = auth.user.id

    render :text => "Welcome #{auth.user.name}!"
  end
end

Let’s review this:

  • If the user is already logged in, we’re going to add the provider they’re using to their account.
  • If they’re not logged in, we’re going to try and find a user with that provider, or create a new one if it’s necessary.

In order for the above code to work, we need to add some methods to our User and Authorization models. Open user.rb and add the following method:

def add_provider(auth_hash)
  # Check if the provider already exists, so we don't add it twice
  unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
    Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"]
  end
end

If the user doesn’t already have this provider associated with their account, we'll go ahead and add it -- simple. Now, add this method to your authorization.rb file:

def self.find_or_create(auth_hash)
  unless auth = find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
    user = User.create :name => auth_hash["user_info"]["name"], :email => auth_hash["user_info"]["email"]
    auth = create :user => user, :provider => auth_hash["provider"], :uid => auth_hash["uid"]
  end

  auth
end

In the code above, we attempt to find an authorization that matches the request, and if unsuccessful, we create a new user.

If you want to try this out locally, you’ll need a second authentication provider. You could use Twitter’s OAuth system, but, as I pointed out before, you’re going to need to use a different approach, since Twitter doesn’t allow using “localhost” as the callback URL’s domain (at least it doesn’t work for me). You could also try hosting your code on Heroku, which is perfect for a simple site like the one we’re creating.


Step 8: Some Extra Tweaks

Lastly, we need to, of course, allow users to log out. Add this piece of code to your sessions controller:

def destroy
  session[:user_id] = nil
  render :text => "You've logged out!"
end

We also need to create the applicable route (in routes.rb).

get '/logout', :to => 'sessions#destroy'

It's as simple as that! If you browse to localhost:3000/logout, your session should be cleared, and you'll be logged out. This will make it easier to try multiple accounts and providers. We also need to add a message that displays when users deny access to our app. If you remember, we added this route near the beginning of the tutorial. Now, we only need to add the method in the sessions controller:

def failure
  render :text => "Sorry, but you didn't allow access to our app!"
end

And last but not least, create the login page, where the user can click on the “Connect With Facebook” link. Open app/views/sessions/new.html.erb and add:

<%= link_to "Connect With Facebook", "/auth/facebook" %>

If you go to localhost:3000/login you’ll see a link that will redirect you to the Facebook authentication page.


Conclusion

I hope this article has provided you with a brief example of how Omniauth works. It’s a considerably powerful gem, and allows you to create websites that don’t require users to sign up, which is always a plus! You can learn about Omniauth on GitHub.

Let me us know if you have any questions!