In Depth Guide: “Sign In With Google” In A React JS Application

This is a big and detailed guide about how to add “Sing In With Google” to a React JS application. There are many painful pitfalls that one can fall into when trying to do this. So, my aim with this guide is to help you stay away from those & save your time.

Lets get started.

Some Outdated Info Warning: Do Not Use NPM Package “react-google-login”

When I first tried to implement this, I found a lot of articles that talked about using this package. And the problem is that, as of right now (Nov 25th 2022) the package does not work.

You get a strange error like:

{error: "popup_closed_by_user"}

You are left wondering, “what popup??”. There was no popup. There are some other issues too. When I tried to use it, it turned out, that this package was not compatible with the latest version of ReactJS. So, avoid it.

The Correct Package To Use: @react-oauth/google

This is the correct package to use right now. Here is the NPM link.

You can add it to your React JS project in the usual way.

npm install @react-oauth/google@latest

Before You Use The Library: Setting Up Your Project On Google Cloud Platform

In order for you to start using the above package, you will have to set up a new project on Google Cloud Platform.

At the end of your setup, what you get is something called a Client ID & Client Secret.

I have always found it really hard to navigate and get things done on Google Cloud Platform. Maybe it’s the UI design. Maybe I am dumb. Not sure which.

In any case, I have given step by step instructions with screenshots below. Hopefully this will save you a lot of trouble.

Step 1: Create A New Project

Head over to this URL.

After login (if needed) you should see the following screen..

Create A New Project On Google Cloud Platform For The Sign In With Google ReactJS ProjectFill in the name of the new project and hit “Create”

Step 2: Select The Project & Go To The Credentials Screen

Well, just follow the screenshots. First select the project from the top left..

Select the project from the GCP console

On the left, using the “burger” menu, go to “APIs and services” and then “Credentials” like so…

go to the credentials screen in order to create credentials for login with google

Step 3: Fill out the “OAuth Consent Screen” Details If You Have Not

When you get to “Credentials”, you will most likely be asked to fill out the “OAuth Consent Screen” form. (In case you are not asked to do so, jump to next step)

This form is all about the information that the user sees in the “Sign In With Google” popup window. On the first screen, choose “External”. Hit “Create” and move to the next screen.

the oauth consent screen for sign in with google form

Below is roughly how I have filled out the form on the next screen. The URLs of the privacy policy and terms etc. can be dummy URLs for now. But, you should update them later.

Fill out OAuth consent screen for sign in with GoogleWhen you are done filling the form, hit “Save And Continue”.

Google will prompt you to fill out another form on a new page. But, we can ignore that for now. What we have done is the minimum that is required.

Step 4: Back To The Credentials Page

Head back to the “Credentials” page and lets make those credentials that we need. Follow the screenshots:

create oauth credentials on the credentials page

Select the correct application type: “Web application”

select the web application as the application type for our react js projectFill out the form to create the credentials. Make sure you put in both localhost & localhost:3000

Fill out the form to create the credentials. But, its important to get the JS origins right

Step 5: You Finally Have Credentials

When all this is done, you finally have credentials that you can use in your web application. Copy them and keep them somewhere. We will be using them in the next step.

Finally, you have the credentials you can use in your react js app

How To Set Up A Basic Sign In With Google Button

There Are 2 steps to using the package we installed above.

Step 1: Wrap The Application In The GoogleOAuthProvider Component (You will need your ClientID from the last step)

Below is a code sample of me doing this in a brand new “create-react-app” application. I have commented the parts that are important.

// Bringing in the GoogleOAuthProvider from the package
import { GoogleOAuthProvider } from '@react-oauth/google';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <GoogleOAuthProvider clientId="PUT-YOUR-CLIENT-ID-HERE">
      <App />
    </GoogleOAuthProvider>
  </React.StrictMode>
);

On line 7 you can see the wrapping being. We have to put in the Client ID from the last step here.

