Firebase Authentication in React: A Simple Step-by-Step Guide
Fonyuy Gita

Fonyuy Gita @fonyuygita

About: Fetch->Decode->Execute->Store

Location:
Cameroon-Bamenda
Joined:
Oct 5, 2023

Firebase Authentication in React: A Simple Step-by-Step Guide

Publish Date: May 17
3 0

As Much as I know I have twice to learn

Authentication is a fundamental requirement for most web applications. In this tutorial, I'll walk you through implementing Firebase Authentication in a React application without using the Context API. We'll create sign-up and sign-in pages and set up navigation to a home page after successful authentication.

What We'll Build

  • A React application with Firebase Authentication
  • Sign-up page for new users
  • Sign-in page for existing users
  • Protected home page that requires authentication
  • Simple navigation between these pages

Prerequisites

  • Basic knowledge of React
  • Node.js and npm installed on your computer
  • A Google account to set up Firebase

Step 1: Set Up a New React Project

Let's start by creating a new React application:

npx create-react-app firebase-auth-app
cd firebase-auth-app
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Required Dependencies

We'll need to install Firebase and React Router:

npm install firebase react-router-dom
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Firebase Project

  1. Go to the Firebase Console
  2. Click "Add project" and follow the setup instructions
  3. Once your project is created, click on the web icon (</>) to add a web app to your project
  4. Register your app with a nickname (e.g., "firebase-auth-app")
  5. Copy the Firebase configuration object that looks like this:
const firebaseConfig = {
  apiKey: "your-api-key",
  authDomain: "your-project-id.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project-id.appspot.com",
  messagingSenderId: "your-messaging-sender-id",
  appId: "your-app-id"
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Enable Authentication Methods in Firebase

  1. In the Firebase Console, navigate to "Authentication" in the left sidebar
  2. Click on "Get started"
  3. Enable "Email/Password" authentication by clicking on it and toggling the switch to "Enable"
  4. Click "Save"

Step 5: Set Up Firebase in Your React App

Create a new file src/firebase.js to initialize Firebase:

// src/firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  // Replace with your Firebase config object
  apiKey: "your-api-key",
  authDomain: "your-project-id.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project-id.appspot.com",
  messagingSenderId: "your-messaging-sender-id",
  appId: "your-app-id"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export default app;
Enter fullscreen mode Exit fullscreen mode

Step 6: Create the Sign-Up Component

// src/components/Signup.js
import React, { useState } from 'react';
import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase';
import { Link, useNavigate } from 'react-router-dom';

function Signup() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  async function handleSubmit(e) {
    e.preventDefault();

    if (password !== confirmPassword) {
      return setError('Passwords do not match');
    }

    try {
      setError('');
      setLoading(true);
      await createUserWithEmailAndPassword(auth, email, password);
      navigate('/');
    } catch (error) {
      setError('Failed to create an account: ' + error.message);
    }

    setLoading(false);
  }

  return (
    <div className="signup-container">
      <div className="signup-form">
        <h2>Sign Up</h2>
        {error && <div className="error-message">{error}</div>}
        <form onSubmit={handleSubmit}>
          <div className="form-group">
            <label>Email</label>
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              required
            />
          </div>
          <div className="form-group">
            <label>Password</label>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
            />
          </div>
          <div className="form-group">
            <label>Confirm Password</label>
            <input
              type="password"
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
              required
            />
          </div>
          <button disabled={loading} type="submit" className="submit-button">
            Sign Up
          </button>
        </form>
        <div className="login-link">
          Already have an account? <Link to="/login">Log In</Link>
        </div>
      </div>
    </div>
  );
}

export default Signup;
Enter fullscreen mode Exit fullscreen mode

Step 7: Create the Login Component

// src/components/Login.js
import React, { useState } from 'react';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../firebase';
import { Link, useNavigate } from 'react-router-dom';

function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  async function handleSubmit(e) {
    e.preventDefault();

    try {
      setError('');
      setLoading(true);
      await signInWithEmailAndPassword(auth, email, password);
      navigate('/');
    } catch (error) {
      setError('Failed to log in: ' + error.message);
    }

    setLoading(false);
  }

  return (
    <div className="login-container">
      <div className="login-form">
        <h2>Log In</h2>
        {error && <div className="error-message">{error}</div>}
        <form onSubmit={handleSubmit}>
          <div className="form-group">
            <label>Email</label>
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              required
            />
          </div>
          <div className="form-group">
            <label>Password</label>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
            />
          </div>
          <button disabled={loading} type="submit" className="submit-button">
            Log In
          </button>
        </form>
        <div className="signup-link">
          Need an account? <Link to="/signup">Sign Up</Link>
        </div>
      </div>
    </div>
  );
}

export default Login;
Enter fullscreen mode Exit fullscreen mode

Step 8: Create the Home Component

// src/components/Home.js
import React, { useState, useEffect } from 'react';
import { onAuthStateChanged, signOut } from 'firebase/auth';
import { auth } from '../firebase';
import { useNavigate } from 'react-router-dom';

