Google OAuth Implementation with Firebase
Table of Contents
Introduction
This guide builds upon our previous Redux Toolkit and Redux Persist implementations to create a complete authentication system using Google OAuth with Firebase. We'll cover both frontend and backend integration, focusing on secure and efficient implementation.
Firebase Setup and Configuration
Step-by-Step Firebase Setup
Before implementing Google OAuth, we need to set up Firebase in our project. Here's the detailed process:
- Navigate to Firebase Console
- Access the main console interface
- Create a new project
- Look for the web icon (
</>
) in the dashboard - Register your app and activate the configuration and add the configuration code in a src folder . Discussed in the Front-End Intergration.
Frontend Integration
After setting up the Firebase project, we need to integrate it into our frontend:
- Install Firebase in your project:
npm install firebase
- Create
firebaseConfig.js
in your src root folder - Configure environment variables for sensitive data
- firebaseConfig.js
.env
file for security.
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_apiKey,
authDomain: import.meta.env.VITE_FIREBASE_authDomain,
projectId: import.meta.env.VITE_FIREBASE_projectId,
storageBucket: import.meta.env.VITE_FIREBASE_storageBucket,
messagingSenderId: import.meta.env.VITE_FIREBASE_messagingSenderId,
appId: import.meta.env.VITE_FIREBASE_appId,
measurementId: import.meta.env.VITE_FIREBASE_measurementId
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);
Authentication Logic Implementation
After configuration, we implement the authentication logic:
- Import and set up Google Auth Provider:
import { GoogleAuthProvider, getAuth } from 'firebase/auth'; const provider = new GoogleAuthProvider(); provider.setCustomParameters({ prompt: 'select_account' // Ensures account selection prompt });
- Initialize Firebase auth:
import { app } from '../firebaseConfig'; const auth = getAuth(app);
- Implement sign-in logic:
const handleGoogleSignIn = async () => { try { const result = await signInWithPopup(auth, provider); // Process authentication result } catch (error) { console.error('Authentication error:', error); } };
Backend Integration and State Management
Backend Authentication Flow
The backend handles user authentication through a sophisticated flow:
- Receive Google OAuth data from frontend
- Check if user exists in database
- If user exists:
- Generate JWT token
- Send user data back to frontend
- If user doesn't exist:
- Create new user record
- Generate secure password hash
- Save user with profile photo
- Generate JWT token
export const googleouth = async (req, res, next) => {
const { email, displayName, photoURL } = req.body;
try {
const user = await User.findOne({email});
if(user){
// Handle existing user
const token = jwt.sign({id:user._id}, process.env.JWT_SECRET);
// ... token handling
} else {
// Create new user
const newUser = new User({
username: displayName.toLowerCase().split(' ').join('') +
Math.random().toString(9).slice(-4),
email,
photoURL,
});
// ... user creation and token generation
}
} catch(error) {
next(errorHandler(500, 'Internal Server Error'));
}
};
Redux Integration
After successful authentication, we integrate with Redux Toolkit for state management:
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { signInSuccess } from '../redux/user/authSlice';
// In your component
const dispatch = useDispatch();
const navigate = useNavigate();
// After successful authentication
if(response.status === 200){
dispatch(signInSuccess(response.data));
navigate('/');
}
Frequently Asked Questions
1. How do you handle children props in React components?
React components accept children props for flexible content rendering:
const CustomComponent = ({ children, ...props }) => (
<div {...props}>
{children}
</div>
);
// Usage
<CustomComponent
className="custom-class"
>
<p>This is a custom component</p>
</CustomComponent>
2. What are the best practices for implementing Google OAuth?
Key considerations include:
- Using environment variables for Firebase configuration
- Implementing proper error handling and loading states
- Securing user data and tokens
- Following OAuth 2.0 best practices
- Implementing proper state management with Redux Toolkit
3. How do you handle authentication errors?
Implement comprehensive error handling:
try {
const result = await signInWithPopup(auth, provider);
// Handle successful authentication
} catch (error) {
switch (error.code) {
case 'auth/popup-closed-by-user':
console.log('User closed the popup');
break;
case 'auth/cancelled-popup-request':
console.log('Multiple popup requests');
break;
default:
console.error('Authentication error:', error);
}
}
4. How do you ensure secure token storage?
Best practices for token storage include:
- Using HTTP-only cookies for JWT storage
- Implementing proper token expiration
- Using secure and sameSite cookie attributes
- Never storing sensitive data in localStorage
Source Code & Commits
Access the complete source code and implementation details:
- Frontend Implementation: View Code on GitHub Commit
- Backend Implementation: View Code on GitHub Commit