background

How To Add Authentication To Docusaurus

Last modified 3/27/2024

post-image

I wanted to create some documentation that was private. That was all I wanted to do. On my journey though I found that there wasn't many resources out there that allowed me to do this. That is when I found an article that didn't work for Docusaurus 3.1 and used Firebase a library that I didn't want to use. So I had to do what all programmers do and that is jag it. See below for how I did it and how you can do it to.

Prerequisites

  1. Working installation of Docusaurus 3.1 - https://docusaurus.io/docs/installation
  2. A library to authenticate with. Try Pocketbase or another SDK.
  3. 30 minutes of your time

Files

Create 3 new files under the theme folder of your Docusaurus. If the theme folder doesn't exist create it.

files to create: theme/ColorModeToggle/index.jstheme/pocketbase.jstheme/Root.js

Note: You can tell that it is the Docusaurus installation by locating the folder that the .docusaurus installation is located at.

Authentication

Here is the below code I used to setup Authentication in pocketbase. Place this code in your pocketbase.js file

import PocketBase from "pocketbase";

//initialize pocketbase
const app = (() => {
  const POCKETBASE_URL = "https://fastpocket.fly.dev" + "/";
  return new PocketBase(POCKETBASE_URL);
})();

//pass authstore to be used to callback when the user is authenticated
export const auth = app.authStore;

//clears the auth and logs the user out
export const logout = (afterAction = () => {}) => {
  app.authStore.clear();
  afterAction(null);
};

//method to authenticate the user with pocketbase
export const signIn = async (email, password) => {
  try {
    await app.collection("user").authWithPassword(email, password);
  } catch (err) {
    console.error(err);
    alert(err.message);
  }
};

While the auth that is happening is being done using Pocketbase the principals apply to any SDK/Auth framework. 

  1. Authenticate
  2. Pass a callback to the Root component to tell whenever the authentication state changes

Root Override

Now we want to override the root component to display our authentication challenge if the user hasn't signed in. We do this by making a conditional render. Here {children} is the documentation and the other code is our custom auth page.

import React, { useState } from "react";
import { signIn, auth } from "./pocketbase";

export default function Root({ children }) {
  const [userAuth, setUserAuth] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (event) => {
    event.preventDefault();
    setLoading(true)
    await signIn(email,password)
    setLoading(false)
  };

  auth.onChange(async function (user) {
    if (user !== null) {
      setUserAuth(user);
    }
  });

  return (
    <>
      {userAuth != "" ? (
        <>{children}</>
      ) : (
        <div
          style={{
            flexGrow: 1,
            display: "flex",
            justifyItems: "center",
            alignItems: "center",
            marginRight: "auto",
            marginLeft: "auto",
            flexDirection: 'column'
          }}
        >
          <form
            onSubmit={handleSubmit}
            style={{
              display: "flex",
              flexDirection: "column",
              width: "200px",
              margin: "auto",
            }}
          >
            <img src="img/combination-icon.png"  />
            <h1 style={{marginLeft: 'auto', marginRight: 'auto'}}>Docs</h1>
            <label htmlFor="email" style={{ marginBottom: "10px" }}>
              Email:
            </label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              style={{
                marginBottom: "10px",
                padding: "5px",
                borderRadius: "5px",
                border: "1px solid #ccc",
              }}
            />
            <label htmlFor="password" style={{ marginBottom: "10px" }}>
              Password:
            </label>
            <input
              type="password"
              id="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              style={{
                marginBottom: "10px",
                padding: "5px",
                borderRadius: "5px",
                border: "1px solid #ccc",
              }}
            />
            <button
              type="submit"
              style={{
                padding: "5px",
                borderRadius: "5px",
                border: "none",
                backgroundColor: "#fd5367",
                color: "white",
                marginTop: '1rem'
              }}
              disabled={loading}
            >
              {loading ? 'Loading...':'Login'}
            </button>
          </form>
        </div>
      )}
    </>
  );
}


Logout Button

Finally we want to create a logout button by extending the ColorModeToggle component that controls the light or dark theme. We are going to add our own <a /> tag that will appear beside the ColorModeToggle component and act as our logout button.

import React from 'react';
import ColorModeToggle from '@theme-original/ColorModeToggle';
import {logout} from '../pocketbase';

export default function ColorModeToggleWrapper(props) {
  return (
    <>
      <ColorModeToggle {...props} />
      <a style={{background: '#fd5469', color: 'white', paddingRight: 10, paddingLeft: 10, paddingBottom: 2, borderRadius: 5, marginLeft: 15, cursor: "pointer"}} onClick={() => logout(() => window.location.reload())}>
        Logout
      </a>
    </>
  );
}

Putting It All Together

Finally you will want to run npm start to get your code to run and you should be greated with a logon screen.

Explaination

Docusaurus provides you with the ability to override the React components that compose the documentation. They do this through swizzling which is the lingo for basically overriding the components through the Docusaurus/themes folder. What we are doing is overriding the root component with our authentication and ensuring that the user has to pass the authentication challenge before entering the site.

Build Fast With FastPocket

If this article helped you develop fast you should see what you can do with FastPocket. FastPocket is the number 1 Pocketbase/React boilerplate to get you building amazing apps very quickly. It features everything that you need to start making money with your own apps from Authentication to Payments. Get it today!