Step 2: Use The GoogleLogin Component Provided By The Library To Render The Login With Google Button In Your Application

// Bring in the GoogleLogin
// component from the library
import { GoogleLogin } from '@react-oauth/google';

function App() {

  return (
      <div className="App">

          <GoogleLogin

            onSuccess={credentialResponse => {
              console.log(credentialResponse);
            }}
          
            onError={() => {
              console.log('Login Failed');
            }}
          
          />

      </div>
  );
}

With this, you get a nice “Login With Google” button on the screen. When the user clicks it, a pop-up opens that guides the user thought the login flow.

Sing in with google button rendered on the screen in the create application

Step 3: Study The Response That The Success Function Gets

On line 12 in the above code sample, you can see that a “onSuccess” function is specified. This function gets the response when the user finally signs in. Currently, the response is just simply logged to console. But, lets try and see what is actually there in the response. Below is the JSON object you get as the response

{
  "credential": "long JWT token String",
  "clientId": "your client id",
  "select_by": "btn_confirm"
}

Here is an example of what the token looks like:

{
  "credential": "eyJhbGmtpZCI6IjI3Yjg2ZGM2OTM4ZGMzMjdiMjA0MzMzYTI1MGViYjQzYjMyZTRiM2MiLCJ0eXAiOiJKV1QifQ.eyJpcdHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NjkzNDIxNTMsImF1ZCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjU5MjEyMTA2MTU3MjIyMTM4NCIsImVtYWlsIjoia2hvai5iYWRhbWlAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsIm5hbWUiOiJLaG9qIEJhZGFtaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BTG01d3UzMm1lckdfLTlpbkhSMkVFd1VZUDFxbndiZkg4TjJjZnlfcUhtLT1zOTYtYyIsImdpdmVuX25hbWUiOiJLaG9qIiwiZmFtaWx5X25hbWUiOiJCYWRhbWkiLCJpYXQiOjE2NjkzNDI0NTMsImV4cCI6MTY2OTM0NjA1MywianRpIjoiZTIzN2VjMGVmOWFkYWI2NjNhNGNkMmU4NjcxYWZiMjVlMzQ4NmRiYiJ9.qtPkkbtNLU9hKnsbDyFKTRm4Drdr1M0xoWgkyz4ymnxqg-HWh4ZKHARTzH7X6Gup-i5rJfQlA-AZ6XK7GvEkc_gARAu6azmpSbxgsR1etYhG_olqpI5j8Eb06TIGvKQZdRuR2O2wNhqOv0bpGGuMf8IrJzl1RgjfYu_YtHaCgsGGBmKwC-cRDwEuvTrCRiQe-jZkg0Vjmo8--2pkubfGXxbQJqFhi7ZLhTxsCXVCejpHAfzcpD5LszoymsVxcqAZjQOUcL8Fy78JydpEMghH-_unsESSkli8CpsAjTplCHywNu1_A3JIX59VoOh_dBet4HgvEzrlFjcsYSb8umvxIg",
  "clientId": "27111941a4cgdet6cpf802focedi.apps.googleusercontent.com",
  "select_by": "btn_confirm"
}

Step 4: Decoding The JWT Token

In the “credential” field, you get a JWT token. In case you are wondering what a JWT token is and why its used, below is good video..

If you decode the JWT using a tool like this you should see that it contains a JSON object like so..

{
  "iss": "https://accounts.google.com",
  "nbf": 16691069,
  "aud": "27185696-mbfingbqagi0vu135kmtjv87ei08i6ih.apps.googleusercontent.com",
  "sub": "10259261572221384",
  "email": "someemail@gmail.com",
  "email_verified": true,
  "azp": "2718595269ingbqagi0vu135kmtjv87ei08i6ih.apps.googleusercontent.com",
  "name": "Some Name",
  "picture": "https://lh3.googleusercontent.com/a/ALm5wu32mnHR2EEwUYP1qnwbfH8N2cfy_qHm-=s96-c",
  "given_name": "Name",
  "family_name": "Lastname",
  "iat": 1669124369,
  "exp": 1669127969,
  "jti": "188f1fcfa07dd76b545b24bce7177beaff1e4"
}

