{"id":2203,"date":"2022-06-02T13:22:16","date_gmt":"2022-06-02T13:22:16","guid":{"rendered":"https:\/\/codevoweb.com\/?p=2203"},"modified":"2023-05-06T09:25:11","modified_gmt":"2023-05-06T09:25:11","slug":"google-oauth-authentication-react-and-node","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/google-oauth-authentication-react-and-node\/","title":{"rendered":"Google OAuth Authentication React.js and Node.js(No Passport)"},"content":{"rendered":"\n<p>In this article, you&#8217;ll learn how to implement Google OAuth Authentication with React.js and Node.js without using Passport. You&#8217;ll also learn how to send JWT access and refresh tokens after the user has been authenticated.<\/p>\n\n\n\n<p>Related articles:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\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=\"\/how-to-implement-github-oauth-in-reactjs\">How to Implement GitHub OAuth in React.js<\/a><\/li>\n\n\n\n<li><a href=\"\/how-to-implement-google-oauth2-in-reactjs\">How to Implement Google OAuth2 in React.js<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"446\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport.webp\" alt=\"Google OAuth Authentication React.js and Node.js(No Passport)\" class=\"wp-image-2244\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport.webp 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport-300x157.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport-768x403.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport-100x52.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-Authentication-React.js-and-Node.jsNo-Passport-700x367.webp 700w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id_9dfafb-3f .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_9dfafb-3f .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_9dfafb-3f .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_9dfafb-3f .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_9dfafb-3f .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_9dfafb-3f .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_9dfafb-3f .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\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Have some basic knowledge of HTML, CSS, React.js, and Node.js<\/li>\n\n\n\n<li>Have Node.js installed on your machine<\/li>\n<\/ul>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<h2 class=\"wp-block-heading\">Create Google client ID and client secret<\/h2>\n\n\n\n<p>Open your browser and search for <em><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\"><strong>Google OAuth<\/strong><\/mark><\/em>. Click on the first link in the search results and you should be taken to<em> <a href=\"https:\/\/developers.google.com\/identity\/protocols\/oauth2\" target=\"_blank\" rel=\"noreferrer noopener\">OAuth 2.0 to Access Google APIs<\/a><\/em>.<\/p>\n\n\n\n<p>On this page, you will find the instructions to get the access keys but don&#8217;t worry I will guide you on how to do it the right way.<\/p>\n\n\n\n<p>Click on the <a href=\"https:\/\/console.developers.google.com\/\" target=\"_blank\" rel=\"noopener\">Google API Console<\/a> where we will obtain the OAuth 2.0 client credentials.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You need a Google account to access the <a href=\"https:\/\/console.developers.google.com\/\" target=\"_blank\" rel=\"noopener\">Google API Console<\/a> page.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Create a New Project<\/h3>\n\n\n\n<p>Click on the <strong>Google-Oauth<\/strong> dropdown, a popup should appear where you can select an existing project or create a new one.<\/p>\n\n\n\n<p>Click on the <strong>&#8220;New Project&#8221;<\/strong> button then provide the project name and click on the <strong>&#8220;CREATE&#8221;<\/strong> button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"715\" height=\"670\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project.png\" alt=\"google oauth new project\" class=\"wp-image-2206\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project.png 715w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-300x281.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-100x94.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-480x450.png 480w\" sizes=\"auto, (max-width: 715px) 100vw, 715px\" \/><\/figure>\n\n\n\n<p>Now wait for Google to do its job and once the project has been successfully created, you should see a success notification.<\/p>\n\n\n\n<p>Click on the <strong>&#8220;SELECT PROJECT&#8221;<\/strong> button and tap anywhere on the screen to close the popup.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"716\" height=\"512\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-success-notification.png\" alt=\"google oauth new project success notification\" class=\"wp-image-2205\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-success-notification.png 716w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-success-notification-300x215.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-success-notification-100x72.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-new-project-success-notification-629x450.png 629w\" sizes=\"auto, (max-width: 716px) 100vw, 716px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Create an OAuth Consent Screen<\/h3>\n\n\n\n<p>Before we can create the credentials, we first need to create a consent screen. The consent screen is where the user will be redirected when they click on the link.<\/p>\n\n\n\n<p>On the sidebar under <strong>&#8220;APIs &amp; Services&#8221;<\/strong> select <strong>OAuth consent screen<\/strong>. On the <strong>OAuth consent screen<\/strong> tab, select the External User Type if you are making a publicly facing application and click on the <strong>&#8220;CREATE&#8221;<\/strong> button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"790\" height=\"636\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup.png\" alt=\"google oauth consent screen setup\" class=\"wp-image-2209\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup.png 790w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup-300x242.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup-768x618.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup-100x81.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-setup-559x450.png 559w\" sizes=\"auto, (max-width: 790px) 100vw, 790px\" \/><\/figure>\n\n\n\n<p>Next, provide the required information for the consent screen<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"902\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration.png\" alt=\"google oauth consent screen registration\" class=\"wp-image-2211\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration.png 955w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-300x283.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-768x725.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-100x94.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-476x450.png 476w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure>\n\n\n\n<p>Once you are done, click on the <strong>&#8220;SAVE AND CONTINUE&#8221;<\/strong> button.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"717\" height=\"887\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-2.png\" alt=\"google oauth consent screen registration 2\" class=\"wp-image-2210\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-2.png 717w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-2-243x300.png 243w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-2-81x100.png 81w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-registration-2-364x450.png 364w\" sizes=\"auto, (max-width: 717px) 100vw, 717px\" \/><\/figure>\n<\/div>\n\n\n<p>On the <strong>Scopes<\/strong> tab, click on the <strong>&#8220;ADD OR REMOVE SCOPES&#8221;<\/strong>. There are many scopes you can add depending on your application but am only interested in the user&#8217;s email and profile information.<\/p>\n\n\n\n<p>Scroll down and click on the <strong>&#8220;UPDATE&#8221;<\/strong> button after selecting the scopes and then click on the <strong>&#8220;SAVE AND CONTINUE&#8221;<\/strong> button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"939\" height=\"741\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes.png\" alt=\"google oauth consent screen select scopes\" class=\"wp-image-2212\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes.png 939w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes-300x237.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes-768x606.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes-100x79.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-consent-screen-select-scopes-570x450.png 570w\" sizes=\"auto, (max-width: 939px) 100vw, 939px\" \/><\/figure>\n\n\n\n<p>On the <strong>Test users<\/strong> tab, click on the <strong>ADD USERS<\/strong> button to add a test user. The test user is the only account that will be able to log into your application whilst in sandbox mode.<\/p>\n\n\n\n<p>Once you have added the test user, click on <strong>&#8220;SAVE AND CONTINUE&#8221;<\/strong>.<\/p>\n\n\n\n<p>On the <strong>Summary<\/strong> tab, review the information and click on the <strong>&#8220;BACK TO DASHBOARD&#8221;<\/strong> button.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create the OAuth Credentials<\/h3>\n\n\n\n<p>On the sidebar under <strong>&#8220;APIs &amp; Services&#8221;<\/strong> select Credentials. On the <strong>Credentials tab<\/strong>, click on <strong>&#8220;CREATE CREDENTIALS&#8221;<\/strong> and choose <strong>OAuth client ID<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"998\" height=\"691\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials.png\" alt=\"google oauth create credentials\" class=\"wp-image-2208\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials.png 998w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials-300x208.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials-768x532.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials-100x69.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-create-credentials-650x450.png 650w\" sizes=\"auto, (max-width: 998px) 100vw, 998px\" \/><\/figure>\n\n\n\n<p>Now provide the required fields. The authorized redirect URL should point to your server to enable us to make the <strong>GET<\/strong> request directly from the consent screen.<\/p>\n\n\n\n<p>There are many ways you can do this but I find this method a lot quicker. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"708\" height=\"931\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-credentials.png\" alt=\"google oauth credentials\" class=\"wp-image-2213\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-credentials.png 708w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-credentials-228x300.png 228w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-credentials-76x100.png 76w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-oauth-credentials-342x450.png 342w\" sizes=\"auto, (max-width: 708px) 100vw, 708px\" \/><\/figure>\n\n\n\n<p>Next, click on <strong>&#8220;CREATE&#8221;<\/strong> and you should get your client ID and client secret.<\/p>\n\n\n\n<p>Update your <code>.env<\/code> file on the server with the credentials.<\/p>\n\n\n\n<p><strong>.env<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nGOOGLE_OAUTH_CLIENT_ID=your client Id here\nGOOGLE_OAUTH_CLIENT_SECRET=your client secret here\nGOOGLE_OAUTH_REDIRECT_URL=http:\/\/localhost:8000\/api\/sessions\/oauth\/google\n<\/code>\n<\/pre>\n\n\n\n<p>Also, update the <code>.env.local<\/code> file in the React application.<\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nREACT_APP_GOOGLE_OAUTH_CLIENT_ID=your client Id here\nREACT_APP_GOOGLE_OAUTH_CLIENT_SECRET=your client secret here\nREACT_APP_GOOGLE_OAUTH_ENDPOINT=http:\/\/localhost:8000\nREACT_APP_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 Consent Screen URL<\/h2>\n\n\n\n<p>Now, we are ready to implement Google OAuth Authentication in our application.<\/p>\n\n\n\n<p>To begin, we need to build the OAuth consent screen link based on the client ID and the redirect URL.<\/p>\n\n\n\n<p>Also, we need to provide the scopes we selected in the credentials tab above.<\/p>\n\n\n\n<p><strong>src\/utils\/getGoogleUrl.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><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: process.env.REACT_APP_GOOGLE_OAUTH_REDIRECT as string,\n    client_id: process.env.REACT_APP_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\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Build the Google OAuth Login Page with React and MUI<\/h2>\n\n\n\n<p>Next, create a login screen with the consent screen link. I created a simple login page having the Google login button with React and Material UI for those who are not following the tutorials in this series.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"702\" height=\"513\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/OAuth-login-page-with-react-.png\" alt=\"OAuth login page with react\" class=\"wp-image-2219\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/OAuth-login-page-with-react-.png 702w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/OAuth-login-page-with-react--300x219.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/OAuth-login-page-with-react--100x73.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/OAuth-login-page-with-react--616x450.png 616w\" sizes=\"auto, (max-width: 702px) 100vw, 702px\" \/><\/figure>\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 { Box, Container, Typography, Link as MuiLink } from '@mui\/material';\nimport { useLocation } from 'react-router-dom';\nimport { ReactComponent as GoogleLogo } from '..\/assets\/google.svg';\nimport { getGoogleUrl } from '..\/utils\/getGoogleUrl';\n\nconst LoginPage = () =&gt; {\n  const location = useLocation();\n  let from = ((location.state as any)?.from?.pathname as string) || '\/';\n\n  return (\n    &lt;Container\n      maxWidth={false}\n      sx={{\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        height: '100vh',\n        backgroundColor: '#2363eb',\n      }}\n    &gt;\n      &lt;Box width='27rem'&gt;\n        &lt;Typography\n          variant='h6'\n          component='p'\n          sx={{\n            my: '1.5rem',\n            textAlign: 'center',\n            color: 'white',\n          }}\n        &gt;\n          Log in with another provider:\n        &lt;\/Typography&gt;\n        &lt;Box\n          width='100%'\n          sx={{\n            backgroundColor: '#e5e7eb',\n            p: { xs: '1rem', sm: '2rem' },\n            borderRadius: 2,\n          }}\n        &gt;\n          &lt;MuiLink\n            href={getGoogleUrl(from)}\n            sx={{\n              backgroundColor: '#f5f6f7',\n              borderRadius: 1,\n              py: '0.6rem',\n              columnGap: '1rem',\n              textDecoration: 'none',\n              color: '#393e45',\n              cursor: 'pointer',\n              fontWeight: 500,\n              '&amp;:hover': {\n                backgroundColor: '#fff',\n                boxShadow: '0 1px 13px 0 rgb(0 0 0 \/ 15%)',\n              },\n            }}\n            display='flex'\n            justifyContent='center'\n            alignItems='center'\n          &gt;\n            &lt;GoogleLogo style={{ height: '2rem' }} \/&gt;\n            Google\n          &lt;\/MuiLink&gt;\n        &lt;\/Box&gt;\n      &lt;\/Box&gt;\n    &lt;\/Container&gt;\n  );\n};\n\nexport default LoginPage;\n\n<\/code>\n<\/pre>\n\n\n\n<p>If you are coming from the previous tutorial in this series, then update the <code>login.page.tsx<\/code> file to include the Google login button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"861\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-861x1024.png\" alt=\"Google OAuth login page with react\" class=\"wp-image-2218\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-861x1024.png 861w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-252x300.png 252w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-768x913.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-84x100.png 84w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react-378x450.png 378w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-login-page-with-react.png 908w\" sizes=\"auto, (max-width: 861px) 100vw, 861px\" \/><\/figure>\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 { Box, Container, Typography, Link as MuiLink } from '@mui\/material';\nimport { styled } from '@mui\/material\/styles';\nimport { FormProvider, SubmitHandler, useForm } from 'react-hook-form';\nimport { object, string, TypeOf } from 'zod';\nimport { zodResolver } from '@hookform\/resolvers\/zod';\nimport FormInput from '..\/components\/FormInput';\nimport { useEffect } from 'react';\nimport { Link, useLocation, useNavigate } from 'react-router-dom';\nimport { LoadingButton as _LoadingButton } from '@mui\/lab';\nimport { toast } from 'react-toastify';\nimport { useLoginUserMutation } from '..\/redux\/api\/authApi';\nimport { ReactComponent as GoogleLogo } from '..\/assets\/google.svg';\nimport { getGoogleUrl } from '..\/utils\/getGoogleUrl';\n\nconst LoadingButton = styled(_LoadingButton)`\n  padding: 0.6rem 0;\n  background-color: #f9d13e;\n  color: #2363eb;\n  font-weight: 500;\n\n  &amp;:hover {\n    background-color: #ebc22c;\n    transform: translateY(-2px);\n  }\n`;\n\nconst LinkItem = styled(Link)`\n  text-decoration: none;\n  color: #2363eb;\n  &amp;:hover {\n    text-decoration: underline;\n  }\n`;\n\nconst loginSchema = object({\n  email: string()\n    .nonempty('Email address is required')\n    .email('Email Address is invalid'),\n  password: string()\n    .nonempty('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 methods = useForm&lt;LoginInput&gt;({\n    resolver: zodResolver(loginSchema),\n  });\n\n  \/\/ ? API Login Mutation\n  const [loginUser, { isLoading, isError, error, isSuccess }] =\n    useLoginUserMutation();\n\n  const navigate = useNavigate();\n  const location = useLocation();\n\n  const from = ((location.state as any)?.from.pathname as string) || '\/profile';\n\n  const {\n    reset,\n    handleSubmit,\n    formState: { isSubmitSuccessful },\n  } = methods;\n\n  useEffect(() =&gt; {\n    if (isSuccess) {\n      toast.success('You successfully logged in');\n      navigate(from);\n    }\n    if (isError) {\n      if (Array.isArray((error as any).data.error)) {\n        (error as any).data.error.forEach((el: any) =&gt;\n          toast.error(el.message, {\n            position: 'top-right',\n          })\n        );\n      } else {\n        toast.error((error as any).data.message, {\n          position: 'top-right',\n        });\n      }\n    }\n    \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, [isLoading]);\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    \/\/ ? Executing the loginUser Mutation\n    loginUser(values);\n  };\n\n  return (\n    &lt;Container\n      maxWidth={false}\n      sx={{\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        height: '100vh',\n        backgroundColor: '#2363eb',\n      }}\n    &gt;\n      &lt;Box\n        sx={{\n          display: 'flex',\n          justifyContent: 'center',\n          alignItems: 'center',\n          flexDirection: 'column',\n        }}\n      &gt;\n        &lt;Typography\n          textAlign='center'\n          component='h1'\n          sx={{\n            color: '#f9d13e',\n            fontWeight: 600,\n            fontSize: { xs: '2rem', md: '3rem' },\n            mb: 2,\n            letterSpacing: 1,\n          }}\n        &gt;\n          Welcome Back!\n        &lt;\/Typography&gt;\n        &lt;Typography\n          variant='body1'\n          component='h2'\n          sx={{ color: '#e5e7eb', mb: 2 }}\n        &gt;\n          Login to have access!\n        &lt;\/Typography&gt;\n\n        &lt;FormProvider {...methods}&gt;\n          &lt;Box\n            component='form'\n            onSubmit={handleSubmit(onSubmitHandler)}\n            noValidate\n            autoComplete='off'\n            maxWidth='27rem'\n            width='100%'\n            sx={{\n              backgroundColor: '#e5e7eb',\n              p: { xs: '1rem', sm: '2rem' },\n              borderRadius: 2,\n            }}\n          &gt;\n            &lt;FormInput name='email' label='Email Address' type='email' \/&gt;\n            &lt;FormInput name='password' label='Password' type='password' \/&gt;\n\n            &lt;Typography\n              sx={{ fontSize: '0.9rem', mb: '1rem', textAlign: 'right' }}\n            &gt;\n              &lt;LinkItem to='\/forgotpassword' style={{ color: '#333' }}&gt;\n                Forgot Password?\n              &lt;\/LinkItem&gt;\n            &lt;\/Typography&gt;\n\n            &lt;LoadingButton\n              variant='contained'\n              sx={{ mt: 1 }}\n              fullWidth\n              disableElevation\n              type='submit'\n              loading={isLoading}\n            &gt;\n              Login\n            &lt;\/LoadingButton&gt;\n\n            &lt;Typography sx={{ fontSize: '0.9rem', mt: '1rem' }}&gt;\n              Need an account? &lt;LinkItem to='\/register'&gt;Sign Up Here&lt;\/LinkItem&gt;\n            &lt;\/Typography&gt;\n          &lt;\/Box&gt;\n        &lt;\/FormProvider&gt;\n        &lt;Typography\n          variant='h6'\n          component='p'\n          sx={{\n            my: '1.5rem',\n            textAlign: 'center',\n            color: 'white',\n          }}\n        &gt;\n          Log in with another provider:\n        &lt;\/Typography&gt;\n        &lt;Box\n          maxWidth='27rem'\n          width='100%'\n          sx={{\n            backgroundColor: '#e5e7eb',\n            p: { xs: '1rem', sm: '2rem' },\n            borderRadius: 2,\n          }}\n        &gt;\n          &lt;MuiLink\n            href={getGoogleUrl(from)}\n            sx={{\n              backgroundColor: '#f5f6f7',\n              borderRadius: 1,\n              py: '0.6rem',\n              columnGap: '1rem',\n              textDecoration: 'none',\n              color: '#393e45',\n              cursor: 'pointer',\n              fontWeight: 500,\n              '&amp;:hover': {\n                backgroundColor: '#fff',\n                boxShadow: '0 1px 13px 0 rgb(0 0 0 \/ 15%)',\n              },\n            }}\n            display='flex'\n            justifyContent='center'\n            alignItems='center'\n          &gt;\n            &lt;GoogleLogo style={{ height: '2rem' }} \/&gt;\n            Google\n          &lt;\/MuiLink&gt;\n        &lt;\/Box&gt;\n      &lt;\/Box&gt;\n    &lt;\/Container&gt;\n  );\n};\n\nexport default LoginPage;\n\n<\/code>\n<\/pre>\n\n\n\n<p>Once you click on the Google button, you should be taken to the consent screen where you&#8217;ll see your Google accounts.<\/p>\n\n\n\n<p>I opened the link in a private window to avoid disclosing my Google accounts.<\/p>\n\n\n\n<p>On the consent screen, click on the test user account or log in with the test Google account if you haven&#8217;t done that.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"862\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-862x1024.png\" alt=\"Google OAuth consent screen\" class=\"wp-image-2221\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-862x1024.png 862w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-253x300.png 253w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-768x912.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-84x100.png 84w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen-379x450.png 379w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/Google-OAuth-consent-screen.png 907w\" sizes=\"auto, (max-width: 862px) 100vw, 862px\" \/><\/figure>\n<\/div>\n\n\n<p>You should get a <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">404 error<\/mark><\/strong> assuming the server is running. The most exciting part of the redirect URL is the code in the query string.<\/p>\n\n\n\n<p>The reason why we got the <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">404 error<\/mark><\/strong> was that we haven&#8217;t implemented it on the server yet.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"936\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-936x1024.png\" alt=\"google OAuth 2 click on google account\" class=\"wp-image-2222\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-936x1024.png 936w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-274x300.png 274w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-768x840.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-91x100.png 91w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account-411x450.png 411w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/06\/google-OAuth-2-click-on-google-account.png 949w\" sizes=\"auto, (max-width: 936px) 100vw, 936px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Implement the OAuth Authentication on the Node.js Server<\/h2>\n\n\n\n<p>To make the HTTP requests to Google, we&#8217;ll use <a href=\"https:\/\/axios-http.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Axios<\/a>. Run this command to install the <a href=\"https:\/\/axios-http.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Axios<\/a> package.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add axios\n<\/code>\n<\/pre>\n\n\n\n<p>Update the <code>config\/custom-environment-variables.ts<\/code> file to have the OAuth client Id, client secret, and the redirect URL.<\/p>\n\n\n\n<p><strong>config\/custom-environment-variables.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nexport default {\n  dbName: 'MONGODB_USERNAME',\n  dbPass: 'MONGODB_PASSWORD',\n  \n  accessTokenPrivateKey: 'ACCESS_TOKEN_PRIVATE_KEY',\n  accessTokenPublicKey: 'ACCESS_TOKEN_PUBLIC_KEY',\n  refreshTokenPrivateKey: 'REFRESH_TOKEN_PRIVATE_KEY',\n  refreshTokenPublicKey: 'REFRESH_TOKEN_PUBLIC_KEY',\n\n  googleClientId: 'GOOGLE_OAUTH_CLIENT_ID',\n  googleClientSecret: 'GOOGLE_OAUTH_CLIENT_SECRET',\n  googleOauthRedirect: 'GOOGLE_OAUTH_REDIRECT_URL',\n};\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get Google OAuth Access Token and User&#8217;s Profile<\/h3>\n\n\n\n<p>Now create a <code>session.service.ts<\/code> file in the services folder and add these two functions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>getGoogleOauthToken()<\/code> &#8211; To get the OAuth Access Token from Google<\/li>\n\n\n\n<li><code>getGoogleUser()<\/code>  &#8211; Get the user&#8217;s profile information with the OAuth Access Token<\/li>\n<\/ul>\n\n\n\n<p><strong>src\/services\/session.service.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport config from 'config';\nimport axios from 'axios';\nimport qs from 'qs';\n\ninterface GoogleOauthToken {\n  access_token: string;\n  id_token: string;\n  expires_in: number;\n  refresh_token: string;\n  token_type: string;\n  scope: string;\n}\n\nexport const getGoogleOauthToken = async ({\n  code,\n}: {\n  code: string;\n}): Promise&lt;GoogleOauthToken&gt; =&gt; {\n  const rootURl = 'https:\/\/oauth2.googleapis.com\/token';\n\n  const options = {\n    code,\n    client_id: config.get&lt;string&gt;('googleClientId'),\n    client_secret: config.get&lt;string&gt;('googleClientSecret'),\n    redirect_uri: config.get&lt;string&gt;('googleOauthRedirect'),\n    grant_type: 'authorization_code',\n  };\n  try {\n    const { data } = await axios.post&lt;GoogleOauthToken&gt;(\n      rootURl,\n      qs.stringify(options),\n      {\n        headers: {\n          'Content-Type': 'application\/x-www-form-urlencoded',\n        },\n      }\n    );\n\n    return data;\n  } catch (err: any) {\n    console.log('Failed to fetch Google Oauth Tokens');\n    throw new Error(err);\n  }\n};\n\ninterface GoogleUserResult {\n  id: string;\n  email: string;\n  verified_email: boolean;\n  name: string;\n  given_name: string;\n  family_name: string;\n  picture: string;\n  locale: string;\n}\n\nexport async function getGoogleUser({\n  id_token,\n  access_token,\n}: {\n  id_token: string;\n  access_token: string;\n}): Promise&lt;GoogleUserResult&gt; {\n  try {\n    const { data } = await axios.get&lt;GoogleUserResult&gt;(\n      `https:\/\/www.googleapis.com\/oauth2\/v1\/userinfo?alt=json&amp;access_token=${access_token}`,\n      {\n        headers: {\n          Authorization: `Bearer ${id_token}`,\n        },\n      }\n    );\n\n    return data;\n  } catch (err: any) {\n    console.log(err);\n    throw Error(err);\n  }\n}\n\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Update the User Model<\/h3>\n\n\n\n<p>If you followed the previous tutorials in this series then update the <code>user.model.ts<\/code> file to have the following fields.<\/p>\n\n\n\n<p><strong>src\/models\/user.model.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport {\n  DocumentType,\n  getModelForClass,\n  index,\n  modelOptions,\n  pre,\n  prop,\n} from '@typegoose\/typegoose';\nimport bcrypt from 'bcryptjs';\n\n@index({ email: 1 })\n@pre&lt;User&gt;('save', async function () {\n  \/\/ Hash password if the password is new or was updated\n  if (!this.isModified('password')) return;\n\n  \/\/ Hash password with costFactor of 12\n  this.password = await bcrypt.hash(this.password, 12);\n})\n@modelOptions({\n  schemaOptions: {\n    \/\/ Add createdAt and updatedAt fields\n    timestamps: true,\n  },\n})\n\n\/\/ Export the User class to be used as TypeScript type\nexport class User {\n  @prop()\n  name: string;\n\n  @prop({ unique: true, required: true })\n  email: string;\n\n  @prop({ required: true, minlength: 8, maxLength: 32, select: false })\n  password: string;\n\n  @prop({ default: 'user' })\n  role: string;\n\n  @prop({ default: 'default.png' })\n  photo: string;\n\n  @prop({ default: false })\n  verified: boolean;\n\n  @prop({ default: 'local' })\n  provider: string;\n\n  \/\/ Instance method to check if passwords match\n  async comparePasswords(hashedPassword: string, candidatePassword: string) {\n    return await bcrypt.compare(candidatePassword, hashedPassword);\n  }\n}\n\n\/\/ Create the user model from the User class\nconst userModel = getModelForClass(User);\nexport default userModel;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Add a Service to Update the User<\/h3>\n\n\n\n<p>Next, add the code below to the <code>user.service.ts<\/code> file to help us update the user&#8217;s information in the MongoDB database.<\/p>\n\n\n\n<p><strong>src\/services\/user.service.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nexport const findAndUpdateUser = async (\n  query: FilterQuery&lt;User&gt;,\n  update: UpdateQuery&lt;User&gt;,\n  options: QueryOptions\n) =&gt; {\n  return await userModel.findOneAndUpdate(query, update, options);\n};\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create the Google OAuth Controller<\/h3>\n\n\n\n<p>Add the <code>googleOauthHandler<\/code> to the <code>auth.controller.ts<\/code> . This handler will be called when the user gets redirected to the server.<\/p>\n\n\n\n<p><strong>src\/controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nexport const googleOauthHandler = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) =&gt; {\n  try {\n    \/\/ Get the code from the query\n    const code = req.query.code as string;\n    const pathUrl = (req.query.state as string) || '\/';\n\n    if (!code) {\n      return next(new AppError('Authorization code not provided!', 401));\n    }\n\n    \/\/ Use the code to get the id and access tokens\n    const { id_token, access_token } = await getGoogleOauthToken({ code });\n\n    \/\/ Use the token to get the User\n    const { name, verified_email, email, picture } = await getGoogleUser({\n      id_token,\n      access_token,\n    });\n\n    \/\/ Check if user is verified\n    if (!verified_email) {\n      return next(new AppError('Google account not verified', 403));\n    }\n\n    \/\/ Update user if user already exist or create new user\n    const user = await findAndUpdateUser(\n      { email },\n      {\n        name,\n        photo: picture,\n        email,\n        provider: 'Google',\n        verified: true,\n      },\n      { upsert: true, runValidators: false, new: true, lean: true }\n    );\n\n    if (!user)\n      return res.redirect(`${config.get&lt;string&gt;('origin')}\/oauth\/error`);\n\n    \/\/ Create access and refresh token\n    const { access_token: accessToken, refresh_token } = await signToken(user);\n\n    \/\/ Send cookie\n    res.cookie('refresh-token', refresh_token, refreshTokenCookieOptions);\n    res.cookie('access-token', accessToken, accessTokenCookieOptions);\n    res.cookie('logged_in', true, {\n      expires: new Date(\n        Date.now() + config.get&lt;number&gt;('accessTokenExpiresIn') * 60 * 1000\n      ),\n    });\n\n    res.redirect(`${config.get&lt;string&gt;('origin')}${pathUrl}`);\n  } catch (err: any) {\n    console.log('Failed to authorize Google User', err);\n    return res.redirect(`${config.get&lt;string&gt;('origin')}\/oauth\/error`);\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create the Route<\/h3>\n\n\n\n<p>Now create a <code>session.routes.ts<\/code> file in the routes folder and add the code below.<\/p>\n\n\n\n<p><strong>src\/routes\/session.routes.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport express from 'express';\nimport { googleOauthHandler } from '..\/controllers\/auth.controller';\n\nconst router = express.Router();\n\nrouter.get('\/oauth\/google', googleOauthHandler);\n\nexport default router;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Register the Session Router<\/h3>\n\n\n\n<p>Next, add the router defined above to the middleware stack in the <code>app.ts<\/code> file.<\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport sessionRouter from '.\/routes\/session.route';\n\n\/\/ ? Register the session router\napp.use('\/api\/sessions', sessionRouter);\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Congrats for reaching the end. In this article, you learned how to implement Google OAuth in React.js, Node.js, and MongoDB applications without using the Passport.js library.<\/p>\n\n\n\n<p>You can find the complete code used in this <a href=\"https:\/\/github.com\/wpcodevo\/JWT_Authentication_React\/tree\/react_node_oauth_google\" target=\"_blank\" rel=\"noreferrer noopener\">tutorial on GitHub<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you&#8217;ll learn how to implement Google OAuth Authentication with React.js and Node.js without using Passport. You&#8217;ll also learn how to send JWT&#8230;<\/p>\n","protected":false},"author":1,"featured_media":2244,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[47],"tags":[42,56,55],"class_list":["post-2203","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-nodejs","tag-nodejs-api","tag-react","tag-reactjs"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/2203","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=2203"}],"version-history":[{"count":2,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/2203\/revisions"}],"predecessor-version":[{"id":11356,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/2203\/revisions\/11356"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/2244"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=2203"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=2203"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=2203"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}