Authenticating with Google

Jesse Herrick
Jesse Herrick
Share
GooglePlus-Logo-Official

I have a brilliant idea. I am going to build a site that makes *insert verb*ing easier for *insert noun*. There’s a problem though: I need people to be able to login, but I don’t want to deal with registering users, resetting their passwords, etc.. Therefore, I have decided that using Google login is the best option. How can I implement this?

With a combination of Devise and Omniauth, the process is not as painful as it sounds. Essentially, I am going to use the process laid out bu the OAuth 2.0 specification to implement authentication for my NounVerber.

Pull Up Your Suspenders

rails new is nice, but I like a little more functionality out of the box, so I am going to use thoughtbot’s Suspenders gem. Let’s create our app:

$ gem install suspenders
$ suspenders login_with_google
# lots and lots of rails app creation output

$ cd login_with_google

First, we need to create some pages to visit:

  • home – root page, which is not secured.
  • secure – only viewed with login.

We’ll create a pages controller that has these methods. Obviously, this is just a placeholder, as a Google login for static content would be frivolous:

$ rails g controller pages home secure

Set the root page to pages#home:

# config/routes.rb

Rails.application.routes.draw do
  root to: 'pages#home'
  get 'pages/secure'
end

Awesome. But before we can get into actually creating the authentication, we need to create a User model. We are only using Google’s API for authentication purposes, so we’ll only need the following attributes:

  • uid – unique provider-given (Google) id for our user
  • provider – the authentication provider
  • name – name saved from their Google profile
  • email – email saved from their Google profile

Let’s generate that model:

$ rails g model user uid provider name email

This should create our User model. After checking the migration, we can rake db:migrate without worry.

Note: As Suspenders tries to be Heroku-ready, you must have PostgreSQL installed on your computer in order for your migrations to work. If you just want to user SQLite3, then swap the gems and database configurations accordingly.

Adding Devise and Omniauth

Now that we have pages to display and users correctly modeled in the database, it’s time for the fun part: adding Devise and Omniauth.

First, add the gems:

# Gemfile

# ...
gem 'devise'
gem 'omniauth-google-oauth2'
# ...

The gem omniauth-google-oauth2 is what does the magic. Now bundle install and we’re good to go!

In order for this to work properly, setup a few things:

  1. Install Devise
  2. Setup the Rack Middleware
  3. Add Devise routes
  4. Add Devise to User model
  5. Create callbacks controller
  6. Add authentication to secure pages

Installing Devise

Devise provides an installation command for easy setup:

$ rails g devise:install

Setting Up Omniauth Rack Middleware

We need to create an omniauth middleware initializer to authenticate our client application to Google. This is done with a client ID and secret:

# 'config/initializers/omniauth.rb'
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"]
end

Whoa there, wait a minute! Did we just ask for credentials that don’t exist yet? Yes, we did. We need to have Google generate them for us.

First, go to https://console.developers.google.com/ and create a project. Then, underneath the sidebar’s “API’s & auth”, click “Credentials” and then click “Create new Client ID”. Now copy and paste the credentials you get into your .env file as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. Suspenders supplies the dotenv gem that allows us to put our environment variables into a .env file that is executed by Rails on startup. It’s a good practice that ensures we don’t put secrets into source control.

Another thing that may trip you up later is enabling APIs. For our purpose, we just need the Google+ API. So go to your project page on Google, click “APIs”, then enable Google+.

Adding Devise Routes

Devise creates the devise_for method to make custom routes for authentication, and we need to add routes to handle the OAuth flow:

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

Notice that we defined a controller that doesn’t exist yet. We’ll get to that later.

Adding Devise to User Model

Devise has another helper method, devise, that configures our model to use Devise features. Add the following to the User model:

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

Creating the Callbacks Controller

When a user logs in, they’re sent to the provider (Google in this case) and then back to the original site at a callback URL. This callback does authentication for the user based on data from the provider. Let’s create this callback:

# 'app/controllers/users/omniauth_callbacks_controller.rb'

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

    if @user.persisted?
      sign_in_and_redirect root_path, event: :authentication
    else
      redirect_to root_path, flash: { error: 'Authentication failed!' }
    end
  end
end

The method google_oauth2 in the callback controller will apply only to the route: /users/auth/google_oauth2/callback. When this method is called, it finds an existing user or creates a new one. If the user exists or was just created, the user is logged in and redirected to the root path. Otherwise, the user is redirected back to the root and an error is flashed.

In a more complex application, the redirect paths would most likely be different, but we’ll use the root page for our purposes.

Wait a minute! We called a method named User.from_omniauth that doesn’t exist! Let’s go into the User model and create it. This method will take a hash of user arguments and find or create a new user:

# 'app/models/user.rb'
...
def self.from_omniauth(auth)
  where(provider: auth[:provider], uid: auth[:uid]).first_or_create do |user|
    user.name = auth[:info][:name]
    user.email = auth[:info][:email]
  end
end
...

Adding Authentication to Secure Pages

