Skip to content

Instantly share code, notes, and snippets.

@SumitGA
Created January 2, 2018 09:48
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save SumitGA/97d77c39be5c370c906972e14e809cdd to your computer and use it in GitHub Desktop.
Save SumitGA/97d77c39be5c370c906972e14e809cdd to your computer and use it in GitHub Desktop.

Adding Google Sign In With Rails and React

For this blog post we will be using ruby version 2.4.0 and rails version 5.1.4 as backend and React/Redux as frontend. Adding Google Sign in functionality in your application can be done in two popular ways.

  • Adding google sign in functionality in the frontend with google api client gapi.

  • Adding google sign in functionality logic in the backend server.

If your application has separate backendend with APIs feeding data to the frontend React application, I suggest you to follow the step 2. If you want yo enhance your application security, then you implement both.

Before we can work on the actual code for authentication, we need to do some setup on the Google Developer Console.

  • First, sign into the Google APIs console. Then click on the 'Select a project' dropdown in the upper right and click Create Project.

  • The create project modal will appear. Enter a project name and agree to the terms of service, then click 'Create'.

  • Next, do a search for contacts then click on the Contacts API entry that pops up.

  • Click the blue enable button, then click the back button right next to it once it enables.

  • Now do another search for google+ and click the Google+ API entry that appears.

  • Click the enable button.

  • Now click the Credentials link on the left side of the screen.

  • Next, click Oauth Consent Screen, fill in the details, and click save.

  • Next click the blue button that says Create Credentials and choose OAuth client ID.

  • Choose Web application, enter http://localhost:3000/auth/google_oauth2/callback, then hit create.

  • A box will pop up containing your credentials, copy them to a text file, you will need them later.

Now we are ready to dive into adding the google sign in logic to the backend.

Adding google sign in logics to your backend application

Now that we've set up things with Google, we need to add the code necessary to do the actual.

This blog post assumes that you are using devise_token_auth gem or handling User registration, sign in, sign out. This gem pretty much adds all the field to the database during migration such as provider and uid, which shall be used later on. Once you are done with devise_token_auth gem. Now is the time to move ahead for adding google sigin logic to the rails backend.

The best way to authenticate with Google is to use the omniauth-google_oauth2 gem. This gem allows you to quickly and easily add Google authentication, and it plays nice with all of the other omniauth gems. Add the gem to your gemfile.

Gemfile.rb

gem "omniauth-google-oauth2", "~> 0.2.1"

Now run bundle

Next, we need to create an initializer. Create an initializer in the initializers folder called omniauth.rb or if you are using Devise gem to handle users then add the code listed below into you devise.rb file inside of initializer. If you are editing an existing project that already uses omniauth you can just add the code below to your existing omniauth.rb file. Make sure to replace Google client id with your Google client id, and Google client secret with your Google client secret.

OmniAuth.config.logger = Rails.logger

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, 'my Google client id', 'my Google client secret', {client_options: {ssl: {ca_file: Rails.root.join("cacert.pem").to_s}}}
end

On some systems, Rails is unable to find any cert files for SSL certificates. This presents a problem since the actual authentication is done via SSL. To resolve this, you will need to download this cert file and place it in the root directory of your Rails application making sure the file does not have a .txt extensionn on the end.

Since you are using devise_token_auth gem to handle user's session, it also adds omniauth routes for you, but for this post we are doing from the scratch. So go ahead and skip the omniauth routes in your routes file as shown below.

mount_devise_token_auth_for 'User', at: 'auth', skip: [:omniauth_callbacks]

And also add a new routes for handling google sign in authentication by yourself

post 'auth/request', to:'authorization#get_authorization'

Now is the perfect time to create an authorization controller as mentioned in the routes file. Make sure the controller name is same as in the routes.rb

Add the following code block to your authorization controller file you just created.

