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
After login (if needed) you should see the following screen..
Fill 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..
On the left, using the “burger” menu, go to “APIs and services” and then “Credentials” like so…
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.
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.
When 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:
Select the correct application type: “Web application”
Fill out the form to create the credentials. Make sure you put in both localhost & localhost:3000
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.
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.
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):
- Tap 1: Click on the “log in with google” button
- 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:
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.
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:
- Pass the access_token to your backend
- Make the request using the URL shown above and get the JSON response
- 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:
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.