We’ve done a lot so far, so let’s rails s and test out the authentication. Open up a browser and go to http://localhost:3000/users/auth/google_oauth2 and go through the steps (you know the drill). If it works, you’re redirected to a blank page! How exciting! Almost revolutionary! But seriously though, it looks to the average user as if they did absolutely nothing.

We need to add a little login info section to our home page. Open up app/views/home:

Boring.

# 'app/views/pages/home.html.erb'
<h1>Pages#home</h1>
<p>Find me in app/views/pages/home.html.erb</p>

Exciting!

# 'app/views/pages/home.html.erb'

<div class="container">
  <% if user_signed_in?  %>
    <p>Welcome back, <a href="mailto:<%= current_user.email %>"><%= current_user.name %></a>!</p>
  <% else  %>
    <p><%= link_to 'Sign in.', user_omniauth_authorize_path('google_oauth2') %></p>
  <% end %>
</div>

<h1>Pages#home</h1>
<p>Find me in app/views/pages/home.html.erb</p>

Now, if we visit the home page here’s what we get!

Logged in With Google

We are now logged in with Google! The hard part is done.

Authorized Personel Only

Remember way back when we created our pages controller? Well that secure page is going to come in handy now.

First, add get 'pages/secure' to config/routes.rb and then open up the pages controller. We are going to ensure that the user is logged in before viewing the secure page.

# 'app/controllers/pages_controller.rb'
class PagesController < ApplicationController
  def home
  end

  def secure
    unless user_signed_in?
      redirect_to root_path, flash: { error: 'Please sign in first.' }
    end
  end
end

If we try to go to http://localhost:3000/pages/secure without logging in first, we’ll get sent back to the home page with an error flashed. And there you have it!

Conclusion

There are so many reasons why logging in with Google is a great choice. Almost every user already has a Google account, you don’t have to deal with username/password resetting, and you don’t have to worry about securing passwords because you don’t have any. Thanks to the thriving Rails community, we have the tools to quickly authenticate with Google. Instead of a registration page on your next project, why don’t you try logging in with Google instead?

All example project code can be found on GitHub.

Frequently Asked Questions (FAQs) on Google Authentication

How can I integrate Google Authentication into my Rails application?

Integrating Google Authentication into your Rails application involves several steps. First, you need to create a new project in the Google Developers Console. After creating the project, you will be provided with a client ID and secret, which you will use in your Rails application. You will also need to set up the OmniAuth Google OAuth2 strategy in your Rails application. This involves adding the OmniAuth Google OAuth2 gem to your Gemfile, configuring the strategy in an initializer, and setting up routes and controllers to handle the authentication process.

What is OmniAuth and how does it work with Google Authentication?

OmniAuth is a flexible authentication system for Ruby applications. It provides a standardized interface for multiple authentication providers, including Google. When used with Google Authentication, OmniAuth handles the process of redirecting users to Google for authentication, and then handling the callback from Google once authentication is complete. This simplifies the process of integrating Google Authentication into your Rails application.

How can I handle the callback from Google once authentication is complete?

Handling the callback from Google involves setting up a route and controller action in your Rails application. The route should match the callback URL that you specified in the Google Developers Console. The controller action should handle the OmniAuth authentication hash that is returned by Google, and use this information to create or update a user record in your application.

How can I secure my Google client ID and secret in my Rails application?

It’s important to keep your Google client ID and secret secure, as they can be used to impersonate your application. One way to do this is by using environment variables. You can set these variables in your development environment, and then reference them in your Rails application. This way, your client ID and secret are never hard-coded into your application, and they are not exposed in your source code or version control system.

How can I handle errors during the Google Authentication process?

Handling errors during the Google Authentication process involves setting up a route and controller action in your Rails application. The route should match the failure path that you specified in the OmniAuth configuration. The controller action should handle the error message that is returned by OmniAuth, and display an appropriate message to the user.

How can I test the Google Authentication process in my Rails application?

Testing the Google Authentication process can be done using a combination of unit tests and integration tests. Unit tests can be used to test individual methods and functions, while integration tests can be used to test the entire authentication process from start to finish. You can use testing frameworks such as RSpec and Capybara to write these tests.

How can I log out a user who has authenticated with Google?

Logging out a user who has authenticated with Google involves deleting the user’s session in your Rails application. This can be done by calling the reset_session method in your controller action. Once the session is reset, the user will be logged out and will need to authenticate again to access protected resources.

How can I use the user information returned by Google in my Rails application?

The user information returned by Google can be used to create or update a user record in your Rails application. This information includes the user’s Google ID, email address, name, and profile picture. You can access this information from the OmniAuth authentication hash that is returned by Google.

How can I restrict access to certain parts of my Rails application to authenticated users?

Restricting access to certain parts of your Rails application to authenticated users can be done using before filters in your controllers. These filters can check if a user is authenticated, and redirect them to the login page if they are not. You can use the current_user method to check if a user is authenticated.

How can I customize the look and feel of the Google Authentication process?

Customizing the look and feel of the Google Authentication process can be done using the Google Developers Console. Here, you can customize the consent screen that is displayed to users during the authentication process. You can also customize the callback URL and the scopes that your application requests access to.