Yay! So you have the users email ID. But, now you need to log the user in securely. So, lets look at how to do that in the next section.

What To Do With The JWT Token & How To Securely Sign Your Users In?

It is tempting to just decode the JWT token on the front end in the ReactJS application and extract the email ID of the user. But, DON’T DO THAT.

You see, after you do that, the next step would be to create a session on your back-end for this user with the email ID.  Lets assume you do that with a POST request to something like: “/create-session”

Now, how does your back-end know that the POST request was really generated by your ReactJS application. Maybe a hacker could generate the POST request via the browser dev console. Your back-end would happily accept the request and create a session for any email ID given. If that is done, the hacker can log into any account.

What you should actually do..

The better approach is that you should send the raw JWT token to the back-end.

Then use the NPM package (made by Google) google-auth-library to verify that the token has actually come from Google. Then create a session for the user based on the decoded email address.

Here is a section from the Google Docs about the same:

After you have verified the token, check if the user is already in your user database. If so, establish an authenticated session for the user. If the user isn’t yet in your user database, create a new user record from the information in the ID token payload, and establish a session for the user. You can prompt the user for any additional profile information you require when you detect a newly created user in your app.

From: Google Docs

Below is some sample code for doing the JWT verification in Node JS using googles NPM package. (The part about creating the session on your back-end etc is something you will have to handle)

const { OAuth2Client } = require('google-auth-library');

async function verify(client_id, jwtToken) {

    const client = new OAuth2Client(client_id);

    // Call the verifyIdToken to
    // varify and decode it
    const ticket = await client.verifyIdToken({
        idToken: jwtToken,
        audience: client_id,
    });

    // Get the JSON with all the user info
    const payload = ticket.getPayload();

    // This is a JSON object that contains
    // all the user info
    return payload;
}

The “payload” you get will the be same as the JSON with user details I have given an example of above.

As you can see, here once again you will need the “client_id” of your application.

If your back end is not NodeJS, do check out this page from Google. It covers all the details of how to do this in Java, PHP and Python too.

“One Tap” Login & How To Set It Up

Recently, Google started to offer a even faster way to login. It calls it “one tap” login.

What does “one tap” mean?

In the login, we have seen above, there are 2 taps (if the user is already logged into a Google account):

  1. Tap 1: Click on the “log in with google” button
  2. Tap 2: Choose the correct account in order to login

In the “one tap” version, a little popup is already on the screen with the accounts the user is signed into. The user just has to do a single tap and choose the account they want. Google responds with the same JWT as above.

Let me share some screenshots so that you know what all of this means:

When the user is not logged into any Google account, this is what happens:

how "one tap" login looks when user is not logged into any google accounts

So, in the above view, it’s still a few taps.

One tap “shines” when the user is already logged into a Google account or 2. Now, they just need to do their “one tap” on the account they need.

one tap when the user is logged in into a google account

How To Use “One Tap Login”?

You just need to change one line:

<GoogleLogin

    onSuccess={credentialResponse => {
      console.log(credentialResponse);
    }}

    onError={() => {
      console.log('Login Failed');
    }}

        { "This is all you need to add" }
    useOneTap

/>

After the user logs in, the exact same thing happens. You get a JWT that you need to use to log in your user as described above.

How To: Log Your Users Out

I have made a section about this since this might be a source of confusion.

Google’s role in this whole process is simply to give you an email ID (and profile pic etc. if you want it). After that, you have to create a session for the user in your back-end. Your user might choose to log out at any time. The user might click the “Log Out” button and then you will simply remove the session from your backed.

After logout, on the front end, you will find that the session is nowhere to be seen. So you will render the “Sign In With Google” button once more and the login process starts again.

