Implementing OmniAuth

Posted by Eli Cusic on May 21, 2021

Ok, my last blog post was about setting up OmniAuth from Facebook, so this post will be about actually implementing it within the application.

First, we’ll set up our user resource to give us a table, model, and controller to work with. We’ll keep things pretty basic with a :name, :email, and :password_digest. We’ll also want to add a :uid, which will be the id sent from Facebook identifying a specific user. So, in the command line of our root directory, we can type in:

rails g resource User name:string email:string password_digest:string uid:string

Now that we’ve got our base user resource created, we need to add a route to our /config/routes.rb file, which will be used to handle the interaction between our application and Facebook’s. This will be the callback route that we used when we set things up on the Facebook side of things. We’ll also need to create a method in our sessions controller to handle this request. We’ll call this method create_with_omniauth, which will handle checking to see if the user is already in our database or if we need to create a new user. First, here is the route:

get '/auth/facebook/callback' => 'sessions#create_with_omniauth'

Now, for the sessions controller. Before getting into the create_with_omniauth method, let’s set up our private methods. The first will be the session_params, which is used to get the params from the browser when a request is sent. Next is an initialize_session method, which will be used to attribute the beginning of the session for the user that is signed in. And finally, we have the auth method, which will access our .env file to get the necessary info from our OmniAuth setup. Here is what our methods will look like:

private

def session_params
    params.require(:user).permit(:email, :password)
end

def initialize_session
    session[:user_id] = @user.id
end

def auth
    request.env['omniauth.auth']
end

Ok, now on to our create_with_omniauth method. There are a number of ways this method can be written, so feel free to do it the way that makes sense to you. There are a couple different ways that a user can be identified by the info sent from FB - name, email, or uid. Personally, I prefer to use the email attribute to check and see if that user already has an account with our application. If they don’t, then we create one with the info available, but if they do, then we just pull up their info. Whereas using the uid is specific to FB, and so our user may already have an account but didn’t originally sign up through FB. So, here is what that controller method ends up looking like:

def create_with_omniauth
    @user = User.find_by(email: auth['info']['email'])
    if !@user
        @user = User.create(
				  uid: auth['uid'],
          email: auth['info']['email'],
          name: auth['info']['name'],
					password: SecureRandom.hex(10)
				)
    end
    initialize_session
    redirect_to user_path(@user)
end

With our bcrypt gem, the user will be required to have a password, so I just use SecureRandom to assign a strong password. We can then redirect them to a page where they can set up their own password if they prefer. But we won’t worry about that in this blog. We just want to get a base functionality. Now all we need is a link for the user to click. So in our welcome page, we can put a button like so

<button><%= link_to 'Log in with Facebook', '/auth/facebook' %></button>

And then we can set up a user show page with

<h1>@user.name</h1>

And voila! This should render our user.