Introduction to Firebase Authentication
Firebase Authentication provides a complete identity solution for your React applications, supporting multiple authentication methods including email/password, social logins, and phone authentication. In this guide, we'll implement a robust authentication system from scratch.
Setting Up Firebase
First, let's set up Firebase in your React project:
npm install firebase
Create a Firebase configuration file:
// lib/firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
// ... other config
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
Creating an Authentication Context
We'll use React Context to manage authentication state across our application:
// context/AuthContext.jsx
import { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, signOut } from 'firebase/auth';
import { auth } from '../lib/firebase';
const AuthContext = createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const logout = () => {
return signOut(auth);
};
const value = {
currentUser,
logout
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
Implementing Login and Signup
Create components for user authentication:
// components/Login.jsx
import { useState } from 'react';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../lib/firebase';
export default function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
await signInWithEmailAndPassword(auth, email, password);
} catch (error) {
setError(error.message);
}
};
return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Login</button>
</form>
);
}
Protected Routes
Implement route protection to secure authenticated areas:
// components/ProtectedRoute.jsx
import { useAuth } from '../context/AuthContext';
import { Navigate } from 'react-router-dom';
export default function ProtectedRoute({ children }) {
const { currentUser } = useAuth();
return currentUser ? children : <Navigate to="/login" />;
}
Best Practices for Security
- Environment Variables: Store Firebase config in environment variables
- Security Rules: Configure Firestore security rules properly
- Input Validation: Validate user inputs on both client and server
- Error Handling: Implement proper error handling for auth failures
Conclusion
Firebase Authentication provides a robust foundation for user management in React applications. By following these patterns and best practices, you can build secure, scalable authentication systems that provide excellent user experiences.