DEV Community

Cover image for Create a Google Login Button With No Dependencies in React / Next.js
Jay @ Designly
Jay @ Designly

Posted on • Edited on • Originally published at blog.designly.biz

Create a Google Login Button With No Dependencies in React / Next.js

When developing applications, it's important to consider the number of dependencies your project relies on. While dependencies can often make development easier and faster, they can also introduce security vulnerabilities, compatibility issues, and increase the overall complexity of your codebase. That's why it's always a good idea to minimize the number of dependencies you use in your project whenever possible.

In this article, we'll be exploring how to create a Google login button in React without relying on any external dependencies. By leveraging the power of Next.js and React.js, we'll be able to create a simple, yet effective authentication flow that allows users to log in to your application using their Google account. So, let's dive in and see how we can achieve this with minimal dependencies.

Login Button Component

Here's our nice reusable GoogleLoginButton component:

import React, { useEffect } from "react";
import Script from "next/script";
import { useRouter } from "next/router";

export default function GoogleLoginBtn() {
  const router = useRouter();

  // Google will pass the login credential to this handler
  const handleGoogle = async (response) => {
    try {
      const result = await auth.handleGoogle({
        credential: response.credential,
        endpoint: '/path/to/auth/handler',
      });
      if (result) {
        router.push("/dashboard");
      } else {
        alert("Login failed");
      }
    } catch (err) {
      alert(`Error: ${err.message}`);
    }
  };

  useEffect(() => {
    // We check every 300ms to see if google client is loaded
    const interval = setInterval(() => {
      if (window.google) {
        clearInterval(interval);
        google.accounts.id.initialize({
          client_id: process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID, // Your client ID from Google Cloud
          callback: handleGoogle, // Handler to process login token
        });

        // Render the Google button
        google.accounts.id.renderButton(
          document.getElementById("google-login-btn"),
          {
            type: "standard",
            theme: "filled_blue",
            size: "large",
            text: "signin_with",
            shape: "rectangular",
          }
        );

        google.accounts.id.prompt();
      }
    }, 300);
  }, []); //eslint-disable-line

  return (
    <>
      <Script src="https://accounts.google.com/gsi/client" async defer></Script>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

We set up a useEffect() to check for the readiness of the Google client every 300ms. Next, we dynamically load the Google Client SKD. Once the user logs in, we pass the credential to handleGoogle(), which fires off a request to our API.


Handling the Back-End

Here's a short example of how you might implement this on the back-end:

export async function handler(req, res) {
  const endpoint = "https://oauth2.googleapis.com/tokeninfo";

  // Send request to Google to authenticate token
  try {
    const result = await fetch(`${endpoint}?id_token=${req.body.credential}`);
    if (!result.ok) {
      throw new Error("Failed to authenticate token");
    }
    const jwt = await result.json();
    /**
     * TODO: Code to look up user in DB by jwt.email
     */
  } catch (err) {
    return res.status(500).text(err.message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Pretty easy, huh? Google's tokeninfo endpoint will return the following JSON Web Token:

{
 // These six fields are included in all Google ID Tokens.
 "iss": "https://accounts.google.com",
 "sub": "110169484474386276334",
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "iat": "1433978353",
 "exp": "1433981953",

 // These seven fields are only included when the user has granted the "profile" and
 // "email" OAuth scopes to the application.
 "email": "testuser@gmail.com",
 "email_verified": "true",
 "name" : "Test User",
 "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
 "given_name": "Test",
 "family_name": "User",
 "locale": "en"
}
Enter fullscreen mode Exit fullscreen mode

You can use these fields however you like to link the Google user to the local user in your database.


Conclusion

By creating your own components like this, you eliminate the need for so many dependencies, and also potentially hundred, or even thousands, of lines of unnecessary code.

A couple of caveats:

  1. You will be responsible for making sure your site is using the current version of the Google Client SDK and adhering to best practices. But in reality, I would rather be responsible for this than depend on some package maintainer that may or may not update their code.
  2. You should check out Google's Branding Guidelines before implementing this into your design. Google does not allow certain design features of their own SDK button anymore, which I found out the hard way.

Further Reading:


Thank you for taking the time to read my article and I hope you found it useful (or at the very least, mildly entertaining). For more great information about web dev, systems administration and cloud computing, please read the Designly Blog. Also, please leave your comments! I love to hear thoughts from my readers.

I use Hostinger to host my clients' websites. You can get a business account that can host 100 websites at a price of $3.99/mo, which you can lock in for up to 48 months! It's the best deal in town. Services include PHP hosting (with extensions), MySQL, Wordpress and Email services.

Looking for a web developer? I'm available for hire! To inquire, please fill out a contact form.

Top comments (1)

Collapse
 
logemann profile image
Marc Logemann

How about putting a DIV with the ID "google-login-btn" under your Script component. This way your code does in fact work. Or where do you create the element with the mentioned ID?