How to Set Up the New Google Auth in a React and Express App

In this article, you will learn how to configure the new Google Authentication “Sign in with Google” button in React.js and Express.js applications.

This new method simplifies the way developers can implement Google Authentication. This brings some important benefits, such as allowing users to view a profile picture to select the correct Google Account – which prevents sign-up mistakes and ensures that Google will be able to use the old one when it closes. Your application will not be affected.sign in with googleJavascript Library on March 31, 2023.

It is worth noting that the newly created Client IDs are now blocked from using the old ones. platform library And that Google Authentication should be implemented in this manner.

Here is the source code of this article: Server And Customer,

Generate a Google Client ID and Secret

The first step to implement Google Authentication is to create a client ID and secret for the application you are building.

step 1

let’s start by heading towards google console,

step 2

Click on the dropdown highlighted above. Then, click on the new project highlighted below.

add new project

step 3

Add a project name. i chose connect-google-auth-article,

add project name

step 4

Click on the dropdown in step 1 to select the project.

choose project

step 5

The next screen you see should look like the sample below. Then click on Dashboard.

ex dashboard

step 6

The next step is to configure OAuth consent. To achieve this, hover over “APIs and Services” and click on “OAuth Consent Screen”.

pre enable

step 7

Select the type of consent you want. I chose outside and hit to create,

concentration

step 8

Once the consent is set, click on Credentials to set up your app details. Since my app is hosted on localhost, I set the details as in the picture below.

Application Type, Web Application;  name, connect-google-article-article;  URI1, http://localhost;  URI2, http://localhost:3000;

Note: When you are ready to deploy your application, you should replace URI1 and URI2 with the domain name you want to use — eg https://example.com,

step 9

Once your credentials have been successfully stored, you can copy or download the generated Client ID and Secret.

Oath

setup react app

The easiest way to bootstrap a React.js app is using Create React App,

So, create a folder, name it whatever you want. Then open a terminal and run the following code: npx create-react-app app,

express server installation

Create another folder in the root directory. I’m naming myself server, Then, open terminal and cd into the server: cd server,

then make a server.js before generating the file a package.json driving npm init -y, Next, install the following packages:

  • Express.js: “A minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications”.
  • CORS: A Node.js package for providing a Connect/Express middleware that can be used to enable cross-origin resource sharing with various options.
  • Dotenv: A Node.js package that loads environment variables .env file.
  • google-authentication-library: Google API’s authentication client library for Node.js.
  • Jsonwebtoken: A JSON web token implementation library for Node.js.
  • Nodemon: A simple monitor script for use during Node.js app development.

You can install the packages above by running the following command:

npm install express cors dotenv google-auth-library jsonwebtoken nodemon

Then, configure your script by doing this:


  "scripts": 
    "start": "node server.js",
    "dev": "nodemon server.js"
  ,

Your package.json Should look like this:



  "name": "connect-google-auth-article",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": 
    "start": "node server.js",
    "dev": "nodemon server.js"
  ,
  "dependencies": 
    "cors": "^2.8.5",
    "dotenv": "^16.0.2",
    "express": "^4.18.1",
    "google-auth-library": "^8.5.2",
    "jsonwebtoken": "^8.5.1",
    "nodemon": "^2.0.20"
  ,
  "keywords": [],
  "author": "",
  "license": "ISC"

then write the below code in it server.js and run npm run dev To start your server:


const express = require("express");
const app = express();
require("dotenv/config"); 
const cors = require("cors");
const  OAuth2Client  = require("google-auth-library");
const jwt = require("jsonwebtoken");

app.use(
  cors(
    origin: ["http://localhost:3000"],
    methods: "GET,POST,PUT,DELETE,OPTIONS",
  )
);
app.use(express.json());

let DB = [];

app.listen("5152", () => console.log("Server running on port 5152"));

Creating a React App

To prepare our client app, we’ll add Google Script to the top of our public/index.html file:


  <script src="https://accounts.google.com/gsi/client" async defer>script>

Our index.html The file should look like this:


DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    
    <script src="https://accounts.google.com/gsi/client" async defer>script>
    <title>React Apptitle>
  head>
  <body>
    <noscript>You need to enable JavaScript to run this app.noscript>
    <div id="root">div>
  body>
html>

Next, we will create two folders in our folder src, screens And hooks,
screens The folder will contain five files: Home.jsx, Landing.jsx, Login.jsx, Signup.jsx And index.js, hooks There will be only one file in the folder: useFetch.jsx,

Configure client-side routing

The package we will be leveraging for client-side routing is react-router-dom, Open a new terminal, cd into the app, and run the following code: npm install react-router-dom,

then we can update our App.js To look like this:


import React,  useEffect  from "react";
import  useState  from "react";
import  BrowserRouter, Routes, Route, Navigate  from "react-router-dom";

const App = () => 
  const [user, setUser] = useState();

  return (
    <BrowserRouter>
      <Routes>

      </Routes>
    </BrowserRouter>
  );
;

export default App;

create landing page

In our case the landing page is the only page available to an unauthenticated user. It will contain links to the sign-up and login pages. It would look like this:


import React from "react";
import  Link  from "react-router-dom";

const Landing = () => 
  return (
    <>
      <header style= textAlign: "center" >
        <h1>Welcome to my world</h1>
      </header>
      <main style= display: "flex", justifyContent: "center", gap: "2rem" >
        <Link
          to="/signup"
          style=
            textDecoration: "none",
            border: "1px solid gray",
            padding: "0.5rem 1rem",
            backgroundColor: "wheat",
            color: "#333",
          
        >
          Sign Up
        </Link>
        <Link
          to="/login"
          style=
            textDecoration: "none",
            border: "1px solid gray",
            padding: "0.5rem 1rem",
            backgroundColor: "whitesmoke",
            color: "#333",
          
        >
          Login
        </Link>
      </main>
    </>
  );
;

export default Landing;

Let’s break it down:

  • The component returns the React fragment element represented by an empty tag.
  • The fragment consists of two elements:
    And
    , returns the header

    and centers the text in it, while returning two links from the head element react-router-dom And also focuses them.

  • To improve the UX a different background color has been provided for the two links.

Next, we can open screens/index.js File & Export Landing.jsx hence:


export  default as Landing  from "./Landing";

After that, we can import it into App.js file, where we configure a route for:


import   Landing  from "./screens";

Too:


<Route
  path="
  element=user?.email ? <Navigate to="/home" /> : <Landing />
  />

create a usefetch hook

a hook in React is a special type of function that allows you to use the functionality of React. To make a hook, open hooks/useFetch.jsx and add the following code:


import  useState  from "react";

const useFetch = (url) => 
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const handleGoogle = async (response) => 
    console.log(response)
  ;
  return  loading, error, handleGoogle ;
;

export default useFetch;

create a sign-up page

open screens/Signup.jsx file and add the following code:


import React,  useEffect  from "react";
import  Link  from "react-router-dom";
import useFetch from "../hooks/useFetch";



const SignUp = () => 
  const  handleGoogle, loading, error  = useFetch(
    "
  );

  useEffect(() => 
    
    if (window.google) 
      google.accounts.id.initialize(
        client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
        callback: handleGoogle,
      );

      google.accounts.id.renderButton(document.getElementById("signUpDiv"), 
        
        theme: "filled_black",
        
        text: "continue_with",
        shape: "pill",
      );

      
    
  , [handleGoogle]);

  return (
    <>
      <nav style= padding: "2rem" >
        <Link to=">Go Back</Link>
      </nav>
      <header style= textAlign: "center" >
        <h1>Register to continue</h1>
      </header>
      <main
        style=
          display: "flex",
          justifyContent: "center",
          flexDirection: "column",
          alignItems: "center",
        
      >
        error && <p style= color: "red" >error</p>
        loading ? (
          <div>Loading....</div>
        ) : (
          <div id="signUpDiv" data-text="signup_with"></div>
        )
      </main>
      <footer></footer>
    </>
  );
;

export default SignUp;

