Google OAuth + Rails 5 using Devise and OmniAuth

Adam Langsner
7 min readJul 2, 2019

I love adding a Google OAuth login to a Rails app. It’s a great option for building a simple login for back-office admin tools. Employees don’t need to remember a username and password. Instead, they can just log in with their company email address.

The setup is not conceptually difficult but I have found it convoluted and nuanced. So, I wrote this article to provide a step by step guide of a barebones implementation. I’m going to be doing this in the context of an Admin model but it works just as well for end users or other identities.

Here’s a quick rundown of what we’ll be doing:

  • Creating Admin ActiveRecord model and migration with Devise.
  • Creating appropriate routes.
  • Creating basic login page and authenticated admin dashboard page.
  • Configuring OAuth client ID in Google Cloud Console.
  • Handling OAuth Callback to find or create an admin and log in.

Let’s Dive in!

Installing Devise and OmniAuth

Add the following 3 gems to your Gemfile then run bundle install.

Now run rails generate devise:install. This creates two files: a devise.rb initializer and a devise.en.yml translation file. It also modifies your routes file. When run, it will print four additional setup instructions. You can ignore these for now, we’ll do them as we go along.

Open the devise.rb file and add the following anywhere inside the setup block to configure OmniAuth:

Don’t worry. We’ll set those environment variables in a bit.

Devise Admin Model and Migration

Now that we have devise and OmniAuth installed, let’s generate our Admin model. Run rails generate devise Admin. This creates a model and a migration that we’ll need to edit.

Admin Model

We don’t need all the devise modules in the generated model, only OmniAuth. So let’s simplify it to this:

Admin Migration

We can also remove most of the stuff in the generated migration. But we will add some new columns to persist stuff we get from Google:

Run rails db:migrate to generate the admins table.

Devise Routes

Devise added a devise_for :admins line of code to the routes file. It creates a bunch of routes we don’t need. Let’s replace it with the bare minimum routes needed for OmniAuth:

Let’s go through this. First we make the root path go to the dashboard.

The next line generates two routes needed for OmniAuth: a passthru route that gets called when the user clicks login and a callback route that the browser redirects to after the user authenticates with Google.

The last two routes are for the login page and the logout button. They’re scoped to the admins namespace so they don’t conflict with login / logout routes for other potential identities like users.

Generated routes for Google OmniAuth Rails login

Login Page and Admin Dashboard Page

Creating the Controllers

Let’s create the dashboard controller in app/controllers/dashboards_controller.rb:

and the sessions controller in app/controllers/admins/sessions_controller.rb:

We don’t need to implement new or destroy in the sessions controller because we inherit them from Devise::SessionsController. We just need to override the after_sign_out_path_for and after_sign_in_path_for methods.

We also want to ensure that all controllers are authenticated by default, so let’s add that to the ApplicationController:

Devise generates the authenticate_admin! for us. This ensures that when an unauthenticated user tries to load any page they’ll get redirected to the login page.

Creating the Views

Let’s stub out an authenticated dashboard view in views/dashboards/show.html.erb:

And then the login view in views/admins/sessions/new.html.erb:

Devise uses the flash to communicate with the user, so let’s update the appplication.html.erb layout to show it:

The code at the top of the body loops through the flash and prints out whatever’s inside using the key as part of the class name. I added a <style> tag just to give some color to different flash types so we can easily differentiate them.

Great! Now that that’s all done, boot up your server, go to localhost:3000 and you’ll get redirected to /admins/sign_in and you’ll see the following:

The Login Page

The link won’t work yet because we haven’t configured Google OAuth. Let’s do that.

Configuring OAuth in Google Cloud Console

This part is a little confusing.

If you’re logged into multiple Google accounts it helps to log out of all but the one you want to use for this app (e.g. log out of your personal gmail if you intend to set this up for your work).

Go to https://console.cloud.google.com. We need to create a Project to house the OAuth Client ID that we’ll create. If you don’t have any Projects you’ll see this page. Click “Create” to set up a new Project.

I named my project “Admin Dashboard”, name it anything you want. If you’re doing this for your company you’ll want to create/select an Organization to house the Project.

Now, make sure your new project is selected in the blue dropdown at the top. Then, within “API & Services”, click “Credentials” on the left and select the “OAuth consent screen” tab. Here you just need to set the “Application Name” and save. You can leave everything else as is. What you set here will be shown when the user sees Google’s account selection page.

Setting Application Name for OAuth consent screen

Now, go to the “Credentials” tab and create a new OAuth Client ID

Select “Web application” and provide a name. I named it “Development” because I like to create one OAuth client ID per environment. Under “Authorized redirect URIs”, add the path to the callback endpoint (http://localhost:3000/admins/auth/google_oauth2/callback) and press enter.

After you create the OAuth client ID you’ll see the client ID and client secret.

Copy these values to wherever you set your environment variables for local development. I put them at the bottom of my development.rb file:

Handling OAuth Callback Request

The last piece of the puzzle is handling OAuth in our rails app. We’ll need two things: a controller and a new method on out Admin model.

Create the OmniAuth callbacks controller in controllers/admins/omniauth_callbacks_controller.rb:

The google_oauth2 method is the action for the callback route. It’s called after the user selects their Google account. The request has a bunch of info about the user that just authenticated. We check to see if we can find an admin record and if so we sign in and redirect to the dashboard with a success flash. Otherwise, we sign out and go back to the login page with an alert flash.

Let’s implement the Admin.from_google method. Add the following to the Admin model:

This method first checks that the user’s email is of the correct domain. If it is, we do a find or create for an admin record using the email address.

Verifying that the email’s domain matches your company domain is a security measure to ensure only employees can log in to the dashboard. If you’re building this for end users you’ll probably want to remove that check.

There’s also an opportunity for more security here. Doing a find or create of the admin record is great for a development or staging environment but might not be a good idea in production. To add more security in production you could do just a find. This means new employees won’t be able to log in automatically. Someone else in the company will need to first create an admin record with the new employee’s email.

Now that we have all the code in place, let’s log in:

You can download the example rails app we created from this github repo: https://github.com/adamlangsner/google_oauth_devise. The only thing missing are client ID and client Secret environment variables.

If you haven’ t already read POODR (Practical Object Oriented Design in Ruby) by Sandi Metz. I highly recommend it. I read it a few years ago and it’s made me both a better Rubyist, a better programmer and a better thinker.

--

--