Integrating Google OAuth2 with Devise for a Ruby on Rails Application
Tags: rails, ruby, google, oauth
Published: 2023-05-21 05:08:31

**As a Ruby on Rails developer, there often comes a time when you're required to implement authentication mechanisms in your application. Devise is one of the most popular authentication solutions within the Rails ecosystem. When combined with Google OAuth2, it enables an effortless sign-in experience for the users. In this article, I'm going to walk you through a step-by-step guide on how to set up Google OAuth2 with Devise on a Rails application, also incorporating exception handling to make your app robust and reliable.**

## Step 1: Setting up Google API

Our journey begins with creating a new project in the Google API Console. After creating the project, we'll need to enable the Google+ API (an essential part of the Google OAuth2 authentication process). Post this; you can create OAuth client ID credentials for your web application. Remember to add your callback URL as an Authorized redirect URI. For development, this usually takes the form `http://localhost:3000/users/auth/google_oauth2/callback`. Google provides a Client ID and Client Secret, which we will use later in the Rails application configuration.

## Step 2: Configuring the Rails Application

The next step is to adapt your Rails application to the Google OAuth system. Start by adding the `omniauth-google-oauth2` and `dotenv-rails` gems to your Gemfile. The latter helps to manage your environment variables effectively.

```ruby
gem 'devise'
gem 'omniauth-google-oauth2'
gem 'dotenv-rails', groups: [:development, :test]
```

After running `bundle install` to install these gems, create a `.env` file in the root of your Rails application. This is where we store the Google Client ID and Client Secret that we obtained earlier.

```ruby
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
```

These environment variables are essential for your Rails application to communicate with the Google OAuth2 server. To integrate them, insert the following code into 

`config/initializers/devise.rb`:

```ruby
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], {}
```

Next, we need to make sure our User model is omniauthable and can handle the callback data. To achieve this, update `app/models/user.rb`:

```ruby
class User < ApplicationRecord
  devise :database_authenticatable, :registerable, :omniauthable, omniauth_providers: %i[google_oauth2]

  def self.from_omniauth(access_token)
    data = access_token.info
    user = User.where(email: data['email']).first

    # Uncomment the section below if you want users to be created if they don't exist
    unless user
        user = User.create(
           email: data['email'],
           password: Devise.friendly_token[0,20]
        )
    end
    user
  end
end
```
Furthermore, we need to specify the callback in `config/routes.rb`:

```ruby
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
```

Finally, let's create the `OmniauthCallbacksController `in `app/controllers/users/omniauth_callbacks_controller.rb`, which will handle the authentication data returned by the Google OAuth2 server:

```ruby
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def google_oauth2
      @user = User.from_omniauth(request.env['omniauth.auth'])

      if @user.persisted?
        sign_in_and_redirect @user
      else
        session['devise.google_data'] = request.env['omniauth.auth'].except(:extra) # Removing extra as it can overflow some session stores
        redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
      end
  end
end
```

## Step 3: Exception Handling

Last but not least, we must not overlook the importance of exception handling. Robust applications need to handle failures gracefully. For this purpose, we'll set up an OmniAuth exception handler.

In `config/initializers/omniauth.rb`:

```ruby
OmniAuth.config.on_failure = proc do |env|
  "Users::OmniauthCallbacksController".constantize.action(:failure).call(env)
end
```

And then add the failure method to `OmniauthCallbacksController`:

```ruby
def failure
  flash[:error] = 'There was an error while trying to authenticate you...'
  redirect_to new_user_session_path
end
```

Remember to restart your Rails server whenever you make changes to the initializers.

And voila! You've just set up your Rails application with Google OAuth2 using Devise, providing your users with an easy and secure way to sign in with their Google accounts. Happy coding!