Google Authentication with Rails 7 and Devise Gem

In today’s fast-evolving world, saving time is essential, and Google authentication helps achieve that. With just one click, you can sign in to any website that uses Google authentication. This comprehensive article will guide you through the steps to incorporate this feature into your Ruby on Rails application.

This is a tutorial for advanced Ruby on Rails developers. We also assume that you have all the prerequisites installed. These include 
Ruby and Ruby on Rails. 

You can install Ruby using an easy all-plugins manager called ASDF, I use it to manage all my plugins and it comes in so handy trust me, It saves me the hassles and the bassles of having to use many other different managers for different plugins I am sure you may wanna try it out, check out the asdf-vm.com.

Let's kick off by creating a Ruby on Rails app, assuming you don't have one. 
rails new <app name> --database=postgresql 
Let's now add the devise gem. You can use this link or add it manually using 
bundle add device

At this point, we have all our requirements ready. Now we can start the feature we are looking at today.

Step 1. Add the required Gems


In your newly created or existing(best practice is to start this new work in a new branch, this is my way, you could do it your way) Ruby on Rails app, add the following gems in your Rails app in the Gemfile:

gem 'omniauth-google-oauth2' 
gem "omniauth-rails_csrf_protection" 

You can then install them using bundle install once more.

Step 2: Configure Devise Model

Add the following code in your devise model (I'll be using User):

# models/user.rb
:omniauthable, omniauth_providers: [:google_oauth2]

Now we need to add two fields, `uid` and `provider`, to our Devise model (in my case, the User model).

`uid` (User ID): This field stores a unique identifier for the user's account provided by the OAuth provider (in this case, Google). Each user receives a distinct `uid` from the provider, enabling your application to identify them uniquely.

`provider`: This field indicates the name of the OAuth provider used for authentication (e.g., "google_oauth2"). It helps distinguish between different authentication methods and allows your application to support multiple providers if necessary.

Now, let's generate the migration:
rails g migration AddFieldstoUser provider

Add the below code to your migration:

def change
  change_table :users, bulk: true do |t|
    t.string :provider
    t.string :uid
  end
end

then run rails db:migrate to add these two fields in your user's table.

Now, add the below code to your devise model. Mine in user.rb:

def self.from_google(u) 
  create_with(uid: u[:uid], provider: 'google', 
  password: Devise.friendly_token[0, 20]).find_or_create_by!(email: u[:email]) 
end 

Explanation
Method Definition: self.from_google(u) defines a class method named from_google that takes a single argument u. This argument is expected to be a hash containing user data from Google.

create_with: This is a method provided by ActiveRecord that allows you to create a new instance of the model with predefined attributes. In this case, it sets: uid: u[:uid]: The unique identifier from Google for the user.

  • provider: 'google': Specifies that the user was authenticated via Google.
  • password: Devise.friendly_token[0, 20]: Generates a random password (using Devise's helper) for the user, which is not needed for social authentication but is required by Devise.

find_or_create_by!:
This method attempts to find a record with the specified email in the database (the email comes from the u hash). If it does not find a match, it creates a new record with the provided attributes. The exclamation mark (!) at the end means it will raise an error if the record cannot be created.

Step 3: Configure Controller

Create a users/omniauth_callbacks_controller.rb file inside the controller's directory and paste the below code:


# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
   def google_oauth2
     user = User.from_google(from_google_params)

     if user.present?
       sign_out_all_scopes
       flash[:notice] = t 'devise.omniauth_callbacks.success', kind: 'Google'
       sign_in_and_redirect user, event: :authentication
     else
       flash[:alert] = t 'devise.omniauth_callbacks.failure', kind: 'Google',
                        reason: "#{auth.info.email} is not authorized."
       redirect_to new_user_session_path
     end
    end

    def from_google_params
      @from_google_params ||= {
        uid: auth.uid,
        email: auth.info.email
      }
    end

    def auth
      @auth ||= request.env['omniauth.auth']
    end
end


In a nutshell:
Class Definition: The Users::OmniauthCallbacksController inherits from Devise::OmniauthCallbacksController, allowing it to utilize Devise’s built-in functionality for handling OAuth callbacks.

google_oauth2 Method: This method is called when a user authenticates with Google. It attempts to create or find a user via the User.from_google(from_google_params) method, using parameters extracted from the Google auth information. If a user is found (user.present?), the following occurs:All previous sessions are signed out (sign_out_all_scopes). A success message is set in the flash (flash[:notice]). The user is signed in and redirected to their desired resource (sign_in_and_redirect). If no user is found, a failure alert is set in the flash (flash[:alert]), indicating the user's email is not authorized, and the user is redirected to the login page (redirect_to new_user_session_path).

from_google_params Method: This method constructs a hash with data obtained from Google's OAuth response. It stores the uid (unique identifier) and email of the authenticated user for further processing. The method uses memoization (@from_google_params ||=) to prevent re-evaluating the hash during the request.

auth Method: This method retrieves the authentication data from the request.env['omniauth.auth'] object, which contains information returned by Google after authentication. Similar to from_google_params, this method uses memoization to store the authentication data for later use. 


Step 4: Add Routes

 Open config/routes.rb and the routes: 

# config/routes.rb 
   devise_for :user, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
 

Step 5: Set Up a Google API Console Project 

 Visit the Google API Console and create a new project as below:

You can give any name to your project. I named it Google Authentication. After entering the name click Create. And then go into your google login project. There will be a dropdown in the navbar (i've showed it in the first picture), you can use it to change your project.

  1. Go to the OAuth consent screen
  2. Choose user type External
  3. Click Create
  4. Fill all the fields
  5. Scroll down click Save and continue.

Next follow the below steps:
  1. Select Application Type Web application
  2. Enter any name
  3. Go to your console/terminal and type the command rails routes | grep /callback. Copy the URL...
  4. Go to your browser and click on Add URI button
  5. Paste the copied url in URL field such as http://localhost:3000/user/auth/google_oauth2/callback
  6. Click Create

Once you have created the project, a popup will display your client ID and client secret. Make sure to save this information for future use.
Now that you have obtained your client ID and client secret, let's proceed with the configuration of Google login in your Rails app. 

Step 6: Implement Google Login 

Open up config/initializers/devise.rb file and paste the below code: 
config.omniauth :google_oauth2, 
                'GOOGLE_OAUTH_CLIENT_ID',
                'GOOGLE_OAUTH_CLIENT_SECRET' 

Your file should look somewhat like this: 

# config/initializers/devise.rb
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :google_oauth2, 
                'GOOGLE_OAUTH_CLIENT_ID',
                'GOOGLE_OAUTH_CLIENT_SECRET'

Now Just add the google button in your view: 
<%= button_to user_google_oauth2_omniauth_authorize_path, method: :post,
    data: {turbo: "false"},
    class: "bg-white border py-2 w-full rounded-xl mt-5 flex justify-center items-center
            text-sm hover:scale-105 duration-300 hover:bg-[#60a8bc4f] font-medium" do %>
   <svg class="mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="25px"
               height="25px">
       <path fill="#FFC107" d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,
                            8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,
                            1.154,7.961, 3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,
                            4,4,12.955,4,24c0,11.045, 8.955,20,20,20c11.045,0,20-8.955,
                            20-20C44,22.659,43.862,21.35,43.611,20.083z">
       </path>
       <path fill="#FF3D00" d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,
                               0, 5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,
                               24,4C16.318,4,9.656, 8.337,6.306,14.691z">
       </path>
       <path fill="#4CAF50" d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,
                               35.091, 26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.
                               522,5.025C9.505,39.556,16.227, 44,24,44z">
       </path>
       <path fill="#1976D2" d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,
                               4.166-4.087, 5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,
                               5.238C36.971,39.205,44,34,44,24C44, 22.659,43.862,21.35,
                               43.611,20.083z">
       </path>
  </svg>
 Continue with Google
 <% end %>

Congratulations! 🎉 You have successfully added a Google login to your Rails 7 app. Photo credits to Ahmadraza
Thank you for reading🙂 and happy coding.

Before you go you can join me and create your portfolio on this platform and start writing your articles here just click the sign-up link.