Setup Google OAuth Rails without Devise

ยท

2 min read

Here's my recipe to quickly set up Google OAuth login without using the devise gem, only pure Omniauth.

Install dependencies

Add these to your Gemfile:

gem 'omniauth' 
gem 'omniauth-google-oauth2' 
gem 'omniauth-rails_csrf_protection', '~> 0.1'

then `bundle install`.

# Create the user table and model

The absolute minimum is email, provider, and UID, but I usually go with:

```ruby
rails g migration create_users first_name last_name email image provider uid

Run the migration:

rails db:migrate

Then create a new User model:

# app/models/user.rb

class User < ApplicationRecord 
  def name 
    [first_name, last_name].join(' ') 
  end 

  def update_dynamic_attributes(auth) 
    self.first_name = auth.info.first_name 
    self.last_name = auth.info.last_name 
    self.email = auth.info.email 
    self.image = auth.info.image if auth.info.image? 
  end 

  class << self 
    def find_or_create_with_omniauth(auth) 
      user = find_by(auth.slice(:provider, :uid)) || initialize_from_omniauth(auth) 
      user.update_dynamic_attributes(auth) 
      user.save! 
      user 
    end 

    def initialize_from_omniauth(auth) 
      new do |user| 
        user.provider = auth.provider 
        user.uid = auth.uid 
      end 
    end
  end 
end

Setup Omniauth

We'll add a required callback route, but also two custom routes just for the comfort of using `sign_in_path` and `sign_out_path`:

# config/routes.rb

Rails.application.routes.draw do 
  # AUTHENTICATION ROUTES 
  get 'auth/:provider/callback', to: 'sessions#create' 
  get '/sign_out', to: 'sessions#destroy', as: :sign_out 
  get '/auth/google_oauth2', as: :sign_in
  get '/auth/failure' => 'sessions#failure'
end

Create a config/omniauth.rb file like the below:

# config/omniauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, ENV.fetch('GOOGLE_CLIENT_ID'), ENV.fetch('GOOGLE_CLIENT_SECRET')
end

OmniAuth.config.allowed_request_methods = %i[get]
OmniAuth.config.silence_get_warning = true

Handle sign-in, sign-out, and eventual failures

class SessionsController < ApplicationController
  def create
    user = User.find_or_create_with_omniauth(request.env['omniauth.auth'])
    session[:user_id] = user.id
    redirect_to after_sign_in_path, notice: "Signed in as #{user.name}"
  end

  def failure
    Sentry.capture_message(params[:message])
    redirect_to root_url, alert: 'Authentication failed.'
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url, notice: 'Signed out'
  end

  private

  def after_sign_in_path
    request.env['omniauth.params']['after_sign_in_path'] || request.env['omniauth.origin'] || user_projects_path
  end
end

For more details about the failure handling part, I wrote a specific article to document it here: https://www.mdless.com/articles/9-how-to-handle-failures-with-omniauth-rails

Add a current_user helper method

To use a similar method as devise, let's create a custom current_user helper:

class ApplicationController < ActionController::Base 
  helper_method :current_user 

  private 

  def current_user 
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id] 
  end 
end

Add links to sign in and sign out

In our views, we'll add links allowing users to connect:

<% if current_user %> 
  signed in as <%= current_user.name %> 
  <%= link_to 'sign out', sign_out_path %> 
<% else %> 
  <%= link_to 'sign in with google', sign_in_path %> 
<% end %>

Did you find this article valuable?

Support Flavio Wuensche by becoming a sponsor. Any amount is appreciated!

ย