The goal of this section was just to clarify that Google plays no role in logging the user out. It just helps you securely verify that the user really owns a particular email ID. That is all.

How To Use A Custom Login Button

In order to achieve this, you need to set up the ReactJS side of things like so:

// Import this from the lib
import { useGoogleLogin } from '@react-oauth/google';

function App() {

  // Use this function to trigger the
  // "LogIn With Google" process
  // at the end of which the onSuccess function
  // is triggered
  const login = useGoogleLogin({
    onSuccess: codeResponse => console.log(codeResponse)
  });

  return (
    <div className="App">
      <header className="App-header">

        {/* This is my button. Its just a regular HTML Button */}

        <button onClick={() => login()}>
          Log In Using Google
        </button>
      
      </header>
    </div>
  );
}

Now, what you get in the “onSuccess” function is a little bit different. Its not a JWT. Let me give you a sample of what you get:

{
  "access_token": "ya29.a0AeTM1ieGewVlRd75JAIEtk2m_MjpSo-LdO4ZtzTNeNQWVtratlYHJLjMkQZ6_ucX31aadjdghakjhdgWVGTBQ0TnmY2FDkxsfIYO4kiCxoVcOAdZ4kt0CBoEAqJ3RTGZfxgQ6onimg9tkH-okE8euzgFwOgaCgYKAZESARISFQHWtWOmXTGNnRDUYbShFsLKAgbgTg0165",
  "token_type": "Bearer",
  "expires_in": 3599,
  "scope": "email profile https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile",
  "authuser": "0",
  "prompt": "none"
}

So, basically, what you are getting is an “access_token”. This can be use to fetch details about the user using the following URL:

https://www.googleapis.com/oauth2/v3/userinfo?access_token=<PUT THE ACCESS TOKEN HERE>

Security Note: Don’t use this URL on the front end via Axios or something. This will open your application up the security issue described above.

The above URL is a Google API the gives you the user details in JSON form. The output looks something like this:

{
  "id": "102592121061571384",
  "email": "someemail@gmail.com",
  "verified_email": true,
  "name": "John Doe",
  "given_name": "John",
  "family_name": "Doe",
  "picture": "https://lh3.googleusercontent.com/a/ALm5wu32meinHR2EEwUYP1qnwbfH8N2cfy_qHm-=s96-c",
  "locale": "en-GB"
}

So, in order to login the user, what you need to do is:

  1. Pass the access_token to your backend
  2. Make the request using the URL shown above and get the JSON response
  3. Retrieve the email ID from the user and create the user session

Different Styles Available For The Login Button

From the Google Docs, you can tell that they are not really a big fan of you making a custom Login Button. So, they have provided many different ways in which you can style the login button that is rendered on the screen. Below is a screenshot of some of them:

many different button options for the look of the sign in with google button

Below is the code for creating all of the above buttons:

  <GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  shape="rectangular"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  shape="pill"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  shape="square"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  type="icon"

  shape="square"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  type="icon"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  size="small"

  shape="square"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="outline"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="filled_blue"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="filled_black"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="filled_black"

  text="continue_with"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="filled_black"

  text="signin_with"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

<div className='spacer'></div>

<GoogleLogin

  onSuccess={credentialResponse => {
    console.log(credentialResponse);
  }}

  theme="filled_black"

  text="signup_with"

  shape="circle"

  onError={() => {
    console.log('Login Failed');
  }}

/>

Basically, there are many properties you can play with for styling the button. Below are all of them with the possible options.

Property Possible values
type standard | icon
theme outline | filled_blue | filled_black
size large | medium | small
text signin_with | signup_with | continue_with | signin
shape rectangular | pill | circle | square
logo_alignment left | center
width string
locale string

The locale one is the only one that needs more info. You can find it here.

Conclusion

I hope I have comprehensively covered everything you need to know for setting up sign in with google in a React JS project.