Skip to main content

Protect Custom Routes in Docusaurus using Google Firebase

Massoud Maboudi

Docusaurus is a well-designed, and user-friendly documentation generator tool with a modern and optimized codebase that supports multiple plugins to make it easier for either a normal user or a developer to ship out a documentation website swiftly.

Background

In my previous article, we already figured out how to add an authentication layer to a Docusaurus website and secure the whole website using Google Firebase.

Though, what if you want to:

  • Secure one of your specific routes like /docs/* or /admin/* to provide enterprise documentation for your customers?

OR

  • Create an internal documentation website to provide in-depth details for your products only for your internal team.

Goal

The main outcome of this guide is to provide a step-by-step guide on how you can protect a specific route like /docs/* in your Docusaurus website using Google Firebase.

Please find the more advanced authentication flows using Docusaurus and Google Firebase here.

Getting Started

Before we dive into the tutorial, let's assume you've already set up a simple authentication layer as explained in my previous article. This guide is the next step after that.

Prerequisite

  • A Google Firebase account
  • A running Docusaurus website with a simple authentication layer is described here

Docusaurus Setup

This section contains all the changes and new files needed to be added to the Docusaurus website.

Final Directory Hierarchy

Here is the final directory tree which mostly shows the changes added/modified to the base project. Use it as a reference.

└── src
└── utils
└── constants.js

Adding Required Files

In this section, I will provide the changes or additional files required for this feature to work.

tip

On the first line of each code block, you can find the relative path of the file.

  • constants.js: This file contains the most important constant values giving easier management of the project.

    In this file, we have a constant value named PROTECTED_PATHS that contains the list of the routes or paths that need to be protected.

    As of now, the BASE URL is protected which means the whole website is protected and you need to authenticate when you open the website.

    Though we don't want to protect the whole website. We want to ask the user to authenticate only if he wants to access the /docs/* route and any other route under this route for example /docs/intro.

    The reason we use this constant value is hidden in the Auth component that is described below.

    The logic is:

    1. If the requested route is one of the values in the list of routes we have in PROTECTED_PATHS (line 49), then it will redirect to the /login page (line 50); Otherwise, will redirect to the requested page which means the path is not protected.
    2. When it is redirected to the /login page:
      • If the user is authenticated (line 40), it will redirect to the requested page (line 46).
      • If the user is not authenticated (line 40), it will show the /login page (line 51).

    // src/components/Auth/index.js

    import React, { useEffect, useState } from "react";

    import firebase from "firebase/compat/app";
    import { onAuthStateChanged, signOut, getAuth } from "firebase/auth";

    import { Redirect, useLocation } from "@docusaurus/router";

    import { firebaseConfig } from "../../config/firebase-config";
    import { Login } from "../Login";
    import Loading from "../Loading";
    import {
    BASE,
    LOGOUT_PATH,
    LOGIN_PATH,
    PROTECTED_PATHS,
    } from "../../utils/constants";

    firebase.initializeApp(firebaseConfig);

    export const auth = getAuth();

    export function AuthCheck({ children }) {
    const [user, setUser] = useState(null);
    const [authLoading, setAuthLoading] = useState(true);

    useEffect(() => {
    onAuthStateChanged(auth, (user) => {
    setUser(user);
    setAuthLoading(false);
    });
    });

    const location = useLocation();
    let from = location.pathname;

    if (authLoading) return <Loading />;

    if (user?.email) {
    if (from === LOGOUT_PATH) {
    signOut(auth);
    return <Redirect to={BASE} from={LOGOUT_PATH} />;
    } else if (from === LOGIN_PATH) return <Redirect to={BASE} from={from} />;

    return children;
    } else {
    if (from === LOGOUT_PATH) return <Redirect to={BASE} from={from} />;
    else if (PROTECTED_PATHS.filter((x) => from.includes(x)).length)
    return <Login />;
    else if (from === LOGIN_PATH) return <Login />;
    return children;
    }
    }

    So, to utilize the above logic:

    1. Remove BASE from PROTECTED_PATHS.
    2. Add /docs to PROTECTED_PATHS.

    // src/utils/constants.js

    export const LOGIN_PATH = "/login";
    export const LOGOUT_PATH = "/logout";
    export const AUTHENTICATED = "authenticated";
    export const BASE = "/";

    export const LOGOUT_BUTTON = "Logout";
    export const LOGIN_BUTTON = "Login";

    // Add the protected paths here
    export const PROTECTED_PATHS = ["/docs"];

Source Code

You may find the complete source code of this project here.

info

The code repository shared above consists of a few branches that all are related to the Docusaurus Authentication using Google Firebase. Though, you can find the changes for this article in the branch called feat-specific-route-protection.

danger

DO NOT upload any .env to your public repository.

To run the project on your machine, you only need to fill up the .env.local file as described WITH YOUR VALUES.

To use your .env values in production, you need to add them to your hosting.

Below I listed a few hosting options and how you can upload your .env variables as well.

Complete Docusaurus Authentication Series

Please find the complete roadmap here.

In this series, I will provide a step-by-step guide on how to add an authentication layer to your Docusaurus website using well-known auth providers like AWS Cognito, Google Firebase, Auth0, etc.

Also, you can find more advanced auth flows for complicated scenarios as well.

Support

In the end, if you find the articles useful, don’t forget to support me at least with a message :)