function Home() {
  const [currentUser, setCurrentUser] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setCurrentUser(user);
      } else {
        navigate('/login');
      }
    });

    return () => unsubscribe();
  }, [navigate]);

  async function handleLogout() {
    try {
      await signOut(auth);
      navigate('/login');
    } catch (error) {
      console.error('Failed to log out:', error);
    }
  }

  if (!currentUser) return <div>Loading...</div>;

  return (
    <div className="home-container">
      <h2>Welcome to Your Dashboard!</h2>
      <p>You are logged in as: {currentUser.email}</p>
      <button onClick={handleLogout} className="logout-button">
        Log Out
      </button>
    </div>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Step 9: Create a Simple Auth Check Component

Instead of using the Context API, we'll create a simple component to check if a user is authenticated:

// src/components/AuthCheck.js
import React, { useState, useEffect } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import { auth } from '../firebase';
import { Navigate } from 'react-router-dom';

function AuthCheck({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setIsAuthenticated(!!user);
      setIsLoading(false);
    });

    return () => unsubscribe();
  }, []);

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <Navigate to="/login" />;
  }

  return children;
}

export default AuthCheck;
Enter fullscreen mode Exit fullscreen mode

Step 10: Set Up App Routing

Now, let's update the main App component to include all our routes:

// src/App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import AuthCheck from './components/AuthCheck';
import Signup from './components/Signup';
import Login from './components/Login';
import Home from './components/Home';
import './App.css';

function App() {
  return (
    <Router>
      <div className="app-container">
        <Routes>
          <Route path="/" element={
            <AuthCheck>
              <Home />
            </AuthCheck>
          } />
          <Route path="/signup" element={<Signup />} />
          <Route path="/login" element={<Login />} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Step 11: Add Basic CSS Styling

Let's add some minimal CSS to make our app look decent:

/* src/App.css */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background-color: #f4f4f4;
}

.app-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 20px;
}

.signup-container,
.login-container,
.home-container {
  background-color: white;
  border-radius: 5px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  padding: 20px;
  width: 100%;
  max-width: 400px;
}

h2 {
  text-align: center;
  margin-bottom: 20px;
}

.form-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
}

input {
  width: 100%;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

.submit-button,
.logout-button {
  width: 100%;
  padding: 10px;
  background-color: #4285f4;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  margin-top: 10px;
}

.submit-button:hover,
.logout-button:hover {
  background-color: #357ae8;
}

.submit-button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.error-message {
  background-color: #ffebee;
  color: #c62828;
  padding: 10px;
  border-radius: 4px;
  margin-bottom: 15px;
  text-align: center;
}

.login-link,
.signup-link {
  text-align: center;
  margin-top: 15px;
}

.home-container p {
  margin-bottom: 20px;
}
Enter fullscreen mode Exit fullscreen mode

Step 12: Run and Test Your Application

Now let's run the application and test our authentication flow:

npm start
Enter fullscreen mode Exit fullscreen mode

Your React application should open in your default browser at http://localhost:3000.

Testing the Flow

Let's test our authentication flow to make sure everything works as expected:

  1. Sign-up flow:

    • Navigate to /signup
    • Create a new account with email and password
    • After successful registration, you should be redirected to the home page
  2. Login flow:

    • Log out from the home page
    • You should be redirected to the login page
    • Enter your credentials
    • After successful login, you should be redirected to the home page
  3. Authentication protection:

    • Try accessing the home page (/) directly when not logged in
    • You should be redirected to the login page

Understanding How It Works

Authentication Flow

  1. Firebase Setup: We initialize Firebase and its authentication service in firebase.js.

  2. Sign Up:

    • When a user submits the sign-up form, we call Firebase's createUserWithEmailAndPassword function
    • If successful, we navigate to the home page
    • If there's an error, we display it to the user
  3. Login:

    • When a user submits the login form, we call Firebase's signInWithEmailAndPassword function
    • If successful, we navigate to the home page
    • If there's an error, we display it to the user
  4. Authentication Check:

    • We use Firebase's onAuthStateChanged to listen for authentication state changes
    • In the AuthCheck component, we redirect unauthenticated users to the login page
    • In the Home component, we use this to get the current user's information
  5. Logout:

    • When a user clicks the logout button, we call Firebase's signOut function
    • After logging out, we redirect the user to the login page

Common Issues and Troubleshooting

Firebase Initialization Errors

If you see an error like "Firebase App named '[DEFAULT]' already exists", make sure you're only initializing Firebase once in your application.

Authentication Errors

  • Check that you've enabled Email/Password authentication in the Firebase Console
  • Make sure password meets Firebase's minimum requirements (at least 6 characters)
  • Verify that you're using the correct Firebase configuration

Routing Issues

  • Ensure all routes are properly defined in App.js
  • Verify that AuthCheck is correctly protecting your routes

Next Steps

Now that you have a basic authentication system in place, you might want to:

  1. Add password reset functionality
  2. Implement social media login options (Google, Facebook, etc.)
  3. Create a user profile page with more information
  4. Add email verification
  5. Implement more robust error handling

Conclusion

You've successfully built a React application with Firebase Authentication! You now have functional sign-up and sign-in pages that redirect users to a protected home page after successful authentication.

This implementation uses a straightforward approach without the Context API, making it easier to understand and maintain. You can build upon this foundation to create more complex applications with user authentication.

Happy coding!

Comments 0 total

    Add comment