Docusaurus authentication with Firebase

Thomasdevshare
5 min readMay 22, 2022

Docusaurus is a great way to ship a beautiful documentation site in no time with a lot of amazing plugins available. In the search for a tool for making internal documentation for our team, I came across Docusaurus. All seemed to be perfect, except for one missing piece, the authentication layer for the documentation so that it stays within the firm.

I searched the web but had no luck, there is a tutorial on this matter. So I decided to roll up my sleeves and get this to work. Luckily, I managed to deliver so I think that I should share this.

In this article, I will show you guys how to set up a basic Google login authentication layer for the Docusaurus documentation using Firebase Authentication (because I host my documentation on Firebase Hosting).

Prerequisite

Before we begin, I assume that you will have some basic understanding of Docusaurus, and Firebase. I will not show how to create a Docusaurus project, you can read its documentation here

Install packages

After initializing your Docusarus project, it is time to install the necessary packages:

npm i firebase docusaurus2-dotenv

With the above command, we will install firebase SDK for the web and docusaurus2-dotenv for using the env file.

Update docusaurus.config.js

Go to your docusaurus.config.js file and update the plugins list

// 
plugins: [
[
'docusaurus2-dotenv',
{
systemvars: true
}
]
],

Docusaur Swizzling

Next up, we will use the Swizzling feature of Docusaurus in order to run logic on the root page. You can use the command line from Docusaurus or just follow my guide to swizzle the Root component by creating a file: src/theme/Root.js and also other files (see file path in the comment). You can also modify the isAllow function to meet your requirements.

import React, {useState} from 'react';
import {signInWithGoogle, logout, auth} from './firebase';
import '../css/login.css';
import Loading from './Loading';

// Default implementation, that you can customize
export default function Root
({children}) {
const [userAuth, setUserAuth] = useState(null);
const [authLoading, setAuthLoading] = useState(true);

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

setAuthLoading(false);
});

const isAllow = () => {
return userAuth?.email;
};

if (authLoading) {
return (
<>
<Loading />
<div style={{display: 'none'}}>{children}</div>
</>
);
}

return (
<React.Fragment>
{isAllow() ? (
<>{children}</>
) : (
<div className="login">
<div className="login__container">
<button className="login__btn login__google" onClick={signInWithGoogle}>
Login with Google
</button>
</div>
</div>
)}
</React.Fragment>
);
}

The loading component for the page before the authentication process finishes.

// src/theme/Loading.jsimport React from 'react';
import PropTypes from 'prop-types';

const Loading = props => {
return (
<div className="overlay">
<div className="overlayDoor" />
<div className="overlayContent">
<div className="loader">
<div className="inner" />
</div>
</div>
</div>
);
};

Loading.propTypes = {};

export default Loading;

The main Firebase authentication logic here, you can modify this part of you want to add more error handling logic.

// src/theme/firebase.jsimport * as firebase from 'firebase/app';
import 'firebase/storage';
import 'firebase/auth';
import {GoogleAuthProvider, getAuth, signInWithPopup, signOut} from 'firebase/auth';

const app = firebase.initializeApp({
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET
});

export const auth = getAuth(app);
export const googleProvider = new GoogleAuthProvider();

export const logout = (afterAction = () => {}) => {
signOut(auth).then(r => afterAction(null));
};

export const signInWithGoogle = async () => {
try {
const res = await signInWithPopup(auth, googleProvider);
} catch (err) {
console.error(err);
alert(err.message);
}
};

Add some CSS to style the login page and the loading state of the page before the authentication process.

/* src/css/login.css */.login {
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
}
.login__container {
display: flex;
flex-direction: column;
text-align: center;
padding: 30px;
}
.login__textBox {
padding: 10px;
font-size: 18px;
margin-bottom: 10px;
}
.login__btn {
padding: 10px;
font-size: 18px;
margin-bottom: 10px;
border: none;
color: white;
background-color: black;
}
.login__google {
background-color: #4285f4;
}
.login div {
margin-top: 7px;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100000000;
}
.overlay .overlayDoor:before, .overlay .overlayDoor:after {
content: '';
position: absolute;
width: 50%;
height: 100%;
background: #111;
transition: 0.5s cubic-bezier(0.77, 0, 0.18, 1);
transition-delay: 0.8s;
}
.overlay .overlayDoor:before {
left: 0;
}
.overlay .overlayDoor:after {
right: 0;
}
.overlay.loaded .overlayDoor:before {
left: -50%;
}
.overlay.loaded .overlayDoor:after {
right: -50%;
}
.overlay.loaded .overlayContent {
opacity: 0;
margin-top: -15px;
}
.overlay .overlayContent {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
transition: 0.5s cubic-bezier(0.77, 0, 0.18, 1);
background: #fff;
}
.overlay .overlayContent .skip {
display: block;
width: 130px;
text-align: center;
margin: 50px auto 0;
cursor: pointer;
color: #fff;
font-family: 'Nunito';
font-weight: 700;
padding: 12px 0;
border: 2px solid #fff;
border-radius: 3px;
transition: 0.2s ease;
}
.overlay .overlayContent .skip:hover {
background: #ddd;
color: #444;
border-color: #ddd;
}
.loader {
width: 128px;
height: 128px;
border: 3px solid #222222;
border-bottom: 3px solid transparent;
border-radius: 50%;
position: relative;
animation: spin 1s linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.loader .inner {
width: 64px;
height: 64px;
border: 3px solid transparent;
border-top: 3px solid #222222;
border-radius: 50%;
animation: spinInner 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes spinInner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-720deg);
}
}

You create a .env file and then paste all your Firebase configuration there:

FIREBASE_API_KEY=
FIREBASE_AUTH_DOMAIN=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=

Remember that you need to create web integration for Firebase to get Firebase configurations and enable Firebase Auth Google login method.

Once it is all done, you can serve it by npm run start and see if it is up and running or not. It should have a login page like this, you can custom this page any way you want:

Bonus: Logout button

Here is a bonus, you can use Docusaurus Swizzling the add the logout button to the header of the page by creating src/theme/ColorModeToggle/index.js

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

export default function ColorModeToggleWrapper(props) {
return (
<>
<a style={{marginRight: 15, cursor: "pointer", color: '#222222'}} onClick={() => logout(() => window.location.reload())}>
Logout
</a>
<ColorModeToggle {...props} />
</>
);
}

This will intervene the ColorModeToggle component and add a logout button to the left of it:

Finally

Thank you for reading this article, I hope that this tutorial will help you craft your amazing internal documentation that will uphold your team knowledge foundation.

You can see the full source code here. You can follow my blog here

--

--

Thomasdevshare

I’m a developer working with React, NodeJS, GCP and Firebase. I spend my free time learning Data Science. Love digging the data with EDA, building a ML model.