Let’s break it down:

  • We extract from the available states and tasks useFetch hook. We also pass the URL that we’ll call the server to handle our sign-on.
  • In useEffectWe check the availability of Google’s script — controlled by the script we put public.index.html file.
  • we then use initialize Method available in the script to handle the functionality of the authentication button.
  • We also pass a callback function which we have already defined in useFetch hook.

Next, we’ll use renderButton Method to display our authentication button on the screen. The first parameter we pass is the element in which the button will be embedded using getElementById way. The next parameters that we can pass are used to customize the look of the button. It has the following required settings:

  • type: It accepts two values ​​- standard and sign.

In addition, it has optional settings including the following:

  • theme: Button theme. It can accept any one of the following: filled_blue, outlineAnd filled_black,
  • size: Defines the size of the button. it accepts large, mediumAnd small,
  • text: Defines the button text. It accepts one of the following: signin_with, signup_with, continue_withAnd signin,
  • shape: Defines the size of the button. it accepts rectangular, pill, circleeither square,
  • logo_alignment: Defines how the logo will be placed in the button. it can accept left either center,
  • width: Defines the width of the button. Worth noting that the max width is 400.

there is another option localeWhich is used to set it for a specific language.

We also check for the availability of the error and display it to the user. We also check the loading status.

create login page

The login page is similar to the sign-up page. Only difference is in server url and button text. The code should look like this:


import React,  useEffect  from "react";
import  Link  from "react-router-dom";
import useFetch from "../hooks/useFetch";



const Login = () => 
  const  handleGoogle, loading, error  = useFetch(
    "
  );

  useEffect(() => 
    
    if (window.google) 
      google.accounts.id.initialize(
        client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
        callback: handleGoogle,
      );

      google.accounts.id.renderButton(document.getElementById("loginDiv"), 
        
        theme: "filled_black",
        
        text: "signin_with",
        shape: "pill",
      );

      
    
  , [handleGoogle]);

  return (
    <>
      <nav style= padding: "2rem" >
        <Link to=">Go Back</Link>
      </nav>
      <header style= textAlign: "center" >
        <h1>Login to continue</h1>
      </header>
      <main
        style=
          display: "flex",
          justifyContent: "center",
          flexDirection: "column",
          alignItems: "center",
        
      >
        error && <p style= color: "red" >error</p>
        loading ? <div>Loading....</div> : <div id="loginDiv"></div>
      </main>
      <footer></footer>
    </>
  );
;

export default Login;

pay attention google.accounts.id.prompt() Used to automatically ask the user to sign in, as soon as they open your web page. This can be placed in the routes file or login page.

make a too .env.local file in the root folder and add the following:

REACT_APP_GOOGLE_CLIENT_ID=your client id

Next, we export the sign-up and login page screens.index.js file:


export  default as Login  from "./Login";
export  default as Signup  from "./SignUp";

After that, we configure their routes in App.js file:


import   Landing, Login, Signup  from "./screens";

Too:


<Route
    path="/signup"
    element=user?.email ? <Navigate to="/home" /> : <Signup />
  />
  <Route
    path="/login"
    element=user?.email ? <Navigate to="/home" /> : <Login />
  />

updating usefetch

Google Authentication responds with JWT credentials. However, we will make a subsequent call to the server to verify its authenticity and create a session for the user. we should update our hooks/useFetch file to look like this:


  const handleGoogle = async (response) => 
    setLoading(true);
    fetch(url, 
      method: "POST",
      headers: 
        "Content-Type": "application/json",
      ,

      body: JSON.stringify( credential: response.credential ),
    )
      .then((res) => 
        setLoading(false);

        return res.json();
      )
      .then((data) => )
      .catch((error) => 
        setError(error?.message);
      );
  ;

Let’s break it down:

  • Our callback function accepts parameters from Google Authentication passed in as the response.
  • we then use fetch to request the server.
  • When we get the appropriate response, we store the user localStorage in JSON format.

Creating signup and login routes

open server.js file. First, we’ll create a function that will validate the credentials we receive:



const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const client = new OAuth2Client(GOOGLE_CLIENT_ID);

