Facebook Authentication with Devise

The source code for this tutorial can be found here.

While there are many tutorials teaching how to use the Facebook OAuth strategy for a Rails application, I found myself having to piece together old code snippets and multiple pieces of scattered documentation to complete it in a recent project. I decided to put together my own step-by-step guide to setting up a fresh Rails project using Devise for user management while allowing users authenticate through Facebook if they so choose.

Setting Up Your Rails App

This is pretty standard.

$ rails new devise-facebook-oauth --database=postgresql
$ cd rails devise-facebook-oauth/ && rake db:create

Ta-da, we’ve got a Rails app.

Setting Up Devise

Now, you need to add Devise to your Gemfile.

# User Authentication
gem 'devise'

And install it.

$ bundle install
$ rails g devise:install

You’ll see a post-install message, we’ll cover each of these.

The first step after install is to configure your default_url_options in your config/environments/development.rb file. Add the following line to that file:

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

Before configuring your root path, we’re going to skip to creating the user. The reason for this is because we’re going to set up different root paths depending on if the user is logged in or not.

$ rails g devise user
$ rake db:migrate

Now we’re going to set up our root paths.

$ rails g controller home index

We’re going to replace the entirety of our config/routes file to contain the following:

Rails.application.routes.draw do
	devise_for :users

	authenticated :user do
		root 'home#index', as: 'authenticated_root'
	end
	devise_scope :user do
		root 'devise/sessions#new'
	end
end

This sets the index action for the controller we just generated as the root path for a signed in user and sets up the log in page as root path for an unauthenticated user.

During sign-in, sign-up, forgot-password, sign-out, etc. there will be notices and alerts generated. We want to communicate those with our user. We also want to provide links for the user to navigate our Devise pages. Add the following to your app/views/layouts/application.html.erb:

<% if user_signed_in? %>
	<%= link_to "Edit profile", edit_user_registration_path %> |
	<%= link_to "Logout", destroy_user_session_path, method: :delete  %>
<% else %>
	<%= link_to "Sign up", new_user_registration_path  %> |
	<%= link_to "Login", new_user_session_path  %>
<% end %>

<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

We also want to generate the Devise views since we’ll likely want to tweak each of the pages based on our application’s specific needs.

$ rails g devise:views

The final step is to authenticate every request coming in. Add the following to your app/controllers/application_controller.rb after the protect_from_forgery with: :exception:

before_action :authenticate_user!

At this point you should have Devise completely configured with basic functionality.

Setting Up Your Facebook App

Before we write any code that integrates Facebook with our authentication process, we need to create a Facebook App. Head on over to the Facebook Developers Portal and hover on “My Apps” in the top right corner and select “Add New App” in the dropdown menu.

Enter in the “Display Name” and a “Contact Email” for Facebook to reach you at with updates about your app. Then click on “Create App ID”.

Create New Facebook App

Now select “Get Started” for the “Facebook Login” option under “Product Setup”.

Get Started

We aren’t going to use the Javascript SDK for this application so we aren’t going to use the “Quickstart” walkthrough that Facebook provides. Instead we’re going to navigate to “Settings” under “Facebook Login” in the sidebar.

Here we’ll be able to add whitelisted OAuth redirect URIs. We’ll want to add http://localhost:3000/users/auth/facebook to that list and then click “Save Changes”.

OAuth Redirect URIs

Next we’ll navigate to the “Dashboard” in the sidebar. Here we’ll have access to the “App ID” and the “App Secret”. Take note of these, because we’ll need them in the next step.

Create New Facebook App

Configuring Facebook Login

We’re going to add a couple of gems to our Gemfile.

# User Authentication with Facebook
gem 'omniauth-facebook'

# Development ENV Variables
gem 'figaro'

Figaro helps us store sensitive information in a file that isn’t checked into Git. A perfect example of sensitive information is our Facebook App ID and Facebook App Secret. We’ll run the following.

$ bundle install
$ figaro install

This creates a file at config/application.yml to store our variables. We want our’s to look like this:

development:
	FACEBOOK_APP_ID: "YOUR_FACEBOOK_APP_ID"
	FACEBOOK_APP_SECRET: "YOUR_FACEBOOK_APP_SECRET"

Now we’re ready to start integrating the Facebook App we created in the previous step with our application. In our config/initializers/devise.rb file we’ll want to add the following line:

config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], scope: 'email'

If we wanted to access more information from the user’s Facebook profile we may do something like this:

config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], scope: 'name,email,user_birthday'

Now we want to setup a callback controller that will handle the information Facebook sends us after the user approves our app. First define it in our routes. We’ll replace our devise_for line with this:

devise_for :users, controllers: { omniauth_callbacks: 'omniauth_callbacks' }

Now generate that controller.

$ rails g controller users/omniauth_callbacks

We’ll need to change the controller that our generated controller inherits from and add a facebook method to that controller.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
	def facebook
		@user = User.from_omniauth(request.env["omniauth.auth"])
		sign_in_and_redirect @user
	end
end

Before implementing the from_omniauth method on the User method, we’ll need to add a couple of fields to the users table.

$ rails g migration add_oauth_fields_to_users provider:string uid:string
$ rake db:migrate

Now we need to add that method to our User model.

def self.from_omniauth(auth)
	where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
		user.email = auth.info.email
		user.provider = auth.provider
		user.uid = auth.uid
		user.password = Devise.friendly_token[0,20]
	end
end

We also need to update the devise modules list at the top of this file.

devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:facebook]

At this point you can restart your server and navigate to your application. You should see a “Sign in with Facebook” link has automatically been added to your unauthenticated views. If you did everything correctly, you should be signed in!

Moving Forward

This is in no way a complete solution. There are more scenarios to account for such as letting a user link their Facebook account after signing up through the traditional Devise method of email and password, linking other social accounts, and accessing more information from the users Facebook profile. Stay tuned for tutorials on how to address these issues and more!

The source code for this tutorial can be found here.

This is Part 1 of the Facebook Authentication with Devise series. For Part 2, click here.

Need help with a project? We'd love to hop on board. Hire Us.

Subscribe to our email newsletter to receive development tips, tricks, and tutorials, as well as company updates.

Interests (Optional)

Get In
Touch

Must be a valid email address.