Google Login Rails 7 tutorial
Tags: ruby, webdev, rails, google
Published: 2022-07-06 00:34:19

In this tutorial, I'll implement google login in rails 7 using omniauth in [devise gem](https://github.com/heartcombo/devise).

> Take note, in this tutorial I'll be using [login-demo](https://github.com/jkevinbaluyot/login-demo/tree/main) as code base which is tackled in this [tutorial](https://webology.hashnode.dev/create-login-in-ruby-on-rails).
It's best to look at it before proceeding on this one.

I'll be assuming you implemented devise login in your rails app.

1. Add these gems.
    ```ruby
    gem "omniauth-google-oauth2"

    gem "omniauth-rails_csrf_protection"
    ```

2. Add this to your devise model(mine is `User`).
    ```ruby
    # models/user.rb
    :omniauthable, omniauth_providers: [:google_oauth2]
    ```

    This would add the omniauth function of devise to the rails app.

3. Add fields to devise table.
    ```ruby
     class AddFieldsToUser < ActiveRecord::Migration[7.0]
         def change
           change_table :users, bulk: true do |t|
            t.string  :provider
            t.string :uid
           end
         end
     end
    ```

    Run `rails db:migrate` to add these fields to the`users` table.

    I'll be saving the uid and provider (which the value for this would be `google_oauth2`). Though it might change depending on the provider you're using. 

4. Create `omniauth_callbacks_controller.rb`.
    ```ruby
    # 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
  ```
5. Add this to your route.
    ```ruby
    # config/routes.rb
    Rails.application.routes.draw do
        devise_for :user,
          controllers: {
             omniauth_callbacks: 'users/omniauth_callbacks'
          }
  
        # ------
        # Other routes
        # ------
    end
    ```

## Google Oauth
In this part, things get a bit complicated. 

In this, we would get the google client id, client secret and configure the google console to enable OAuth which would allow the rails app to log in using google.

Go to  https://console.cloud.google.com. Then select a project but if not create one.

![2022-06-26-16-34-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656233010107/zc1qfmtDR.png)

I named my newly created project `Login Demo`.

![2022-06-26-16-49-console.cloud.google.com (1).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656237182203/YykXavooQ.png)

Go to `APIs & Services > Oauth consent screen`

![2022-06-26-17-57-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656237516063/M6VJrA4V0.png)

Choose user type `External`. Next is to fill up the necessary input value on the form.


![2022-06-26-18-01-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656237751039/mydc6xvkf.png)

Click  `Save and Continue` at the bottom of the form. 

Next is go to `APIs & Services > Credentials`. At the top click `CREATE CREDENTIALS` and choose `OAuth client ID`.

![2022-06-26-18-07-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656238177863/nPFbUCayb.png)

Choose `Web application` as Application type.  

![2022-06-26-18-11-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656238314359/7cXAzi2hm.png)

Check your routes and find the callback URL for google 

![Screenshot from 2022-06-26 18-31-08.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656239783084/8T6o0xERf.png)

Then add the callback URL to `Authorized redirect URIs`.

![2022-06-26-18-37-console.cloud.google.com.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1656239917586/nFnWILx21.png)

Then click `Create`. After creation, there should be a popup showing your client id and client secret. Save it for later use.

Now that you have your client id and client secret we're gonna start configuring google login in the rails app.

## Implement Google Login

1. Add client id and client secret to your .env 
    ```ruby
    # .env
    GOOGLE_OAUTH_CLIENT_ID=''
    GOOGLE_OAUTH_CLIENT_SECRET=''
    ```

2. Configure your devise.rb
    ```ruby
    # config/initializers/devise.rb

    config.omniauth :google_oauth2, ENV['GOOGLE_OAUTH_CLIENT_ID'], ENV['GOOGLE_OAUTH_CLIENT_SECRET']
    ```

3. Now just add the google log in button
    ```
    // app/views/shared/_user_details.html.erb
    <%= button_to  'Login with Google', user_google_oauth2_omniauth_authorize_path, method: :post, :data => {turbo: "false"} %>
    ```
    We have to create our custom link because the generated link for logging in with google may not work properly in rails 7. 

Congratulations! 🎉 Now you have implemented google login to your rails 7 app.

You could check the repository here: https://github.com/jkevinbaluyot/login-demo/tree/omniauth-demo

### References:
- https://www.reddit.com/r/rails/comments/twjznq/help_with_omniauth_and_devise_rails_7/
- https://dev.to/rbazinet/hotwire-fix-for-cors-error-when-using-omniauth-3k36
- https://medium.com/@adamlangsner/google-oauth-rails-5-using-devise-and-omniauth-1b7fa5f72c8e

**EDIT: 06/25/2023**
- I forgot to include this:
```ruby 
  # app/models/user.rb
  def self.from_google(email:, uid: )
    find_or_create_by!(email: email, uid: uid, provider: 'google_oauth2')
  end
```