async function verifyGoogleToken(token) 
  try 
    const ticket = await client.verifyIdToken(
      idToken: token,
      audience: GOOGLE_CLIENT_ID,
    );
    return  payload: ticket.getPayload() ;
   catch (error) 
    return  error: "Invalid user detected. Please try again" ;
  

make .env file in the root folder of the server and add the following:

# .env
GOOGLE_CLIENT_ID=your client id
JWT_SECRET=mySecret

Next, create the sign-up route:


app.post("/signup", async (req, res) => {
  try {
    
    if (req.body.credential) 
      const verificationResponse = await verifyGoogleToken(req.body.credential);

      if (verificationResponse.error) 
        return res.status(400).json(
          message: verificationResponse.error,
        );
      

      const profile = verificationResponse?.payload;

      DB.push(profile);

      res.status(201).json(
        message: "Signup was successful",
        user: 
          firstName: profile?.given_name,
          lastName: profile?.family_name,
          picture: profile?.picture,
          email: profile?.email,
          token: jwt.sign( email: profile?.email , "myScret", 
            expiresIn: "1d",
          ),
        ,
      );
    
  } catch (error) 
    res.status(500).json(
      message: "An error occurred. Registration failed.",
    );
  
});

Also create login routes:


app.post("/login", async (req, res) => {
  try {
    if (req.body.credential) 
      const verificationResponse = await verifyGoogleToken(req.body.credential);
      if (verificationResponse.error) 
        return res.status(400).json(
          message: verificationResponse.error,
        );
      

      const profile = verificationResponse?.payload;

      const existsInDB = DB.find((person) => person?.email === profile?.email);

      if (!existsInDB) 
        return res.status(400).json(
          message: "You are not registered. Please sign up",
        );
      

      res.status(201).json(
        message: "Login was successful",
        user: 
          firstName: profile?.given_name,
          lastName: profile?.family_name,
          picture: profile?.picture,
          email: profile?.email,
          token: jwt.sign( email: profile?.email , process.env.JWT_SECRET, 
            expiresIn: "1d",
          ),
        ,
      );
    
  } catch (error) 
    res.status(500).json(
      message: error?.message );
  
});

Let’s break it down:

  • In routes, we first check that the credentials are passed in the body. Then we try to verify the credentials. If an error occurs, we send it back to the client in JSON format.
  • In the sign-up route, we store the users’ profile in a DB array and send a success response to an email signed with a JWT as a token.
  • In the login route, we check if the user exists in the DB and if not, throw an error. If it exists, we also send a success response with the JWT signed email as a token along with other parameters.

Updating App.js

In App.js In the client app, we’ll update the file to check for the user local storage with the following code:


 useEffect(() => 
    const theUser = localStorage.getItem("user");

    if (theUser && !theUser.includes("undefined")) 
      setUser(JSON.parse(theUser));
    
  , []);

Creating Home.jsx

Home.jsx The file is the page that will be available to the user after a successful signup or login:


import React from "react";

const Home = ( user ) => 
  const logout = () => 
    localStorage.removeItem("user");
    window.location.reload();
  ;
  return (
    <div style= textAlign: "center", margin: "3rem" >
      <h1>Dear user?.email</h1>

      <p>
        You are viewing this page because you are logged in or you just signed
        up
      </p>

      <div>
        <button
          onClick=logout
          style=
            color: "red",
            border: "1px solid gray",
            backgroundColor: "white",
            padding: "0.5rem 1rem",
            cursor: "pointer",
          
        >
          Logout
        </button>
      </div>
    </div>
  );
;

export default Home;

Next, we’ll export it from screens/index.js The file is as follows:

export  default as Home  from "./Home";

After that, we’ll import and set up its routes App.js,

import  Home, Landing, Login, Signup  from "./screens";

Too:

<Route
    path="/home"
    element=user?.email ? <Home user=user /> : <Navigate to=" />
  />

conclusion

Congratulations! We have set up the new Google Authentication.

Once again, the source code is available here: Server And Customer,

Related Reading:

Leave a Reply