{"id":10038,"date":"2023-01-22T12:26:15","date_gmt":"2023-01-22T12:26:15","guid":{"rendered":"https:\/\/codevoweb.com\/?p=10038"},"modified":"2023-05-06T09:16:42","modified_gmt":"2023-05-06T09:16:42","slug":"how-to-implement-github-oauth-in-reactjs","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/how-to-implement-github-oauth-in-reactjs\/","title":{"rendered":"How to Implement GitHub OAuth in React.js"},"content":{"rendered":"\n<p>In this article, you&#8217;ll learn how to implement GitHub OAuth in a React.js application, including creating an OAuth app in the <a href=\"https:\/\/github.com\/settings\/developers\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub developer settings page<\/a>, configuring the application&#8217;s client ID, client secrets, and redirect URI, and implementing the necessary code in the React application.<\/p>\n\n\n\n<p>Why should you add GitHub OAuth to your React app? OAuth is an open standard for token-based authentication and authorization. GitHub OAuth is a way for users to grant access to their GitHub resources such as repositories, pull requests, issues, profile information, etc with third-party applications without having to share their login credentials.<\/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-google-oauth2-in-reactjs\">How to Implement Google OAuth2 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=\"\/google-oauth-authentication-react-mongodb-and-golang\">Google OAuth Authentication React.js, MongoDB and Golang<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/codevoweb.com\/github-oauth-authentication-vue-mongodb-and-golang\">GitHub OAuth Authentication Vuejs, 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-GitHub-OAuth-in-React.js.webp\" alt=\"How to Implement GitHub OAuth in React.js\" class=\"wp-image-10115\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-GitHub-OAuth-in-React.js.webp 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-GitHub-OAuth-in-React.js-300x169.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-GitHub-OAuth-in-React.js-768x432.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-GitHub-OAuth-in-React.js-100x56.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/How-to-Implement-GitHub-OAuth-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_0ad5cb-17 .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_0ad5cb-17 .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_0ad5cb-17 .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_0ad5cb-17 .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_0ad5cb-17 .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_0ad5cb-17 .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_0ad5cb-17 .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 GitHub OAuth Project<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Download or clone the React GitHub OAuth 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>In the terminal of the root project directory, 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 copied one to <code>.env.local<\/code>.<\/li>\n\n\n\n<li>Follow the &#8220;<strong>Get the GitHub OAuth Credentials<\/strong>&#8221; section to obtain the GitHub OAuth client ID and client secret from the GitHub developer settings page.<\/li>\n\n\n\n<li>Add the OAuth client ID and client secret to the <code>.env.local<\/code> file.<\/li>\n\n\n\n<li>Run <code>yarn dev<\/code> to start the Vite development server.<\/li>\n\n\n\n<li>Set up the backend API to process the request when the GitHub OAuth 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 Node.js API<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Make sure 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 machine.<\/li>\n\n\n\n<li>Download or clone the Node.js GitHub OAuth 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 run <code>git checkout github-oauth2-nodejs<\/code>  to change the Git branch to the GitHub OAuth project. Feel free to use the master branch.<\/li>\n\n\n\n<li>Install all the required dependencies by running <code>yarn<\/code> or <code>yarn install<\/code> in the console of the root project.<\/li>\n\n\n\n<li>Make a copy of the <code>example.env<\/code> file and rename the copied one to <code>.env<\/code>.<\/li>\n\n\n\n<li>Add the GitHub OAuth client ID and client secret to the <code>.env<\/code> file.<\/li>\n\n\n\n<li>Push the Prisma schema to the SQLite database by running <code>npx prisma db push<\/code>.<\/li>\n\n\n\n<li>Start the Node.js HTTP server by running <code>yarn start<\/code> in the terminal of the root directory.<\/li>\n\n\n\n<li>Test the GitHub OAuth flow with the React app and the Node.js API.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Run the React App with Golang API<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Make sure you have the latest version of <a href=\"https:\/\/go.dev\/doc\/install\" target=\"_blank\" rel=\"noreferrer noopener\">Golang installed<\/a> on your machine.<\/li>\n\n\n\n<li>Download or clone the Golang GitHub OAuth 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>In the console of the root directory, run <code>git checkout github-oauth2-golang<\/code> to change the Git branch to the GitHub OAuth project. 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 GitHub OAuth client ID and client secret to the <code>app.env<\/code> file.<\/li>\n\n\n\n<li>Run <code>go run main.go<\/code> to install the necessary dependencies and start the Gin HTTP server.<\/li>\n\n\n\n<li>Test the GitHub OAuth flow with the Golang API and 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>First things first, open the terminal in a convenient location and run the command below to bootstrap the React project with the Vite scaffolding tool.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpm create vite@latest github-oauth-reactjs -- --template react\n# or\nyarn create vite github-oauth-reactjs -- --template react\n<\/code>\n<\/pre>\n\n\n\n<p>This will download the Vite CLI binary from the NPM repository, generate the React boilerplate project, and output the files into a <code>github-oauth-reactjs<\/code> folder.<\/p>\n\n\n\n<p>Once that is done, change into the <code>github-oauth-reactjs<\/code> folder, and run <code>yarn<\/code> or <code>npm install<\/code> to install the required dependencies. After that, open the project folder in a code editor.<\/p>\n\n\n\n<p>Now let&#8217;s set up Tailwind CSS to handle the styling aspect of the project. Run the command below to install tailwind CSS and its peer dependencies.<\/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>After the installation is complete, 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>Replace the content of the <code>tailwind.config.cjs<\/code> file 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>Also, replace the content of the <code>src\/index.css<\/code> file 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>We&#8217;re now ready to install the dependencies we&#8217;ll need for the project. Run the commands below to install them.<\/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; A 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; A validation resolver for React-Hook-Form<\/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 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 management library for React<\/li>\n<\/ul>\n\n\n\n<p>Once that is done, open the <code>package.json<\/code> file and change the <code>dev<\/code> script to <code>\"dev\": \"vite --port 3000\"<\/code> . This will tell Vite to start the 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 GitHub OAuth Credentials<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, navigate to GitHub and click on your profile photo in the upper-right corner. Select &#8220;<strong>Settings<\/strong>&#8221; from the dropdown.<br><img loading=\"lazy\" decoding=\"async\" width=\"336\" height=\"722\" class=\"wp-image-10100\" style=\"width: 336px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-profile-photo-icon-to-display-a-dropdown.webp\" alt=\"click on the profile photo icon to display a dropdown\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-profile-photo-icon-to-display-a-dropdown.webp 336w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-profile-photo-icon-to-display-a-dropdown-140x300.webp 140w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-profile-photo-icon-to-display-a-dropdown-47x100.webp 47w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-profile-photo-icon-to-display-a-dropdown-209x450.webp 209w\" sizes=\"auto, (max-width: 336px) 100vw, 336px\" \/><\/li>\n\n\n\n<li>Scroll down and click on the &#8220;<strong>Developer settings<\/strong>&#8221; in the left sidebar.<br><img loading=\"lazy\" decoding=\"async\" width=\"866\" height=\"683\" class=\"wp-image-10101\" style=\"width: 866px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page.webp\" alt=\"click on the developer settings menu on the profile settings page\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page.webp 866w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page-300x237.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page-768x606.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page-100x79.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-developer-settings-menu-on-the-profile-settings-page-571x450.webp 571w\" sizes=\"auto, (max-width: 866px) 100vw, 866px\" \/><\/li>\n\n\n\n<li>On the GitHub Apps screen, click on the &#8220;<strong>OAuth Apps<\/strong>&#8221; menu.<br><img loading=\"lazy\" decoding=\"async\" width=\"487\" height=\"442\" class=\"wp-image-10104\" style=\"width: 487px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-OAuth-Apps-on-the-GitHub-apps-screen.webp\" alt=\"click on OAuth Apps on the GitHub apps screen\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-OAuth-Apps-on-the-GitHub-apps-screen.webp 487w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-OAuth-Apps-on-the-GitHub-apps-screen-300x272.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-OAuth-Apps-on-the-GitHub-apps-screen-100x91.webp 100w\" sizes=\"auto, (max-width: 487px) 100vw, 487px\" \/><\/li>\n\n\n\n<li>Select an existing OAuth app or create a new one. Click on the &#8220;<strong>New OAuth App<\/strong>&#8221; button to create a new OAuth app.<\/li>\n\n\n\n<li>Provide the OAuth credentials, including the application name, homepage URL, and the authorized redirect URI.<br><img loading=\"lazy\" decoding=\"async\" width=\"717\" height=\"836\" class=\"wp-image-10103\" style=\"width: 717px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/register-the-new-GitHub-OAuth-app.webp\" alt=\"register the new GitHub OAuth app\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/register-the-new-GitHub-OAuth-app.webp 717w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/register-the-new-GitHub-OAuth-app-257x300.webp 257w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/register-the-new-GitHub-OAuth-app-86x100.webp 86w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/register-the-new-GitHub-OAuth-app-386x450.webp 386w\" sizes=\"auto, (max-width: 717px) 100vw, 717px\" \/><br>After entering the necessary information, click on the &#8220;<strong>Register application<\/strong>&#8221; button.<\/li>\n\n\n\n<li>On the &#8220;<strong>New GitHub OAuth App<\/strong>&#8221; screen, upload the application logo, and click on the &#8220;<strong>Generate a new client secret<\/strong>&#8221; button to generate the OAuth client secret.<br><img loading=\"lazy\" decoding=\"async\" width=\"1104\" height=\"857\" class=\"wp-image-10107\" style=\"width: 1104px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button.webp\" alt=\"click on the generate a new client secret button\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button.webp 1104w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button-300x233.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button-1024x795.webp 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button-768x596.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button-100x78.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/click-on-the-generate-a-new-client-secret-button-580x450.webp 580w\" sizes=\"auto, (max-width: 1104px) 100vw, 1104px\" \/><br>Once you click the &#8220;<strong>Generate a new client secret<\/strong>&#8221; button, GitHub will prompt you to confirm your access through the password, two-factor authentication, or mobile app options.<br><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"752\" class=\"wp-image-10106\" style=\"width: 584px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-will-ask-you-to-confirm-your-access.webp\" alt=\"GitHub will ask you to confirm your access\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-will-ask-you-to-confirm-your-access.webp 584w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-will-ask-you-to-confirm-your-access-233x300.webp 233w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-will-ask-you-to-confirm-your-access-78x100.webp 78w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-will-ask-you-to-confirm-your-access-349x450.webp 349w\" sizes=\"auto, (max-width: 584px) 100vw, 584px\" \/><\/li>\n\n\n\n<li>After GitHub has confirmed your identity, the OAuth client secret will be generated.<br><img loading=\"lazy\" decoding=\"async\" width=\"1130\" height=\"911\" class=\"wp-image-10105\" style=\"width: 1130px;\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated.webp\" alt=\"the oauth client secret will be generated\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated.webp 1130w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated-300x242.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated-1024x826.webp 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated-768x619.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated-100x81.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/the-oauth-client-secret-will-be-generated-558x450.webp 558w\" sizes=\"auto, (max-width: 1130px) 100vw, 1130px\" \/><\/li>\n\n\n\n<li>Add the GitHub OAuth client ID and client secret to the <code>.env.local<\/code> file.<\/li>\n<\/ul>\n\n\n\n<p>In the end, your <code>.env.local<\/code> file should have the following environment variables.<\/p>\n\n\n\n<p><strong>.env.local<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nVITE_SERVER_ENDPOINT=http:\/\/localhost:8000\n\nVITE_GITHUB_OAUTH_CLIENT_ID=\nVITE_GITHUB_OAUTH_CLIENT_SECRET=\nVITE_GITHUB_OAUTH_REDIRECT_URL=http:\/\/localhost:8000\/api\/sessions\/oauth\/github\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Generate the GitHub Consent Screen Link<\/h2>\n\n\n\n<p>Now let&#8217;s create a helper function to generate the GitHub OAuth consent screen link from the client ID and secret. To do this, create a <strong>utils<\/strong> folder in the <strong>src<\/strong> directory. Within the <strong>src\/utils\/<\/strong> folder, create a <code>getGithubUrl.ts<\/code> file and add the TypeScript code below.<\/p>\n\n\n\n<p><strong>src\/utils\/getGithubUrl.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nexport function getGitHubUrl(from: string) {\n  const rootURl = \"https:\/\/github.com\/login\/oauth\/authorize\";\n\n  const options = {\n    client_id: import.meta.env.VITE_GITHUB_OAUTH_CLIENT_ID as string,\n    redirect_uri: import.meta.env.VITE_GITHUB_OAUTH_REDIRECT_URL as string,\n    scope: \"user:email\",\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, we created a function to generate the OAuth consent screen URL based on the OAuth app credentials.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>client_id<\/code> &#8211; The GitHub OAuth API will use this parameter to identify the OAuth App that is making the request. This unique identifier was assigned to the OAuth app when we created it in the GitHub developer settings.<\/li>\n\n\n\n<li><code>redirect_uri<\/code> &#8211; This is the URL the GitHub OAuth API will redirect the user to after the permission has been granted or denied. This callback URL must match the authorized redirect URL we configured in the GitHub developer settings.<\/li>\n\n\n\n<li><code>scope<\/code> &#8211; This parameter specifies the level of access the application is requesting. The <code>user:email<\/code> scope grants read access to the user&#8217;s email addresses.<\/li>\n\n\n\n<li><code>state<\/code> &#8211; This parameter allows us to pass data back to our application after the user grants or denies the permission. In this example, we&#8217;ll pass a path to a protected page to the backend API. Also, the <strong>state<\/strong> parameter can be used to prevent cross-site request forgery (<strong>CSRF<\/strong>) attacks.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Setup a React Store with Zustand<\/h2>\n\n\n\n<p>In this section, you&#8217;ll set up a global state to store the authenticated user&#8217;s credentials using the Zustand library. Zustand is a small and simple state management library for React that allows state access and mutation via React hooks.<\/p>\n\n\n\n<p>First, let&#8217;s create a <code>IUser<\/code> type to represent the fields of a user record. To do this, create a <strong>store<\/strong> folder in the <strong>src<\/strong> directory. Within the <strong>src\/store\/<\/strong> folder, create a <code>types.ts<\/code> file and add the interface below.<\/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: string;\n}\n<\/code>\n<\/pre>\n\n\n\n<p>Now create a <code>index.ts<\/code> file in the <strong>src\/store\/<\/strong> directory and add the following code.<\/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 code, we created properties to store the states and methods for mutating the states. Also, we exported the <code>useStore<\/code> hook that we can use to access the states and actions of the store.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create Reusable Components<\/h2>\n\n\n\n<p>With that out of the way, let&#8217;s create some reusable React components and style them with Tailwind CSS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Spinner Component<\/h3>\n\n\n\n<p>The first component is a <strong>Spinner<\/strong> that will be displayed whenever a request is in flight. This component will be hidden by default, however, when a request is being processed by the backend API, it will be made visible to give visual feedback to the user.<\/p>\n\n\n\n<p>Create a <code>Spinner.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\/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\">Header Component<\/h3>\n\n\n\n<p>The second component is a <strong>Header<\/strong> that will display a list of menus. These menus will have links pointing to the different pages of the application. Also, the logic for logging out users will be included in this component.<\/p>\n\n\n\n<p>Create a <code>Header.tsx<\/code> file in the <strong>src\/components\/<\/strong> directory and add the code below.<\/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<p>When the <code>handleLogout<\/code> function is evoked, a <strong>GET<\/strong> request will be made to the backend API to sign out the user. If the request is successful, React will redirect the user to the login page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Layout Component<\/h3>\n\n\n\n<p>Finally, let&#8217;s create a layout component to render the different pages below the Header component. This will prevent us from adding the Header component to all the pages that need it.<\/p>\n\n\n\n<p>Now we need a way to render the page components below the Header component. Luckily, React Router Dom provides an <code>&lt;Outlet \/&gt;<\/code> component that can do just that.<\/p>\n\n\n\n<p>Create a <code>Layout.tsx<\/code> file in the <strong>src\/components\/<\/strong> folder and add the code snippet 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 GitHub OAuth<\/h2>\n\n\n\n<p>Oops, quite a lot of code. At this point, we&#8217;re now ready to create React components to implement the GitHub OAuth flow. To do this, we&#8217;ll create two routes to handle the authentication aspect of the application.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/register<\/code> &#8211; This route will render the account registration component which will contain a form for registering new users.<\/li>\n\n\n\n<li><code>\/login<\/code> &#8211; This route will render the login component which will have a form for signing users into the application. Also, the form component will have the Google and GitHub OAuth buttons.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Account Registration Component<\/h3>\n\n\n\n<p>Here, you&#8217;ll create a React component that will contain a form for registering new users. The validation of the form will be handled by the React-Hook-Form library and the validation schema will be defined by the Zod schema validation library.<\/p>\n\n\n\n<p>When the form is submitted, React-Hook-Form will validate the fields against the Zod schema and if the form is valid, the <code>registerUser<\/code> function will be called to submit the form data 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<p>If the request is successful, React will redirect the user to the login page where the user can sign into the app using the email and password. However, if the request resolves in errors, alert notifications will be displayed to list the errors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Login Component<\/h3>\n\n\n\n<p>Here, you&#8217;ll create a login component that will have a form for signing the registered users into the application. The form validation will be controlled by React-Hook-Form and the validation rules will be provided by Zod.<\/p>\n\n\n\n<p>The backend API is configured in such a way that only users who registered their accounts using email and password will be allowed to log into the application through the login form.<\/p>\n\n\n\n<p>However, users can also use the Google or GitHub OAuth options to sign into the application.<\/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 { getGitHubUrl } from \"..\/utils\/getGithubUrl\";\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=\"\"\n              role=\"button\"\n              data-mdb-ripple=\"true\"\n              data-mdb-ripple-color=\"light\"\n              onClick={(e) =&gt; {\n                e.preventDefault();\n                alert(\"Only GitHub OAuth is allowed\");\n              }}\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={getGitHubUrl(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={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>If the OAuth or local (email and password) authentication is successful, the backend API or the React app will redirect the authenticated user to the Profile page. Also, the backend API will send an HTTP Only cookie to the client. This cookie will be included in subsequent requests that involve authentication.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create the Other Pages<\/h2>\n\n\n\n<p>In this section, you&#8217;ll create the remaining React components. The most essential component is the Profile page which will display the user&#8217;s credentials.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Home Page Component<\/h3>\n\n\n\n<p>First, create a <code>home.page.tsx<\/code> file in the <strong>src\/pages\/<\/strong> folder and add the following code. The Home page component will display a simple welcome message when the user lands on the root route.<\/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 Component<\/h3>\n\n\n\n<p>Let&#8217;s create the profile page component to display the authenticated user&#8217;s account information. The backend API will redirect the user to the profile page after the OAuth verification is complete.<\/p>\n\n\n\n<p>When the user lands on this page, React will make a <strong>GET<\/strong> request with Fetch API to retrieve the authenticated user&#8217;s credentials. For the request to succeed, React will include the Cookie along with the request.<\/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<p>If the request resolves successfully, React will re-render the DOM to display the account information in the UI.<\/p>\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 React components, let&#8217;s create routes for them using the React Router DOM library. Instead of using Route elements, we&#8217;ll use objects to define the routes.<\/p>\n\n\n\n<p>Create a <strong>router<\/strong> folder in the <strong>src<\/strong> directory. In the <strong>src\/router\/<\/strong> folder, create an <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>Next, let&#8217;s use the <code>useRoutes<\/code> hook to generate the route elements from the route object. 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>Also, we need to wrap the <code>BrowserRouter<\/code> component around the root component to give routing control to React Router DOM. Replace the content of the <strong>src\/main.tsx<\/strong> file 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>And we are done! You can now start the Vite development server by running <code>yarn dev<\/code> .<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test the GitHub OAuth Flow<\/h2>\n\n\n\n<p>Now that both the frontend and backend servers are running, we can test the GitHub OAuth flow to make sure we configured everything correctly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Login with GitHub OAuth<\/h3>\n\n\n\n<p>You can initiate the OAuth flow by clicking on the &#8220;<strong>CONTINUE WITH GITHUB<\/strong>&#8221; button available on the login page. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"962\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-962x1024.webp\" alt=\"GitHub OAuth in React.js Login Page\" class=\"wp-image-10122\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-962x1024.webp 962w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-282x300.webp 282w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-768x817.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-94x100.webp 94w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page-423x450.webp 423w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-in-React.js-Login-Page.webp 1014w\" sizes=\"auto, (max-width: 962px) 100vw, 962px\" \/><\/figure>\n\n\n\n<p>Once the button is clicked, you&#8217;ll be redirected to the GitHub OAuth consent screen where you&#8217;ll be prompted to sign into your GitHub account if you are not already signed in.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1012\" height=\"1009\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password.webp\" alt=\"GitHub OAuth prompt to sign in with email and password\" class=\"wp-image-10125\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password.webp 1012w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-300x300.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-150x150.webp 150w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-768x766.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-100x100.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-451x450.webp 451w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-prompt-to-sign-in-with-email-and-password-120x120.webp 120w\" sizes=\"auto, (max-width: 1012px) 100vw, 1012px\" \/><\/figure>\n\n\n\n<p>However, if you are already signed in, you&#8217;ll be presented with a screen that lists the permissions the application is requesting. You can grant the requested permissions to the application by clicking on the &#8220;<strong>Authorize<\/strong>&#8221; button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"992\" height=\"1012\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js.webp\" alt=\"GitHub OAuth Consent Screen in React.js\" class=\"wp-image-10124\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js.webp 992w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js-294x300.webp 294w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js-768x783.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js-98x100.webp 98w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-Consent-Screen-in-React.js-441x450.webp 441w\" sizes=\"auto, (max-width: 992px) 100vw, 992px\" \/><\/figure>\n\n\n\n<p>After you&#8217;ve granted the permissions, GitHub will redirect you to the redirect URL you specified while creating the GitHub OAuth App. The backend API will then exchange the authorization code for an access token using the GitHub OAuth token endpoint.<\/p>\n\n\n\n<p>The token endpoint requires the client ID, client secret, and authorization code as query parameters. Once the backend API has the access token, it&#8217;ll make a <strong>GET<\/strong> request to retrieve your profile information from the GitHub API.<\/p>\n\n\n\n<p>Next, the backend API will store the profile information in the database and return a copy to the frontend app. In addition, it will send an HTTP Only cookie that the React app will include in subsequent requests that require authentication.<\/p>\n\n\n\n<p>Finally, the backend API will redirect you to the path that was stored in the <code>state<\/code> parameter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Display Profile Information<\/h3>\n\n\n\n<p>When the backend API redirects you to the Profile page, React will make a <strong>GET<\/strong> request to exchange the JWT token for your account information. If the request is successful, React will re-render the DOM to display the credentials in the UI.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"978\" height=\"819\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information.webp\" alt=\"GitHub OAuth display the authenticated user's profile information\" class=\"wp-image-10123\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information.webp 978w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information-300x251.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information-768x643.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information-100x84.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2023\/01\/GitHub-OAuth-display-the-authenticated-users-profile-information-537x450.webp 537w\" sizes=\"auto, (max-width: 978px) 100vw, 978px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>You can find the complete code of the React GitHub OAuth 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 GitHub OAuth flow in a React.js application. Our app has all the required functionalities, for example, registering new users, signing them into the application with the GitHub OAuth option or password, and logging them out of the application.<\/p>\n\n\n\n<p>We even went a step further to create an OAuth app in the GitHub developer settings, and obtain the OAuth client ID and client 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 GitHub OAuth in a React.js application, including creating an OAuth app in the GitHub developer settings page,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":10115,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[54],"tags":[56,55],"class_list":["post-10038","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\/10038","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=10038"}],"version-history":[{"count":3,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/10038\/revisions"}],"predecessor-version":[{"id":11351,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/10038\/revisions\/11351"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/10115"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=10038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=10038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=10038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}