require 'httparty'                                                             
require 'json'                                                                                                                  
class AuthorizationController < ApplicationController                              
  include HTTParty

 def get_authorization
   url = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=#{params["id_token"]}"                  
   response = HTTParty.get(url)                   
   @user = User.create_user_for_google(response.parsed_response)      
   tokens = @user.create_new_auth_token                      
   @user.save
   render json:@user
 end
end

Next thing to do is to add some code to your User model. All you need to do is create a method create_user_for_google in your user model. Add the following code block to your User.rb file

def self.create_user_for_google(data)                  
  where(uid: data["email"]).first_or_initialize.tap do |user|
    user.provider="google_oauth2"
    user.uid=data["email"]
    user.email=data["email"]
    user.password=Devise.friendly_token[0,20]
    user.password_confirmation=user.password
    user.save!
  end
end  

Rendering the appropriate response for signed up user from our backend API

  • It is never a good approach to directly render a user object from our backend api, Instead response a success status and also we need to attach a headers with access-tokens, clients, uid, expiry inorder to sign in a user to our API. You want to ingnore all these tokens then you can configure your devise_token_auth.rb file to ignore then. But I recommend that you do include those tokens. This will provide your api with extra layer of security.

  • The code block below adds an headers and satus code to the api response.

controller file

set_headers(tokens)
render json: { status: 'Signed in successfully with google'}

private                                            
def set_headers(tokens)
  headers['access-token'] = (tokens['access-token']).to_s
  headers['client'] =  (tokens['client']).to_s
  headers['expiry'] =  (tokens['expiry']).to_s
  headers['uid'] =@user.uid             
  headers['token-type'] = (tokens['token-type']).to_s                  
 end                                          
end

Adding Google Sign in React Component in the frontend

After your have already completed the backend part, now is the time to add sign in button(React Component) to your frontend.

We will be using react-google-oauth react component to add sign in with google button to our react application.

Follow the link above to get the npm package of the react-google-oauth component.

Add the component to your login form, page whatever as so.

import {GoogleAPI, GoogleLogin, GoogleLogout} from 'react-google-oauth';
<GoogleAPI className="GoogleLogin" clientId="Your client API Key">
<div>
  <GoogleLogin height="10" width="500px" backgroundColor="#4285f4" access="offline" scope="email profile" onLoginSuccess={this.responseGoogle} onFailure={this.responseGoogle}/>
</div>
</GoogleAPI>

The callback method that the component will execute on success-full authentication is responseGoogle.

Now create a method responseGoogle as so.

responseGoogle = (response) => {
  var token = google_response.Zi;
  const requestOptions = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${google_response.Zi.accessToken}`,
      'Content-Type': 'application/json',
      'access_token': `${google_response.Zi.accessToken}`
    },
    body: JSON.stringify(token)
}

return fetch(`backend rails api url to google sign in path`, requestOptions)
  .then(response => {
    Cookie.set('accesstoken', response.headers.get('access-token'), {
      expires: 7
    });
    Cookie.set('client',response.headers.get('client'), {expires: 7});
    Cookie.set('tokentype',response.headers.get('token-type'), {expires: 7});
    Cookie.set('expiry',response.headers.get('expiry'), {expires: 7});
    Cookie.set('uid', response.headers.get('uid'),{expires: 7});
}

Your frontend application should check for the Cookie data for signed in user which you have already set. The frontend application will now redirect to the home page.

Congratulation!! You are now successfully logged in with google.

Google Sign in with javascript client gapi

Install gapi client with npm package as follows

npm install --save gapi-client

Now add the gapi client as so.

import gapi from 'gapi-client';

//On load, called to load the auth2 library and API client library.
gapi.load('client:auth2', initClient);

// Initialize the API client library
function initClient() {
  gapi.client.init({
    discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"],
    clientId: 'YOUR_CLIENT_ID',
    scope: 'https://www.googleapis.com/auth/drive.metadata.readonly'
  }).then(function () {
    // Perform all the actions related to the backend API.
    //This can include sending authentication params to the backend api as mentioned earlier above
    // Once the backend send the success status redirect user to the Home page.
  });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment