Seamless User Authentication with Google OAuth in React and Express
Nov 14, 2023
Nov 14, 2023
Nov 14, 2023
Hey there, tech enthusiasts! 👋 Rahil here, and I'm thrilled to take you on an exciting journey into implementing Google OAuth for our Study Planner app. In Week 3, we've dived headfirst into user authentication, and I'm here to guide you through the process step by step. Let's get started!
NPM Package for Google auth
The package I used to implement google oauth was called react-oauth/google
. The command to install is:
npm install @react-oauth/google
Setting Up Google Cloud Project Credentials
First things first, to use Google OAuth, we need credentials from the Google Cloud project.
During setup, remember to add the following authorized JavaScript origins (Note: No trailing slash at the end):
http://localhost:3000
http://localhost:5000
Also, add the domain:
http://localhost
Missing out on these details could lead to some pesky errors down the road.
After setting up the credentials you will get your client id
and client secret
which will come in handy down the road.
Incorporating Google OAuth in React
If you've been using Create React App, you likely have an index.js
file. This is where the magic begins. We'll start by adding the GoogleOAuthProvider
context.
Don't forget to import the package:
import { GoogleOAuthProvider } from '@react-oauth/google';
The code snippet for reference:
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<GoogleOAuthProvider clientId='YourClientIDHere'>
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/*" element={<App />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
</GoogleOAuthProvider>
);
Adding the Google Login Button
Now, let's keep it simple. The package provides a GoogleLogin
component that makes adding a login button a breeze:
import { GoogleLogin } from '@react-oauth/google';
<GoogleLogin
onSuccess={handleLogin}
onError={handleFailure}
buttonText=""
useOneTap
cookiePolicy={'single_host_origin'}
/>
Where GoogleLogin
is a component for signing in which looks like this:
Also notice the component has onSuccess
and onError
props which are functions that execute on success and failure of login respectively.
Handling the Login Process
Time to get your hands dirty with code! Here's the handleLogin
function:
const handleLogin = async (googleData) => {
const res = await fetch('/api/google-login', {
method: 'POST',
body: JSON.stringify({
token: googleData.credential,
}),
headers: {
'Content-Type': 'application/json',
},
});
const data = await res.json();
setLoginData(data);
localStorage.setItem('loginData', JSON.stringify(data));
};
This function is called upon a successful login and sends a POST request to the backend with the JWT token. The JWT token is crucial for user authentication. It looks something like this:
{
"credential": "eyJhbGmtpZCI6IjI3Yjg2ZGM2OTM4ZGMzMjdiMjA0MzMzYTI1MGViYjQzYjMyZTRiM2MiLCJ0eXAiOiJKV1QifQ.eyJpcdHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NjkzNDIxNTMsImF1ZCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjU5MjEyMTA2MTU3MjIyMTM4NCIsImVtYWlsIjoia2hvai5iYWRhbWlAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsIm5hbWUiOiJLaG9qIEJhZGFtaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BTG01d3UzMm1lckdfLTlpbkhSMkVFd1VZUDFxbndiZkg4TjJjZnlfcUhtLT1zOTYtYyIsImdpdmVuX25hbWUiOiJLaG9qIiwiZmFtaWx5X25hbWUiOiJCYWRhbWkiLCJpYXQiOjE2NjkzNDI0NTMsImV4cCI6MTY2OTM0NjA1MywianRpIjoiZTIzN2VjMGVmOWFkYWI2NjNhNGNkMmU4NjcxYWZiMjVlMzQ4NmRiYiJ9.qtPkkbtNLU9hKnsbDyFKTRm4Drdr1M0xoWgkyz4ymnxqg-HWh4ZKHARTzH7X6Gup-i5rJfQlA-AZ6XK7GvEkc_gARAu6azmpSbxgsR1etYhG_olqpI5j8Eb06TIGvKQZdRuR2O2wNhqOv0bpGGuMf8IrJzl1RgjfYu_YtHaCgsGGBmKwC-cRDwEuvTrCRiQe-jZkg0Vjmo8--2pkubfGXxbQJqFhi7ZLhTxsCXVCejpHAfzcpD5LszoymsVxcqAZjQOUcL8Fy78JydpEMghH-_unsESSkli8CpsAjTplCHywNu1_A3JIX59VoOh_dBet4HgvEzrlFjcsYSb8umvxIg",
"clientId": "27111941a4cgdet6cpf802focedi.apps.googleusercontent.com",
"select_by": "btn_confirm"
}
A resource I found helpful to learn about JWT was: What Is JWT and Why Should You Use JWT by web dev simplified.
Deciphering the JWT Token
To get user info like first name, last name, email, and profile photo, we need to decipher the JWT token in the backend with our secret key.
The backend is in express and we set up a basic server.js file like all express projects. This is the code for the api call on the backend.
function upsert(array, item) {
const i = array.findIndex((_item) => _item.email === item.email);
if (i > -1) array[i] = item;
else array.push(item);
}
app.post('/api/google-login', async (req, res) => {
console.log(req.body)
const { token } = req.body;
const ticket = await client.verifyIdToken({
idToken: token,
//audience: process.env.CLIENT_ID,
});
console.log(ticket)
const { name, email, picture } = ticket.getPayload();
upsert(users, { name, email, picture });
res.status(201);
res.json({ name, email, picture });
});
This code does a few things:
Logs the incoming request body (remove this in production).
Extracts the Google ID token from the request body.
Verifies the token using Google's client library, resulting in a ticket.
Extracts user information (name, email, picture) from the ticket payload.
Calls the upsert function to add or update user data in the `users` array.
Responds with user data as JSON and sets the HTTP status code to 201 (Created).
The 'audience: process.env.CLIENT_ID', line is commented out as it gave me an error while uncommented. The tutorial I was following had that line and it was working for them but for me the error was resolved by simply commenting that line out.
That's it! Now this user info is sent back to the frontend, and we can store it in a userState.
For a visual guide, check out our demo:
Conclusion
This was our journey through implementing user authentication with Google OAuth in React and Express.
Now, you're equipped to enhance your apps with seamless Google OAuth authentication.
Happy coding, and stay tuned for more tech insights from HackeroX!
Hey there, tech enthusiasts! 👋 Rahil here, and I'm thrilled to take you on an exciting journey into implementing Google OAuth for our Study Planner app. In Week 3, we've dived headfirst into user authentication, and I'm here to guide you through the process step by step. Let's get started!
NPM Package for Google auth
The package I used to implement google oauth was called react-oauth/google
. The command to install is:
npm install @react-oauth/google
Setting Up Google Cloud Project Credentials
First things first, to use Google OAuth, we need credentials from the Google Cloud project.
During setup, remember to add the following authorized JavaScript origins (Note: No trailing slash at the end):
http://localhost:3000
http://localhost:5000
Also, add the domain:
http://localhost
Missing out on these details could lead to some pesky errors down the road.
After setting up the credentials you will get your client id
and client secret
which will come in handy down the road.
Incorporating Google OAuth in React
If you've been using Create React App, you likely have an index.js
file. This is where the magic begins. We'll start by adding the GoogleOAuthProvider
context.
Don't forget to import the package:
import { GoogleOAuthProvider } from '@react-oauth/google';
The code snippet for reference:
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<GoogleOAuthProvider clientId='YourClientIDHere'>
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/*" element={<App />} />
</Routes>
</BrowserRouter>
</React.StrictMode>
</GoogleOAuthProvider>
);
Adding the Google Login Button
Now, let's keep it simple. The package provides a GoogleLogin
component that makes adding a login button a breeze:
import { GoogleLogin } from '@react-oauth/google';
<GoogleLogin
onSuccess={handleLogin}
onError={handleFailure}
buttonText=""
useOneTap
cookiePolicy={'single_host_origin'}
/>
Where GoogleLogin
is a component for signing in which looks like this:
Also notice the component has onSuccess
and onError
props which are functions that execute on success and failure of login respectively.
Handling the Login Process
Time to get your hands dirty with code! Here's the handleLogin
function:
const handleLogin = async (googleData) => {
const res = await fetch('/api/google-login', {
method: 'POST',
body: JSON.stringify({
token: googleData.credential,
}),
headers: {
'Content-Type': 'application/json',
},
});
const data = await res.json();
setLoginData(data);
localStorage.setItem('loginData', JSON.stringify(data));
};
This function is called upon a successful login and sends a POST request to the backend with the JWT token. The JWT token is crucial for user authentication. It looks something like this:
{
"credential": "eyJhbGmtpZCI6IjI3Yjg2ZGM2OTM4ZGMzMjdiMjA0MzMzYTI1MGViYjQzYjMyZTRiM2MiLCJ0eXAiOiJKV1QifQ.eyJpcdHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NjkzNDIxNTMsImF1ZCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjU5MjEyMTA2MTU3MjIyMTM4NCIsImVtYWlsIjoia2hvai5iYWRhbWlAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjI3MTExOTQ1NzA0MS00Y2ZxdWdtNXY1aTFhNGNnZGV0NmNwZjgwMmZvY2VkaS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsIm5hbWUiOiJLaG9qIEJhZGFtaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BTG01d3UzMm1lckdfLTlpbkhSMkVFd1VZUDFxbndiZkg4TjJjZnlfcUhtLT1zOTYtYyIsImdpdmVuX25hbWUiOiJLaG9qIiwiZmFtaWx5X25hbWUiOiJCYWRhbWkiLCJpYXQiOjE2NjkzNDI0NTMsImV4cCI6MTY2OTM0NjA1MywianRpIjoiZTIzN2VjMGVmOWFkYWI2NjNhNGNkMmU4NjcxYWZiMjVlMzQ4NmRiYiJ9.qtPkkbtNLU9hKnsbDyFKTRm4Drdr1M0xoWgkyz4ymnxqg-HWh4ZKHARTzH7X6Gup-i5rJfQlA-AZ6XK7GvEkc_gARAu6azmpSbxgsR1etYhG_olqpI5j8Eb06TIGvKQZdRuR2O2wNhqOv0bpGGuMf8IrJzl1RgjfYu_YtHaCgsGGBmKwC-cRDwEuvTrCRiQe-jZkg0Vjmo8--2pkubfGXxbQJqFhi7ZLhTxsCXVCejpHAfzcpD5LszoymsVxcqAZjQOUcL8Fy78JydpEMghH-_unsESSkli8CpsAjTplCHywNu1_A3JIX59VoOh_dBet4HgvEzrlFjcsYSb8umvxIg",
"clientId": "27111941a4cgdet6cpf802focedi.apps.googleusercontent.com",
"select_by": "btn_confirm"
}
A resource I found helpful to learn about JWT was: What Is JWT and Why Should You Use JWT by web dev simplified.
Deciphering the JWT Token
To get user info like first name, last name, email, and profile photo, we need to decipher the JWT token in the backend with our secret key.
The backend is in express and we set up a basic server.js file like all express projects. This is the code for the api call on the backend.
function upsert(array, item) {
const i = array.findIndex((_item) => _item.email === item.email);
if (i > -1) array[i] = item;
else array.push(item);
}
app.post('/api/google-login', async (req, res) => {
console.log(req.body)
const { token } = req.body;
const ticket = await client.verifyIdToken({
idToken: token,
//audience: process.env.CLIENT_ID,
});
console.log(ticket)
const { name, email, picture } = ticket.getPayload();
upsert(users, { name, email, picture });
res.status(201);
res.json({ name, email, picture });
});
This code does a few things:
Logs the incoming request body (remove this in production).
Extracts the Google ID token from the request body.
Verifies the token using Google's client library, resulting in a ticket.
Extracts user information (name, email, picture) from the ticket payload.
Calls the upsert function to add or update user data in the `users` array.
Responds with user data as JSON and sets the HTTP status code to 201 (Created).
The 'audience: process.env.CLIENT_ID', line is commented out as it gave me an error while uncommented. The tutorial I was following had that line and it was working for them but for me the error was resolved by simply commenting that line out.
That's it! Now this user info is sent back to the frontend, and we can store it in a userState.
For a visual guide, check out our demo:
Conclusion
This was our journey through implementing user authentication with Google OAuth in React and Express.
Now, you're equipped to enhance your apps with seamless Google OAuth authentication.
Happy coding, and stay tuned for more tech insights from HackeroX!