{"id":10036,"date":"2023-01-20T14:38:38","date_gmt":"2023-01-20T14:38:38","guid":{"rendered":"https:\/\/codevoweb.com\/?p=10036"},"modified":"2023-05-06T09:15:42","modified_gmt":"2023-05-06T09:15:42","slug":"how-to-implement-google-oauth2-in-reactjs","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/how-to-implement-google-oauth2-in-reactjs\/","title":{"rendered":"How to Implement Google OAuth2 in React.js"},"content":{"rendered":"\n<p>In this article, you&#8217;ll learn how to implement Google OAuth2 in a React.js application, including creating a project in the <a href=\"https:\/\/console.developers.google.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Google API Console<\/a>, configuring the application&#8217;s client ID and redirect URI, and implementing the necessary code in the React application.<\/p>\n\n\n\n<p>Why should you add Google OAuth2 to your React app? OAuth2 is an open standard for authorization that enables third-party applications to obtain limited access to a user&#8217;s resources without sharing their credentials. With Google OAuth2, users can securely log into the React application using their Google account without sharing their password.<\/p>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<p>Related articles:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/how-to-implement-github-oauth-in-reactjs\">How to Implement GitHub OAuth in React.js<\/a><\/li>\n\n\n\n<li><a href=\"\/github-oauth-authentication-vue-and-node\">GitHub OAuth Authentication Vue.js and Node.js (No Passport)<\/a><\/li>\n\n\n\n<li><a href=\"\/google-oauth-authentication-react-and-node\">Google OAuth Authentication React.js and Node.js(No Passport)<\/a><\/li>\n\n\n\n<li><a href=\"\/github-oauth-authentication-react-and-node\">GitHub OAuth Authentication React.js and Node.js(No Passport)<\/a><\/li>\n\n\n\n<li><a href=\"\/github-oauth-authentication-vue-mongodb-and-golang\">GitHub OAuth Authentication Vuejs, MongoDB and Golang<\/a><\/li>\n\n\n\n<li><a href=\"\/google-oauth-authentication-react-mongodb-and-golang\">Google OAuth Authentication React.js, MongoDB and Golang<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"478\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js.webp\" alt=\"How to Implement Google OAuth2 in React.js\" class=\"wp-image-10088\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js.webp 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js-300x169.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js-768x432.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js-100x56.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-Google-OAuth2-in-React.js-700x394.webp 700w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-content-wrap{padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-contents-title-wrap{padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-content-wrap .kb-table-of-content-list{color:#ffffff;font-weight:regular;font-style:normal;margin-top:10px;margin-right:0px;margin-bottom:0px;margin-left:-5px;}@media all and (max-width: 1024px){.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}@media all and (max-width: 767px){.kb-table-of-content-nav.kb-table-of-content-id_b2a316-1a .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}<\/style>\n\n\n<h2 class=\"wp-block-heading\">Run the React Google OAuth2 Project<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Download or clone the React Google OAuth2 project from <a href=\"https:\/\/github.com\/wpcodevo\/google-github-oath2-reactjs\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/wpcodevo\/google-github-oath2-reactjs<\/a> and open the source code in an IDE or text editor.<\/li>\n\n\n\n<li>Open the integrated terminal in your IDE and run <code>yarn<\/code> or <code>yarn install<\/code> to install the project&#8217;s dependencies.<\/li>\n\n\n\n<li>Duplicate the <code>example.env<\/code> file and rename the duplicated one to <code>.env.local<\/code> .<\/li>\n\n\n\n<li>Follow the &#8220;Get the Google OAuth2 Client ID and Secret&#8221; section to obtain the OAuth2 client ID and secret from the Google developer Console API.<\/li>\n\n\n\n<li>Add the OAuth2 client ID and secret to the <code>.env.local<\/code> file.<\/li>\n\n\n\n<li>Start the Vite development server by running <code>yarn dev<\/code> .<\/li>\n\n\n\n<li>Set up the backend API to process the request when the Google OAuth2 API redirects the user to the authorised redirect URI. <\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Run the React app with a Node.js API<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Ensure you have <a href=\"https:\/\/nodejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Node.js<\/a> and <a href=\"https:\/\/classic.yarnpkg.com\/lang\/en\/docs\/install\/\" target=\"_blank\" rel=\"noreferrer noopener\">Yarn<\/a> installed on your system.<\/li>\n\n\n\n<li>Download or clone the Node.js Google OAuth2 project from <a href=\"https:\/\/github.com\/wpcodevo\/google-github-oauth2-nodejs\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/wpcodevo\/google-github-oauth2-nodejs<\/a> and open the source code in an IDE.<\/li>\n\n\n\n<li>Open the integrated terminal and change the Git branch to <code>google-oauth2-nodejs<\/code> . Feel free to use the master branch.<\/li>\n\n\n\n<li>Run <code>yarn<\/code> or <code>yarn install<\/code> to install all the required dependencies.<\/li>\n\n\n\n<li>Duplicate the <code>example.env<\/code> file and rename the copied one to <code>.env<\/code> .<\/li>\n\n\n\n<li>Add the Google OAuth2 client ID and secret to the <code>.env<\/code> file.<\/li>\n\n\n\n<li>Run <code>npx prisma db push<\/code> to push the Prisma schema to the SQLite database.<\/li>\n\n\n\n<li>Start the Express server by running <code>yarn start<\/code> in the console of the root directory.<\/li>\n\n\n\n<li>Interact with the Node.js API from the React app.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Run the React app with a Golang API<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure you have the latest version of <a href=\"https:\/\/go.dev\/doc\/install\" target=\"_blank\" rel=\"noreferrer noopener\">Golang installed<\/a> on your system.<\/li>\n\n\n\n<li>Download or clone the Golang Google OAuth2 project from <a href=\"https:\/\/github.com\/wpcodevo\/google-github-oauth2-golang\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/wpcodevo\/google-github-oauth2-golang<\/a> and open the source code in a code editor.<\/li>\n\n\n\n<li>Change the Git branch to <code>google-oauth2-golang<\/code> . Feel free to use the master branch instead.<\/li>\n\n\n\n<li>Duplicate the <code>example.env<\/code> file and rename the copied one to <code>app.env<\/code> .<\/li>\n\n\n\n<li>Add the Google OAuth2 client ID and secret to the <code>app.env<\/code> file.<\/li>\n\n\n\n<li>Run <code>go run main.go<\/code> to install the required dependencies and start the Gin HTTP server.<\/li>\n\n\n\n<li>Interact with the Golang API from the React app.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Setup the React Project<\/h2>\n\n\n\n<p>To begin, navigate to the location where you would like to create the project and run the command below.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpm create vite@latest google-oauth2-reactjs -- --template react\n# or\nyarn create vite google-oauth2-reactjs -- --template react\n<\/code>\n<\/pre>\n\n\n\n<p>This will generate a React.js boilerplate project with the Vite scaffolding tool and output the files into the <code>google-oauth2-reactjs<\/code> folder. Feel free to give the project another name.<\/p>\n\n\n\n<p>Once that is done, change into the newly-created project folder, and run <code>yarn<\/code> or <code>npm install<\/code> to install the necessary dependencies. After that, open the project folder in a code editor.<\/p>\n\n\n\n<p>Install Tailwind CSS and its peer dependencies with the command below:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpm install -D tailwindcss postcss autoprefixer\n# or\nyarn add -D tailwindcss postcss autoprefixer\n<\/code>\n<\/pre>\n\n\n\n<p>Run the Tailwind CSS <strong>init<\/strong> command to generate the <code>tailwind.config.cjs<\/code>&nbsp;and&nbsp;<code>postcss.config.cjs<\/code> files.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpx tailwindcss init -p\n<\/code>\n<\/pre>\n\n\n\n<p>Open the <code>tailwind.config.cjs<\/code> file and replace its content with the following Tailwind CSS configurations.<\/p>\n\n\n\n<p><strong>tailwind.config.cjs<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\n\/** @type {import('tailwindcss').Config} *\/\nmodule.exports = {\n  content: [\n    \".\/index.html\",\n    \".\/src\/**\/*.{vue,js,ts,jsx,tsx}\",\n  ],\n  theme: {\n    extend: {\n      colors: {\n        'ct-dark-600': '#222',\n        'ct-dark-200': '#e5e7eb',\n        'ct-dark-100': '#f5f6f7',\n        'ct-blue-600': '#2363eb',\n        'ct-yellow-600': '#f9d13e',\n      },\n      fontFamily: {\n        Poppins: ['Poppins, sans-serif'],\n      },\n      container: {\n        center: true,\n        padding: '1rem',\n        screens: {\n          lg: '1125px',\n          xl: '1125px',\n          '2xl': '1125px',\n        },\n      },\n    },\n  },\n  plugins: [],\n};\n\n<\/code>\n<\/pre>\n\n\n\n<p>Next, open the <code>src\/index.css<\/code> file and replace its content with the following CSS and Tailwind CSS directives.<\/p>\n\n\n\n<p><strong>src\/index.css<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-css\"><code>\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Poppins:wght@300;400;500;600;700&amp;display=swap');\n\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml{\n    font-family: 'Poppins', sans-serif;\n}\n<\/code>\n<\/pre>\n\n\n\n<p>Now let&#8217;s install the dependencies we&#8217;ll need for the project:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add react-hook-form @hookform\/resolvers react-router-dom react-toastify tailwind-merge zod zustand\n# or \nnpm install react-hook-form @hookform\/resolvers react-router-dom react-toastify tailwind-merge zod zustand\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-hook-form\" target=\"_blank\" rel=\"noreferrer noopener\">react-hook-form<\/a><\/code> &#8211; Form validation library for React<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/@hookform\/resolvers\" target=\"_blank\" rel=\"noreferrer noopener\">@hookform\/resolvers<\/a><\/code> &#8211; React-Hook-Form validation resolver<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-router-dom\" target=\"_blank\" rel=\"noreferrer noopener\">react-router-dom<\/a><\/code> &#8211; Provides routing for React web applications<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-toastify\" target=\"_blank\" rel=\"noreferrer noopener\">react-toastify<\/a><\/code> &#8211; Display alert notifications in React<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/tailwind-merge\" target=\"_blank\" rel=\"noreferrer noopener\">tailwind-merge<\/a><\/code> &#8211; Merge Tailwind CSS classes without style conflicts<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/zod\" target=\"_blank\" rel=\"noreferrer noopener\">zod<\/a><\/code> &#8211; A TypeScript-first schema validation library<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/zustand\" target=\"_blank\" rel=\"noreferrer noopener\">zustand<\/a><\/code> &#8211; A state manager for React<\/li>\n<\/ul>\n\n\n\n<p>After the installation is complete, open the <code>package.json<\/code> file and replace the <code>dev<\/code> script with <code>\"dev\": \"vite --port 3000\"<\/code> . This will start the Vite development server on port <strong>3000<\/strong> instead of the default port <strong>5173<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Get the Google OAuth2 Client ID and Secret<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Make sure you&#8217;ve already logged into your Google account and go to the <a href=\"https:\/\/console.developers.google.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Google API Console<\/a>.<\/li>\n\n\n\n<li>Click on the dropdown adjacent to the Google Cloud logo to display a popup where you can select an existing project or create a new one.<br><img loading=\"lazy\" decoding=\"async\" width=\"840\" height=\"671\" class=\"wp-image-10054\" style=\"width: 840px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard.webp\" alt=\"select a project or create a new one on the Google Cloud API dashboard\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard.webp 840w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard-300x240.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard-768x613.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard-100x80.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-a-project-or-create-a-new-one-on-the-Google-Cloud-API-dashboard-563x450.webp 563w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><\/li>\n\n\n\n<li>To create a new project, click on the &#8220;<strong>New Project<\/strong>&#8221; button at the top-right corner of the popup. On the next page, provide the project name and click on the &#8220;<strong>Create<\/strong>&#8221; button.<br><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"691\" class=\"wp-image-10055\" style=\"width: 852px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard.webp\" alt=\"create a new project on the google console api dashboard\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard.webp 852w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard-300x243.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard-768x623.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard-100x81.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/create-a-new-project-on-the-google-console-api-dashboard-555x450.webp 555w\" sizes=\"auto, (max-width: 852px) 100vw, 852px\" \/><\/li>\n\n\n\n<li>Within a few milliseconds, the project will be created and a notification will be displayed for you to select the newly-created project.<br><img loading=\"lazy\" decoding=\"async\" width=\"690\" height=\"483\" class=\"wp-image-10056\" style=\"width: 690px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-newly-created-project-from-the-notification.webp\" alt=\"click on the newly created project from the notification\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-newly-created-project-from-the-notification.webp 690w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-newly-created-project-from-the-notification-300x210.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-newly-created-project-from-the-notification-100x70.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-newly-created-project-from-the-notification-643x450.webp 643w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><br>From the &#8220;<strong>Notifications<\/strong>&#8220;, click on the &#8220;<strong>SELECT PROJECT<\/strong>&#8221; button available on the newly-created project.<\/li>\n\n\n\n<li>In the left sidebar, click on the &#8220;<strong>OAuth consent screen<\/strong>&#8221; menu and select &#8220;<strong>External<\/strong>&#8221; under the &#8220;<strong>User Type<\/strong>&#8221; on the next page. <br><img loading=\"lazy\" decoding=\"async\" width=\"919\" height=\"669\" class=\"wp-image-10057\" style=\"width: 919px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create.webp\" alt=\"select external under the user type and click on create\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create.webp 919w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create-300x218.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create-768x559.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create-100x73.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-external-under-the-user-type-and-click-on-create-618x450.webp 618w\" sizes=\"auto, (max-width: 919px) 100vw, 919px\" \/><br>After that, click on the &#8220;<strong>Create<\/strong>&#8221; button.<\/li>\n\n\n\n<li>Under the &#8220;<strong>App information<\/strong>&#8221; on the &#8220;<strong>Edit app registration<\/strong>&#8221; screen, provide the required consent screen information.<br><img loading=\"lazy\" decoding=\"async\" width=\"951\" height=\"915\" class=\"wp-image-10059\" style=\"width: 951px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1.webp\" alt=\"provide the consent screen credentials part 1\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1.webp 951w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1-300x289.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1-768x739.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1-100x96.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-1-468x450.webp 468w\" sizes=\"auto, (max-width: 951px) 100vw, 951px\" \/><br>Scroll down to the &#8220;<strong>App domain<\/strong>&#8221; section, and provide the application links.<br><img loading=\"lazy\" decoding=\"async\" width=\"950\" height=\"977\" class=\"wp-image-10058\" style=\"width: 950px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2.webp\" alt=\"provide the consent screen credentials part 2\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2.webp 950w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2-292x300.webp 292w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2-768x790.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2-97x100.webp 97w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-consent-screen-credentials-part-2-438x450.webp 438w\" sizes=\"auto, (max-width: 950px) 100vw, 950px\" \/><br>Under the &#8220;<strong>Developer contact information<\/strong>&#8221; section, enter your email and click on the &#8220;<strong>SAVE AND CONTINUE<\/strong>&#8221; button.<\/li>\n\n\n\n<li>On the &#8220;<strong>Scopes<\/strong>&#8221; screen, click on the &#8220;<strong>ADD OR REMOVE SCOPES<\/strong>&#8221; button, select <code>...\/auth\/userinfo.email<\/code> and <code>...\/auth\/userinfo.profile<\/code> , and click on the &#8220;<strong>UPDATE<\/strong>&#8221; button at the bottom.<br><img loading=\"lazy\" decoding=\"async\" width=\"813\" height=\"432\" class=\"wp-image-10061\" style=\"width: 813px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes.webp\" alt=\"select the scopes\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes.webp 813w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes-300x159.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes-768x408.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes-100x53.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-the-scopes-700x372.webp 700w\" sizes=\"auto, (max-width: 813px) 100vw, 813px\" \/><br>On the &#8220;<strong>Scopes<\/strong>&#8221; screen, scroll down and click on the &#8220;<strong>SAVE AND CONTINUE<\/strong>&#8221; button.<\/li>\n\n\n\n<li>Click on the &#8220;<strong>ADD USERS<\/strong>&#8221; button on the &#8220;<strong>Test users<\/strong>&#8221; screen. Only the test users can log into your application while still in sandbox mode.<br><img loading=\"lazy\" decoding=\"async\" width=\"811\" height=\"357\" class=\"wp-image-10062\" style=\"width: 811px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user.webp\" alt=\"add the test user\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user.webp 811w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user-300x132.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user-768x338.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user-100x44.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/add-the-test-user-700x308.webp 700w\" sizes=\"auto, (max-width: 811px) 100vw, 811px\" \/><br>After adding the test users, click on the &#8220;<strong>SAVE AND CONTINUE<\/strong>&#8221; button and on the &#8220;<strong>Summary<\/strong>&#8221; screen, click on the &#8220;<strong>BACK TO DASHBOARD<\/strong>&#8221; button.<\/li>\n\n\n\n<li>Now that we&#8217;ve set up the consent screen, we can go ahead and create the credentials. In the left sidebar, click on the &#8220;<strong>Credentials<\/strong>&#8221; menu and click on the &#8220;<strong>CREATE CREDENTIALS<\/strong>&#8221; button. Select &#8220;<strong>OAuth client ID<\/strong>&#8221; from the options.<br><img loading=\"lazy\" decoding=\"async\" width=\"589\" height=\"363\" class=\"wp-image-10063\" style=\"width: 589px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-oauth-client-ID.webp\" alt=\"select oauth client ID\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-oauth-client-ID.webp 589w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-oauth-client-ID-300x185.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/select-oauth-client-ID-100x62.webp 100w\" sizes=\"auto, (max-width: 589px) 100vw, 589px\" \/><\/li>\n\n\n\n<li>On the &#8220;<strong>Create OAuth client ID<\/strong>&#8221; screen, select &#8220;<strong>Web application<\/strong>&#8221; as the Application type, enter the app name, and provide the authorized redirect URI.<br><img loading=\"lazy\" decoding=\"async\" width=\"1020\" height=\"956\" class=\"wp-image-10064\" style=\"width: 1020px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials.webp\" alt=\"provide the oauth credentials\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials.webp 1020w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials-300x281.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials-768x720.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials-100x94.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/provide-the-oauth-credentials-480x450.webp 480w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><br>Enter <code>http:\/\/localhost:8000\/api\/sessions\/oauth\/google<\/code> as the Authorised redirect URI and click on the &#8220;<strong>Create<\/strong>&#8221; button at the bottom.<\/li>\n\n\n\n<li>Create a <code>.env.local<\/code> file in the root directory, add the Client ID as <code>VITE_GOOGLE_OAUTH_CLIENT_ID<\/code>. Also, add the Client secret as <code>VITE_GOOGLE_OAUTH_CLIENT_SECRET<\/code> .<br><\/li>\n<\/ol>\n\n\n\n<p>In the end, your <code>.env.local<\/code> file should look somewhat like this:<\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nVITE_SERVER_ENDPOINT=http:\/\/localhost:8000\n\nVITE_GOOGLE_OAUTH_CLIENT_ID=\nVITE_GOOGLE_OAUTH_CLIENT_SECRET=\nVITE_GOOGLE_OAUTH_REDIRECT=http:\/\/localhost:8000\/api\/sessions\/oauth\/google\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Build the OAuth2 Consent Screen Link<\/h2>\n\n\n\n<p>Now that we&#8217;ve obtained the OAuth2 client ID and secret from the Google Console API, let&#8217;s create a helper function to generate the consent screen URL from them.<\/p>\n\n\n\n<p>To do this, create a <strong>utils<\/strong> folder in the <strong>src<\/strong> directory. Within the <code>src\/utils\/<\/code> folder, create a <code>getGoogleUrl.ts<\/code> file and add the following code.<\/p>\n\n\n\n<p><strong>src\/utils\/getGoogleUrl.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nexport const getGoogleUrl = (from: string) =&gt; {\n  const rootUrl = `https:\/\/accounts.google.com\/o\/oauth2\/v2\/auth`;\n\n  const options = {\n    redirect_uri: import.meta.env.VITE_GOOGLE_OAUTH_REDIRECT as string,\n    client_id: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_ID as string,\n    access_type: \"offline\",\n    response_type: \"code\",\n    prompt: \"consent\",\n    scope: [\n      \"https:\/\/www.googleapis.com\/auth\/userinfo.profile\",\n      \"https:\/\/www.googleapis.com\/auth\/userinfo.email\",\n    ].join(\" \"),\n    state: from,\n  };\n\n  const qs = new URLSearchParams(options);\n\n  return `${rootUrl}?${qs.toString()}`;\n};\n<\/code>\n<\/pre>\n\n\n\n<p>In the above code, we created a function to generate the consent screen URL based on the OAuth2 credentials.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>client_id<\/code> &#8211; Is the client ID of the application, which we obtained from the Google API Console.<\/li>\n\n\n\n<li><code>redirect_uri<\/code> &#8211; This is the URL the OAuth2 API will redirect the user to after the permission has been granted or denied. This URL must match one of the redirect URLs configured in the Google API Console.<\/li>\n\n\n\n<li><code>access_type: \"offline\"<\/code> &#8211; This indicates that the application needs to access the user&#8217;s data when the user is not present.<\/li>\n\n\n\n<li><code>scope<\/code> &#8211; This is a scope of permissions the application is requesting. In this project, we&#8217;ll only request access to the user&#8217;s email address and profile information.<\/li>\n\n\n\n<li><code>response_type: \"code\"<\/code> &#8211; This indicates that an authorization code will be returned on the query string.<\/li>\n\n\n\n<li><code>state<\/code> &#8211; This will allow us to pass data to the backend API. In this example, we&#8217;ll include a path in the <code>state<\/code> so that the backend API can redirect the user to that page after the authentication is successful.<\/li>\n\n\n\n<li><code>prompt: \"consent\"<\/code> &#8211; This indicates that the user should only see the consent screen the first time they grant permission to the application.<\/li>\n<\/ul>\n\n\n\n<p>With that out of the way, we can now evoke the <code>getGoogleUrl(\"\/profile\")<\/code> function in an <code>href<\/code> attribute of a link tag to redirect the user to the consent screen when the link is clicked. Don&#8217;t worry, we&#8217;ll later do this in the login component.<\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\n    &lt;a href={getGoogleUrl(from)}&gt;\n              &lt;img\n                className=\"pr-2\"\n                src={GoogleLogo}\n                alt=\"\"\n                style={{ height: \"2rem\" }}\n              \/&gt;\n              Continue with Google\n         &lt;\/a&gt;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create a Zustand Store<\/h2>\n\n\n\n<p>Now let&#8217;s set up a global state store using Zustand. This way, we can store the authenticated user&#8217;s credentials in the store and display them when needed.<\/p>\n\n\n\n<p>To begin, create a <strong>store<\/strong> folder in the <strong>src<\/strong> directory. In the <strong>store<\/strong> folder, create a <code>types.ts<\/code> file and add the type below. The <code>IUser<\/code> type will describe the structure of a user record that the backend API will return.<\/p>\n\n\n\n<p><strong>src\/store\/types.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nexport interface IUser {\n  id: string;\n  name: string;\n  email: string;\n  role: string;\n  photo: string;\n  provider: string;\n  verified: boolean;\n}\n\n<\/code>\n<\/pre>\n\n\n\n<p>Next, create an <code>index.ts<\/code> file in the <strong>store<\/strong> folder and add the code below:<\/p>\n\n\n\n<p><strong>src\/store\/index.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport { create } from \"zustand\";\nimport { IUser } from \".\/types\";\n\ntype Store = {\n  authUser: IUser | null;\n  requestLoading: boolean;\n  setAuthUser: (user: IUser | null) =&gt; void;\n  setRequestLoading: (isLoading: boolean) =&gt; void;\n};\n\nconst useStore = create&lt;Store&gt;((set) =&gt; ({\n  authUser: null,\n  requestLoading: false,\n  setAuthUser: (user) =&gt; set((state) =&gt; ({ ...state, authUser: user })),\n  setRequestLoading: (isLoading) =&gt;\n    set((state) =&gt; ({ ...state, requestLoading: isLoading })),\n}));\n\nexport default useStore;\n<\/code>\n<\/pre>\n\n\n\n<p>In the above file, we defined a type for the state and the actions that will be used to update the state. Then, we used the <code>create<\/code> method to create the store with some initial states and actions.<\/p>\n\n\n\n<p>Next, we exported the <code>useStore<\/code> hook from the file. The <code>useStore<\/code> hook will allow us to access and mutate the store from other React components.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create Reusable React Components<\/h2>\n\n\n\n<p>Before we can implement the Google OAuth2 flow in the React app, we first need to create some reusable components.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Spinner Component<\/h3>\n\n\n\n<p>The first component is a <strong>Spinner<\/strong> that will be displayed when a request is being processed by the backend API. Create a <code>src\/components\/Spinner.tsx<\/code> file and add the following TSX code:<\/p>\n\n\n\n<p><strong>src\/components\/Spinner.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React from 'react';\nimport { twMerge } from 'tailwind-merge';\ntype SpinnerProps = {\n  width?: number;\n  height?: number;\n  color?: string;\n  bgColor?: string;\n};\nconst Spinner: React.FC&lt;SpinnerProps&gt; = ({\n  width = 5,\n  height = 5,\n  color,\n  bgColor,\n}) =&gt; {\n  return (\n    &lt;svg\n      role='status'\n      className={twMerge(\n        'w-5 h-5 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600',\n        `w-${width} h-${height} ${color} ${bgColor}`\n      )}\n      viewBox='0 0 100 101'\n      fill='none'\n      xmlns='http:\/\/www.w3.org\/2000\/svg'\n    &gt;\n      &lt;path\n        d='M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z'\n        fill='currentColor'\n      \/&gt;\n      &lt;path\n        d='M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z'\n        fill='currentFill'\n      \/&gt;\n    &lt;\/svg&gt;\n  );\n};\n\nexport default Spinner;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Header Component<\/h3>\n\n\n\n<p>The second component will be the <strong>Header<\/strong> which will display a list of links for navigating through the different pages. Also, this component will hold the logic for logging out of the application.<\/p>\n\n\n\n<p>Create a <code>Header.tsx<\/code> file in the <strong>src\/components<\/strong> folder and add the following TSX code.<\/p>\n\n\n\n<p><strong>src\/components\/Header.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { Link, useNavigate } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\nimport useStore from \"..\/store\";\nimport Spinner from \".\/Spinner\";\n\nconst Header = () =&gt; {\n  const store = useStore();\n  const user = store.authUser;\n  const navigate = useNavigate();\n\n  const handleLogout = async () =&gt; {\n    try {\n      store.setRequestLoading(true);\n      const VITE_SERVER_ENDPOINT = import.meta.env.VITE_SERVER_ENDPOINT;\n      const response = await fetch(`${VITE_SERVER_ENDPOINT}\/api\/auth\/logout`, {\n        credentials: \"include\",\n      });\n      if (!response.ok) {\n        throw await response.json();\n      }\n\n      store.setRequestLoading(false);\n      store.setAuthUser(null);\n      navigate(\"\/login\");\n    } catch (error: any) {\n      store.setRequestLoading(false);\n      const resMessage =\n        (error.response &amp;&amp;\n          error.response.data &amp;&amp;\n          error.response.data.message) ||\n        error.message ||\n        error.toString();\n\n      if (error?.message === \"You are not logged in\") {\n        navigate(\"\/login\");\n      }\n\n      toast.error(resMessage, {\n        position: \"top-right\",\n      });\n    }\n  };\n\n  return (\n    &lt;&gt;\n      &lt;header className=\"bg-white h-20\"&gt;\n        &lt;nav className=\"h-full flex justify-between container items-center\"&gt;\n          &lt;div&gt;\n            &lt;Link to=\"\/\" className=\"text-ct-dark-600 text-2xl font-semibold\"&gt;\n              CodevoWeb\n            &lt;\/Link&gt;\n          &lt;\/div&gt;\n          &lt;ul className=\"flex items-center gap-4\"&gt;\n            &lt;li&gt;\n              &lt;Link to=\"\/\" className=\"text-ct-dark-600\"&gt;\n                Home\n              &lt;\/Link&gt;\n            &lt;\/li&gt;\n            {!user &amp;&amp; (\n              &lt;&gt;\n                &lt;li&gt;\n                  &lt;Link to=\"\/login\" className=\"text-ct-dark-600\"&gt;\n                    Login\n                  &lt;\/Link&gt;\n                &lt;\/li&gt;\n                &lt;li&gt;\n                  &lt;Link to=\"\/register\" className=\"text-ct-dark-600\"&gt;\n                    Register\n                  &lt;\/Link&gt;\n                &lt;\/li&gt;\n              &lt;\/&gt;\n            )}\n            {user &amp;&amp; (\n              &lt;&gt;\n                &lt;li&gt;\n                  &lt;Link to=\"\/profile\" className=\"text-ct-dark-600\"&gt;\n                    Profile\n                  &lt;\/Link&gt;\n                &lt;\/li&gt;\n                &lt;li className=\"cursor-pointer\" onClick={handleLogout}&gt;\n                  Logout\n                &lt;\/li&gt;\n              &lt;\/&gt;\n            )}\n          &lt;\/ul&gt;\n        &lt;\/nav&gt;\n      &lt;\/header&gt;\n      &lt;div className=\"pt-4 pl-2 bg-ct-blue-600 fixed\"&gt;\n        {store.requestLoading &amp;&amp; &lt;Spinner color=\"text-ct-yellow-600\" \/&gt;}\n      &lt;\/div&gt;\n    &lt;\/&gt;\n  );\n};\n\nexport default Header;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Layout Component<\/h3>\n\n\n\n<p>Instead of adding the <strong>Header<\/strong> component to every page that needs it, let&#8217;s use the power of <code>react-router-dom<\/code> to create a layout component that will render the pages below the Header component. To achieve this, we&#8217;ll leverage React-Router-Dom&#8217;s <code>Outlet<\/code> component.<\/p>\n\n\n\n<p>Create a <code>Layout.tsx<\/code> file in the <strong>src\/components<\/strong> directory and add the code below.<\/p>\n\n\n\n<p><strong>src\/components\/Layout.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { Outlet } from \"react-router-dom\";\nimport Header from \".\/Header\";\n\nconst Layout = () =&gt; {\n  return (\n    &lt;&gt;\n      &lt;Header \/&gt;\n      &lt;Outlet \/&gt;\n    &lt;\/&gt;\n  );\n};\n\nexport default Layout;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Implement the Authentication<\/h2>\n\n\n\n<p>At this point, we&#8217;re now ready to implement the JWT authentication and OAuth2 flow in the React project. To do that, we&#8217;ll create two routes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/register<\/code> &#8211; This route will display the account registration page where the user can create a new account using the email, password, and name.<\/li>\n\n\n\n<li><code>\/login<\/code> &#8211; This route will display the account login page where the user can either sign in with the OAuth2 option or email and password.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Account Registration Page<\/h3>\n\n\n\n<p>Here, you&#8217;ll create a React component that will have the logic for registering new users. To prevent the user from sending junk credentials to the backend, we&#8217;ll use the React-Hook-Form library along with the Zod schema validation library to validate the credentials before sending the request to the backend API.<\/p>\n\n\n\n<p><strong>src\/pages\/register.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useNavigate } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\nimport useStore from \"..\/store\";\nimport { object, string, TypeOf } from \"zod\";\nimport { useEffect } from \"react\";\nimport { useForm, SubmitHandler } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform\/resolvers\/zod\";\n\nconst registerSchema = object({\n  name: string().min(1, \"Full name is required\").max(100),\n  email: string()\n    .min(1, \"Email address is required\")\n    .email(\"Email Address is invalid\"),\n  password: string()\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n  passwordConfirm: string().min(1, \"Please confirm your password\"),\n}).refine((data) =&gt; data.password === data.passwordConfirm, {\n  path: [\"passwordConfirm\"],\n  message: \"Passwords do not match\",\n});\n\nexport type RegisterInput = TypeOf&lt;typeof registerSchema&gt;;\n\nconst RegisterPage = () =&gt; {\n  const navigate = useNavigate();\n  const store = useStore();\n\n  const registerUser = async (data: RegisterInput) =&gt; {\n    try {\n      store.setRequestLoading(true);\n      const VITE_SERVER_ENDPOINT = import.meta.env.VITE_SERVER_ENDPOINT;\n      const response = await fetch(\n        `${VITE_SERVER_ENDPOINT}\/api\/auth\/register`,\n        {\n          method: \"POST\",\n          credentials: \"include\",\n          body: JSON.stringify(data),\n          headers: {\n            \"Content-Type\": \"application\/json\",\n          },\n        }\n      );\n      if (!response.ok) {\n        throw await response.json();\n      }\n\n      toast.success(\"Account created successfully\", {\n        position: \"top-right\",\n      });\n      store.setRequestLoading(false);\n      navigate(\"\/login\");\n    } catch (error: any) {\n      store.setRequestLoading(false);\n      if (error.error) {\n        error.error.forEach((err: any) =&gt; {\n          toast.error(err.message, {\n            position: \"top-right\",\n          });\n        });\n        return;\n      }\n      const resMessage =\n        (error.response &amp;&amp;\n          error.response.data &amp;&amp;\n          error.response.data.message) ||\n        error.message ||\n        error.toString();\n\n      toast.error(resMessage, {\n        position: \"top-right\",\n      });\n    }\n  };\n\n  const methods = useForm&lt;RegisterInput&gt;({\n    resolver: zodResolver(registerSchema),\n  });\n\n  const {\n    reset,\n    handleSubmit,\n    register,\n    formState: { isSubmitSuccessful, errors },\n  } = methods;\n\n  useEffect(() =&gt; {\n    if (isSubmitSuccessful) {\n      reset();\n    }\n    \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, [isSubmitSuccessful]);\n\n  const onSubmitHandler: SubmitHandler&lt;RegisterInput&gt; = (values) =&gt; {\n    registerUser(values);\n  };\n\n  return (\n    &lt;section className=\"bg-ct-blue-600 min-h-screen pt-20\"&gt;\n      &lt;div className=\"container mx-auto px-6 py-12 h-full flex justify-center items-center\"&gt;\n        &lt;div className=\"md:w-8\/12 lg:w-5\/12 bg-white px-8 py-10\"&gt;\n          &lt;form onSubmit={handleSubmit(onSubmitHandler)}&gt;\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"text\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Name\"\n                {...register(\"name\")}\n              \/&gt;\n              {errors.name &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.name?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"email\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Email address\"\n                {...register(\"email\")}\n              \/&gt;\n              {errors.email &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.email?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"password\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Password\"\n                {...register(\"password\")}\n              \/&gt;\n              {errors.password &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.password?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"password\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Confirm Password\"\n                {...register(\"passwordConfirm\")}\n              \/&gt;\n              {errors.passwordConfirm &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.passwordConfirm?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n\n            &lt;button\n              type=\"submit\"\n              className=\"inline-block px-7 py-4 bg-blue-600 text-white font-medium text-sm leading-snug uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out w-full\"\n              data-mdb-ripple=\"true\"\n              data-mdb-ripple-color=\"light\"\n            &gt;\n              Sign up\n            &lt;\/button&gt;\n          &lt;\/form&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default RegisterPage;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Login Page<\/h3>\n\n\n\n<p>Now it&#8217;s time to implement the OAuth2 logic. In addition, the user will have the option to log in with an email and password. Let me clear the air. Users who register with OAuth2 won&#8217;t be able to use their Google account email and password to log into the application. They can only log into the application using the OAuth option.<\/p>\n\n\n\n<p>To begin, you&#8217;ll need access to the GitHub and Google SVG logos. Click on this <a href=\"https:\/\/minhaskamal.github.io\/DownGit\/#\/home?url=https:\/\/github.com\/wpcodevo\/google-github-oath2-reactjs\/tree\/master\/src\/assets\" target=\"_blank\" rel=\"noreferrer noopener\">link<\/a> to download the assets folder as a Zip file. Unzip the file and move it to the <strong>src<\/strong> folder to replace the existing assets folder.<\/p>\n\n\n\n<p>Next, create a <code>login.page.tsx<\/code> file in the <strong>src\/pages\/<\/strong> directory and add the following TSX code.<\/p>\n\n\n\n<p><strong>src\/pages\/login.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useLocation, useNavigate } from \"react-router-dom\";\nimport GitHubLogo from \"..\/assets\/github.svg\";\nimport GoogleLogo from \"..\/assets\/google.svg\";\nimport { getGoogleUrl } from \"..\/utils\/getGoogleUrl\";\nimport { object, string, TypeOf } from \"zod\";\nimport { useForm, SubmitHandler } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform\/resolvers\/zod\";\nimport useStore from \"..\/store\";\nimport { toast } from \"react-toastify\";\nimport { useEffect } from \"react\";\n\nconst loginSchema = object({\n  email: string()\n    .min(1, \"Email address is required\")\n    .email(\"Email Address is invalid\"),\n  password: string()\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n});\n\nexport type LoginInput = TypeOf&lt;typeof loginSchema&gt;;\n\nconst LoginPage = () =&gt; {\n  const location = useLocation();\n  const navigate = useNavigate();\n  const store = useStore();\n  const from = ((location.state as any)?.from.pathname as string) || \"\/profile\";\n\n  const loginUser = async (data: LoginInput) =&gt; {\n    try {\n      store.setRequestLoading(true);\n      const VITE_SERVER_ENDPOINT = import.meta.env.VITE_SERVER_ENDPOINT;\n      const response = await fetch(`${VITE_SERVER_ENDPOINT}\/api\/auth\/login`, {\n        method: \"POST\",\n        credentials: \"include\",\n        body: JSON.stringify(data),\n        headers: {\n          \"Content-Type\": \"application\/json\",\n        },\n      });\n      if (!response.ok) {\n        throw await response.json();\n      }\n\n      store.setRequestLoading(false);\n      navigate(\"\/profile\");\n    } catch (error: any) {\n      store.setRequestLoading(false);\n      if (error.error) {\n        error.error.forEach((err: any) =&gt; {\n          toast.error(err.message, {\n            position: \"top-right\",\n          });\n        });\n        return;\n      }\n      const resMessage =\n        (error.response &amp;&amp;\n          error.response.data &amp;&amp;\n          error.response.data.message) ||\n        error.message ||\n        error.toString();\n\n      toast.error(resMessage, {\n        position: \"top-right\",\n      });\n    }\n  };\n\n  const methods = useForm&lt;LoginInput&gt;({\n    resolver: zodResolver(loginSchema),\n  });\n\n  const {\n    reset,\n    handleSubmit,\n    register,\n    formState: { isSubmitSuccessful, errors },\n  } = methods;\n\n  useEffect(() =&gt; {\n    if (isSubmitSuccessful) {\n      reset();\n    }\n    \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, [isSubmitSuccessful]);\n\n  const onSubmitHandler: SubmitHandler&lt;LoginInput&gt; = (values) =&gt; {\n    loginUser(values);\n  };\n\n  return (\n    &lt;section className=\"bg-ct-blue-600 min-h-screen pt-20\"&gt;\n      &lt;div className=\"container mx-auto px-6 py-12 h-full flex justify-center items-center\"&gt;\n        &lt;div className=\"md:w-8\/12 lg:w-5\/12 bg-white px-8 py-10\"&gt;\n          &lt;form onSubmit={handleSubmit(onSubmitHandler)}&gt;\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"email\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Email address\"\n                {...register(\"email\")}\n              \/&gt;\n              {errors.email &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.email?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n\n            &lt;div className=\"mb-6\"&gt;\n              &lt;input\n                type=\"password\"\n                className=\"form-control block w-full px-4 py-5 text-sm font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none\"\n                placeholder=\"Password\"\n                {...register(\"password\")}\n              \/&gt;\n\n              {errors.password &amp;&amp; (\n                &lt;p className=\"text-red-700 text-sm mt-1\"&gt;\n                  {errors.password?.message}\n                &lt;\/p&gt;\n              )}\n            &lt;\/div&gt;\n\n            &lt;div className=\"flex justify-between items-center mb-6\"&gt;\n              &lt;div className=\"form-group form-check\"&gt;\n                &lt;input\n                  type=\"checkbox\"\n                  className=\"form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer\"\n                  id=\"exampleCheck3\"\n                \/&gt;\n                &lt;label\n                  className=\"form-check-label inline-block text-gray-800\"\n                  htmlFor=\"exampleCheck2\"\n                &gt;\n                  Remember me\n                &lt;\/label&gt;\n              &lt;\/div&gt;\n              &lt;a\n                href=\"#!\"\n                className=\"text-blue-600 hover:text-blue-700 focus:text-blue-700 active:text-blue-800 duration-200 transition ease-in-out\"\n              &gt;\n                Forgot password?\n              &lt;\/a&gt;\n            &lt;\/div&gt;\n\n            &lt;button\n              type=\"submit\"\n              className=\"inline-block px-7 py-4 bg-blue-600 text-white font-medium text-sm leading-snug uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out w-full\"\n              data-mdb-ripple=\"true\"\n              data-mdb-ripple-color=\"light\"\n            &gt;\n              Sign in\n            &lt;\/button&gt;\n\n            &lt;div className=\"flex items-center my-4 before:flex-1 before:border-t before:border-gray-300 before:mt-0.5 after:flex-1 after:border-t after:border-gray-300 after:mt-0.5\"&gt;\n              &lt;p className=\"text-center font-semibold mx-4 mb-0\"&gt;OR&lt;\/p&gt;\n            &lt;\/div&gt;\n\n            &lt;a\n              className=\"px-7 py-2 text-white font-medium text-sm leading-snug uppercase rounded shadow-md hover:shadow-lg focus:shadow-lg focus:outline-none focus:ring-0 active:shadow-lg transition duration-150 ease-in-out w-full flex justify-center items-center mb-3\"\n              style={{ backgroundColor: \"#3b5998\" }}\n              href={getGoogleUrl(from)}\n              role=\"button\"\n              data-mdb-ripple=\"true\"\n              data-mdb-ripple-color=\"light\"\n            &gt;\n              &lt;img\n                className=\"pr-2\"\n                src={GoogleLogo}\n                alt=\"\"\n                style={{ height: \"2rem\" }}\n              \/&gt;\n              Continue with Google\n            &lt;\/a&gt;\n            &lt;a\n              className=\"px-7 py-2 text-white font-medium text-sm leading-snug uppercase rounded shadow-md hover:shadow-lg focus:shadow-lg focus:outline-none focus:ring-0 active:shadow-lg transition duration-150 ease-in-out w-full flex justify-center items-center\"\n              style={{ backgroundColor: \"#55acee\" }}\n              href=\"#!\"\n              role=\"button\"\n              data-mdb-ripple=\"true\"\n              data-mdb-ripple-color=\"light\"\n              onClick={() =&gt; alert(\"Only Google OAuth2 is implemented\")}\n            &gt;\n              &lt;img\n                className=\"pr-2\"\n                src={GitHubLogo}\n                alt=\"\"\n                style={{ height: \"2.2rem\" }}\n              \/&gt;\n              Continue with GitHub\n            &lt;\/a&gt;\n          &lt;\/form&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default LoginPage;\n<\/code>\n<\/pre>\n\n\n\n<p>Once the local or OAuth2 login is successful, the backend API will send an HTTP Only cookie that the React app will include in subsequent requests to access protected routes.<\/p>\n\n\n\n<p>Also, the user will be redirected to the profile page where the account credentials will be displayed in the UI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create the Remaining Pages<\/h2>\n\n\n\n<p>So far so good. Let&#8217;s create the remaining React components. Here, you&#8217;ll create Home and Profile pages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Home Page<\/h3>\n\n\n\n<p>The home page will display a simple message when the user lands on the root route of the React application. Create a <code>home.page.tsx<\/code> file in the <strong>src\/pages\/<\/strong> directory and add the code below.<\/p>\n\n\n\n<p><strong>src\/pages\/home.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nconst HomePage = () =&gt; {\n  return (\n    &lt;&gt;\n      &lt;section className=\"bg-ct-blue-600 min-h-screen pt-20\"&gt;\n        &lt;div className=\"max-w-4xl mx-auto bg-ct-dark-100 rounded-md h-[20rem] flex justify-center items-center\"&gt;\n          &lt;p className=\"text-3xl font-semibold\"&gt;\n            Welcome to Google OAuth2 with React.js\n          &lt;\/p&gt;\n        &lt;\/div&gt;\n      &lt;\/section&gt;\n    &lt;\/&gt;\n  );\n};\n\nexport default HomePage;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Profile Page<\/h3>\n\n\n\n<p>When the backend API redirects the user to this page, a <strong>GET<\/strong> request will be fired to retrieve the authenticated user&#8217;s profile information. For this to work, the React app will send along the JWT token to retrieve the user&#8217;s credentials.<\/p>\n\n\n\n<p>Once the request resolves successfully, React will re-render the DOM to display the user&#8217;s account information in the UI.<\/p>\n\n\n\n<p><strong>src\/pages\/profile.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useEffect } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { toast } from \"react-toastify\";\nimport useStore from \"..\/store\";\nimport { IUser } from \"..\/store\/types\";\n\nconst ProfilePage = () =&gt; {\n  const navigate = useNavigate();\n  const store = useStore();\n\n  const fetchUser = async () =&gt; {\n    try {\n      store.setRequestLoading(true);\n      const VITE_SERVER_ENDPOINT = import.meta.env.VITE_SERVER_ENDPOINT;\n      const response = await fetch(`${VITE_SERVER_ENDPOINT}\/api\/users\/me`, {\n        credentials: \"include\",\n      });\n      if (!response.ok) {\n        throw await response.json();\n      }\n\n      const data = await response.json();\n      const user = data.data.user as IUser;\n      store.setRequestLoading(false);\n      console.log(user);\n\n      store.setAuthUser(user);\n    } catch (error: any) {\n      store.setRequestLoading(false);\n      if (error.error) {\n        error.error.forEach((err: any) =&gt; {\n          toast.error(err.message, {\n            position: \"top-right\",\n          });\n        });\n        return;\n      }\n      const resMessage =\n        (error.response &amp;&amp;\n          error.response.data &amp;&amp;\n          error.response.data.message) ||\n        error.message ||\n        error.toString();\n\n      if (error?.message === \"You are not logged in\") {\n        navigate(\"\/login\");\n      }\n\n      toast.error(resMessage, {\n        position: \"top-right\",\n      });\n    }\n  };\n\n  useEffect(() =&gt; {\n    fetchUser();\n  }, []);\n\n  const user = store.authUser;\n\n  return (\n    &lt;section className=\"bg-ct-blue-600  min-h-screen pt-20\"&gt;\n      &lt;div className=\"max-w-4xl mx-auto bg-ct-dark-100 rounded-md h-[20rem] flex justify-center items-center\"&gt;\n        &lt;div&gt;\n          &lt;p className=\"text-5xl text-center font-semibold\"&gt;Profile Page&lt;\/p&gt;\n          {!user ? (\n            &lt;p&gt;Loading...&lt;\/p&gt;\n          ) : (\n            &lt;div className=\"flex items-center gap-8\"&gt;\n              &lt;div&gt;\n                &lt;img\n                  src={\n                    user.photo.includes(\"default.png\")\n                      ? `http:\/\/localhost:8000\/api\/images\/${user.photo}`\n                      : user.photo\n                  }\n                  className=\"max-h-36\"\n                  alt={`profile photo of ${user.name}`}\n                \/&gt;\n              &lt;\/div&gt;\n              &lt;div className=\"mt-8\"&gt;\n                &lt;p className=\"mb-3\"&gt;ID: {user.id}&lt;\/p&gt;\n                &lt;p className=\"mb-3\"&gt;Name: {user.name}&lt;\/p&gt;\n                &lt;p className=\"mb-3\"&gt;Email: {user.email}&lt;\/p&gt;\n                &lt;p className=\"mb-3\"&gt;Role: {user.role}&lt;\/p&gt;\n                &lt;p className=\"mb-3\"&gt;Provider: {user.provider}&lt;\/p&gt;\n              &lt;\/div&gt;\n            &lt;\/div&gt;\n          )}\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default ProfilePage;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create Routes for the Pages<\/h2>\n\n\n\n<p>Now that we&#8217;ve created all the page components, let&#8217;s create routes for them. To do this, we&#8217;ll use the object syntax provided by React-Router-Dom v6.<\/p>\n\n\n\n<p>Create a <strong>router<\/strong> folder in the <strong>src<\/strong> directory. Within the <strong>src\/router<\/strong> folder, create a <code>index.tsx<\/code> file and add the following code.<\/p>\n\n\n\n<p><strong>src\/router\/index.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport type { RouteObject } from \"react-router-dom\";\nimport Layout from \"..\/components\/Layout\";\nimport HomePage from \"..\/pages\/home.page\";\nimport LoginPage from \"..\/pages\/login.page\";\nimport ProfilePage from \"..\/pages\/profile.page\";\nimport RegisterPage from \"..\/pages\/register.page\";\n\nconst normalRoutes: RouteObject = {\n  path: \"*\",\n  element: &lt;Layout \/&gt;,\n  children: [\n    {\n      index: true,\n      element: &lt;HomePage \/&gt;,\n    },\n    {\n      path: \"profile\",\n      element: &lt;ProfilePage \/&gt;,\n    },\n    {\n      path: \"login\",\n      element: &lt;LoginPage \/&gt;,\n    },\n    {\n      path: \"register\",\n      element: &lt;RegisterPage \/&gt;,\n    },\n  ],\n};\n\nconst routes: RouteObject[] = [normalRoutes];\n\nexport default routes;\n<\/code>\n<\/pre>\n\n\n\n<p>Let&#8217;s use the <code>useRoutes<\/code> hook to render the Route Object as Route elements. Open the <strong>src\/App.tsx<\/strong> file and replace its content with the following code.<\/p>\n\n\n\n<p><strong>src\/App.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useRoutes } from \"react-router-dom\";\nimport routes from \".\/router\";\n\nfunction App() {\n  const content = useRoutes(routes);\n  return content;\n}\n\nexport default App;\n<\/code>\n<\/pre>\n\n\n\n<p>For the routing to work, we need to wrap the <code>BrowserRouter<\/code> component around the entry point of our application. To do this, open the <strong>src\/main.tsx<\/strong> file and replace its content with the code below.<\/p>\n\n\n\n<p><strong>src\/main.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport \".\/index.css\";\nimport { ToastContainer } from \"react-toastify\";\nimport \"react-toastify\/dist\/ReactToastify.css\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\/client\";\nimport App from \".\/App\";\nimport { BrowserRouter } from \"react-router-dom\";\n\nReactDOM.createRoot(document.getElementById(\"root\") as HTMLElement).render(\n  &lt;React.StrictMode&gt;\n    &lt;BrowserRouter&gt;\n      &lt;App \/&gt;\n      &lt;ToastContainer \/&gt;\n    &lt;\/BrowserRouter&gt;\n  &lt;\/React.StrictMode&gt;\n);\n<\/code>\n<\/pre>\n\n\n\n<p>Oops, quite a lot of code. Am proud of you if you made it this far. Now you can start the Vite development server by running <code>yarn dev<\/code> .<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test the Google OAuth2 Flow<\/h2>\n\n\n\n<p>At this point, we&#8217;ve basically implemented the Google OAuth2 flow in the React application. Now we need to test the app with a backend API that has OAuth2 support. <\/p>\n\n\n\n<p>Luckily, I&#8217;ve included step-by-step instructions on how you can spin up a Node.js or Golang API that has OAuth2 support. You can find the instruction at the beginning of the article.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create an Account<\/h3>\n\n\n\n<p>Despite having the option to log in with OAuth2, you can log in with an email and a password. Before you&#8217;ll be able to use your email and password to log into the application, you need to register for an account.<\/p>\n\n\n\n<p>On the account registration page, provide the required credentials and click on the &#8220;<strong>SIGN UP<\/strong>&#8221; button to submit the form data to the backend API. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1003\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-1024x1003.webp\" alt=\"Google OAuth2 in React.js Register for an Account\" class=\"wp-image-10082\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-1024x1003.webp 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-300x294.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-768x753.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-100x98.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account-459x450.webp 459w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Register-for-an-Account.webp 1044w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The backend API will validate the credentials, add them to the database, and return a success message to the React app.<\/p>\n\n\n\n<p>Once the request resolves successfully, React will redirect you to the login page where you&#8217;ll be required to provide the email and password.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Login with OAuth2<\/h3>\n\n\n\n<p>On the login page, you can provide the email and password to sign into the application. Alternatively, you can click on the &#8220;<strong>CONTINUE WITH GOOGLE<\/strong>&#8221; button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1021\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-1021x1024.webp\" alt=\"Google OAuth2 in React.js Login\" class=\"wp-image-10084\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-1021x1024.webp 1021w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-300x300.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-150x150.webp 150w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-768x770.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-100x100.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-449x450.webp 449w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login-120x120.webp 120w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Login.webp 1052w\" sizes=\"auto, (max-width: 1021px) 100vw, 1021px\" \/><\/figure>\n\n\n\n<p>React will redirect you to the Google OAuth2 consent screen where you&#8217;ll be prompted to choose a Google account to grant the requested permissions to the application.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"589\" height=\"784\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-Consent-Screen.webp\" alt=\"Google OAuth2 Consent Screen\" class=\"wp-image-10092\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-Consent-Screen.webp 589w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-Consent-Screen-225x300.webp 225w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-Consent-Screen-75x100.webp 75w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-Consent-Screen-338x450.webp 338w\" sizes=\"auto, (max-width: 589px) 100vw, 589px\" \/><\/figure>\n<\/div>\n\n\n<p>From the available Google accounts, click on the test user you configured on the Google API Console to grant the permissions to the React application.<\/p>\n\n\n\n<p>Once you grant the permissions, the Google OAuth2 API will redirect you to the application&#8217;s redirect URI with an authorization code as a query parameter. The backend API will then make a <strong>POST<\/strong> request to the Google OAuth2 API to exchange the authorization code for an access token. Also, the client ID and client secret will be included in the request.<\/p>\n\n\n\n<p>After the Google OAuth2 API returns the access token, the backend API will make a <strong>GET<\/strong> request with the access token, client ID and client secret to obtain the user&#8217;s account information.<\/p>\n\n\n\n<p>If the request is successful, the user&#8217;s information will be stored in the database and a JWT token will be generated and returned to the frontend app as HTTP Only cookie.<\/p>\n\n\n\n<p>Also, the backend API will redirect the user to the URL path we stored in the <code>state<\/code> query parameter. In this case, we stored the path to the Profile page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Access the Protected Page<\/h3>\n\n\n\n<p>When the backend API redirects the authenticated user to the Profile page, a <strong>GET<\/strong> request will be fired to retrieve the user&#8217;s information. For this to work, the React app will include the Cookie along with the request.<\/p>\n\n\n\n<p>After that, the DOM will be re-rendered to display the profile information in the UI.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"803\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-1024x803.webp\" alt=\"Google OAuth2 in React.js Access a Protected Page\" class=\"wp-image-10083\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-1024x803.webp 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-300x235.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-768x603.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-100x78.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page-574x450.webp 574w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/Google-OAuth2-in-React.js-Access-a-Protected-Page.webp 1091w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>And we&#8217;re done! You can find the complete code of the React Google OAuth2 project on <a href=\"https:\/\/github.com\/wpcodevo\/google-github-oath2-reactjs\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/p>\n\n\n\n<p>In this tutorial, we implemented Google OAuth2 flow in a React.js application. Our app has all the required functionalities, for example, registering new users, logging them into their account with the OAuth2 option or password, and logging them out of the application.<\/p>\n\n\n\n<p>We even went a step further to create a project in the Google API Console, and obtain the OAuth2 client ID and secret. I hope you enjoyed this article. Don&#8217;t forget to leave a comment if you have any questions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you&#8217;ll learn how to implement Google OAuth2 in a React.js application, including creating a project in the Google API Console, configuring the&#8230;<\/p>\n","protected":false},"author":1,"featured_media":10088,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[54],"tags":[56,55],"class_list":["post-10036","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react","tag-react","tag-reactjs"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/10036","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/comments?post=10036"}],"version-history":[{"count":3,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/10036\/revisions"}],"predecessor-version":[{"id":11350,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/10036\/revisions\/11350"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/10088"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=10036"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=10036"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=10036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}