Implementing Authentication with Firebase in a Next.js App

Saturday, January 25, 2025

Authentication is a core feature in most modern web applications, and Firebase Authentication makes it easy to implement user authentication with various providers like Email/Password, Google, and more. In this tutorial, we'll walk through how to integrate Firebase Authentication into a Next.js app, covering everything from setup to protecting routes and displaying user data.


Setting Up Firebase

To get started, you'll need to set up a Firebase project.

Create a Firebase Project

  • Go to the Firebase Console and create a new project.
  • Once created, navigate to the "Authentication" section and enable the sign-in methods you want (e.g., Email/Password, Google, etc.).
  • Get Your Firebase Config

  • In the Firebase Console, go to Project Settings > General and scroll down to find your Firebase SDK configuration.
  • Copy the configuration object, which looks something like this:
  • Language: javascript
    const firebaseConfig = {
      apiKey: "your-api-key",
      authDomain: "your-app.firebaseapp.com",
      projectId: "your-project-id",
      storageBucket: "your-app.appspot.com",
      messagingSenderId: "your-sender-id",
      appId: "your-app-id",
    };

    Installing Firebase in Your Next.js Project

    Install Firebase SDK

    Run the following command to install the Firebase library:

    Language: shell
    npm install firebase

    Set Up Firebase Client Initialization

    Create a firebase.js file in your project to initialize Firebase:

    Language: javascript
    import { initializeApp } from "firebase/app";
    import { getAuth } from "firebase/auth";
    
    const firebaseConfig = {
      apiKey: "your-api-key",
      authDomain: "your-app.firebaseapp.com",
      projectId: "your-project-id",
      storageBucket: "your-app.appspot.com",
      messagingSenderId: "your-sender-id",
      appId: "your-app-id",
    };
    
    const app = initializeApp(firebaseConfig);
    const auth = getAuth(app);
    
    export { auth };

    Implementing Authentication

    User Registration

    Create a function to register users:

    Language: javascript
    import { createUserWithEmailAndPassword } from "firebase/auth";
    import { auth } from "./firebase";
    
    export const registerUser = async (email, password) => {
      try {
        const userCredential = await createUserWithEmailAndPassword(auth, email, password);
        console.log("User registered:", userCredential.user);
      } catch (error) {
        console.error("Error registering user:", error);
      }
    };

    User Login

    Similarly, create a function to handle user login:

    Language: javascript
    import { signInWithEmailAndPassword } from "firebase/auth";
    
    export const loginUser = async (email, password) => {
      try {
        const userCredential = await signInWithEmailAndPassword(auth, email, password);
        console.log("User logged in:", userCredential.user);
      } catch (error) {
        console.error("Error logging in user:", error);
      }
    };

    Managing Authentication State

    Use Firebase's onAuthStateChanged to listen for authentication changes:

    Language: javascript
    import { onAuthStateChanged } from "firebase/auth";
    import { auth } from "./firebase";
    
    onAuthStateChanged(auth, (user) => {
      if (user) {
        console.log("User is signed in:", user);
      } else {
        console.log("No user signed in.");
      }
    });

    Protecting Pages and API Routes

    Protecting Client-Side Pages

    Redirect unauthenticated users by checking the auth state in Next.js pages:

    Language: javascript
    import { useRouter } from "next/router";
    import { useEffect } from "react";
    import { onAuthStateChanged } from "firebase/auth";
    import { auth } from "../firebase";
    
    const ProtectedPage = () => {
      const router = useRouter();
    
      useEffect(() => {
        const unsubscribe = onAuthStateChanged(auth, (user) => {
          if (!user) {
            router.push("/login");
          }
        });
        return () => unsubscribe();
      }, [router]);
    
      return <div>Welcome to the protected page!</div>;
    };
    
    export default ProtectedPage;
    

    Protecting API Routes

    Secure API routes by verifying Firebase tokens server-side:

    Language: javascript
    import { getAuth } from "firebase-admin/auth";
    
    const adminAuth = getAuth();
    
    export default async function handler(req, res) {
      const token = req.headers.authorization?.split("Bearer ")[1];
    
      try {
        const decodedToken = await adminAuth.verifyIdToken(token);
        console.log("Decoded token:", decodedToken);
        res.status(200).json({ message: "Authenticated!" });
      } catch (error) {
        res.status(401).json({ error: "Unauthorized" });
      }
    }
    

    Adding Social Authentication (Optional)

    If you'd like to add Google sign-in, enable it in Firebase Console and add the following:

    Language: javascript
    import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
    
    export const googleSignIn = async () => {
      const provider = new GoogleAuthProvider();
      try {
        const result = await signInWithPopup(auth, provider);
        console.log("Google sign-in successful:", result.user);
      } catch (error) {
        console.error("Error during Google sign-in:", error);
      }
    };
    

    Displaying User Info

    Show user details in your app:

    Language: javascript
    const UserProfile = () => {
      const [user, setUser] = useState(null);
    
      useEffect(() => {
        const unsubscribe = onAuthStateChanged(auth, (user) => {
          setUser(user);
        });
        return () => unsubscribe();
      }, []);
    
      return user ? (
        <div>
          <p>Welcome, {user.displayName || user.email}!</p>
          <button onClick={() => auth.signOut()}>Logout</button>
        </div>
      ) : (
        <p>Please log in.</p>
      );
    };
    

    Wrapping Up

    In this tutorial, we implemented Firebase Authentication in a Next.js app, covering setup, authentication, route protection, and user state management. Firebase's seamless integration with Next.js allows you to build secure and scalable applications. Experiment with additional features like multi-factor authentication or custom claims to further enhance your app.