DEV Community

Cover image for Docusaurus authentication with Entra ID and MSAL
Igor Bertnyk
Igor Bertnyk

Posted on

Docusaurus authentication with Entra ID and MSAL

Introduction

Docusaurus (https://docusaurus.io) is a well-regarded open-source tool for building documentation websites. It is a static-site generator that builds a single-page application leveraging the full power of React. However, it does not provide any kind of authentication out of the box. Adding authentication is crucial for securing access to your documentation.

Adding Entra ID Authentication

Let’s try to add Entra ID authentication to the static website generated by Docusaurus. In its heart, it is still a React application, so we could use relevant react packages to help with this task. However, there are some Docusaurus-specific techniques that are described below. With that in mind, let’s start!

Registering a Single Page Application

First, you will need to register a Single Page Application in Entra ID (previously known as Azure AD, Microsoft marketing department strikes again). The process is well-known and you can find step-by-step instructions there:

Single-page application: App registration

It is recommended to use MSAL.js 2.0 with auth code flow for enhanced security. Do not forget to add redirect URLs, and add http://localhost:3000/ for local debugging.

Installing MSAL Libraries

Next, install MSAL libraries for React

npm install react react-dom 
npm install @azure/msal-react @azure/msal-browser 
Enter fullscreen mode Exit fullscreen mode

Configuring Entra ID Registration Details

In the root of the application add authConfig.js file where you configure your Entra ID registration details.

AuthConfig.js Content

import { LogLevel } from "@azure/msal-browser";

/**
 * Configuration object to be passed to MSAL instance on creation. 
 * For a full list of MSAL.js configuration parameters, visit:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md 
 */

export const msalConfig = {
    auth: {
        clientId: "YOUR CLIENT ID",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
        redirectUri: "http://localhost:3000",
        postLogoutRedirectUri: '/',
    },
    cache: {
        cacheLocation: "sessionStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {   
        loggerOptions: {    
            loggerCallback: (level, message, containsPii) => {  
                if (containsPii) {      
                    return;     
                }       
                switch (level) {
                    case LogLevel.Error:
                        console.error(message);
                        return;
                    case LogLevel.Info:
                        console.info(message);
                        return;
                    case LogLevel.Verbose:
                        console.debug(message);
                        return;
                    case LogLevel.Warning:
                        console.warn(message);
                        return;
                    default:
                        return;
                }   
            }   
        }   
    }
};

/**
 * Scopes you add here will be prompted for user consent during sign-in.
 * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
 */
export const loginRequest = {
    scopes: []
};
Enter fullscreen mode Exit fullscreen mode

Adding Login Functionality

Finally, we need to add the actual login functionality. That is where things become more interesting. On a first look, there is no place where we can insert a custom login component and enforce authentication. However, the Docusaurus uses a technique called swizzling.

For Docusaurus, component swizzling means providing an alternative component that takes precedence over the component provided by the theme. You can think of it as Monkey Patching for React components, enabling you to override the default implementation. We need to find and override the very top component in the hierarchical tree, and Docusaurus provides just that:

Swizzling Wrapping your site with <Root> component

The <Root> component is rendered at the very top of the React tree, above the theme <Layout>, and never unmounts. It is the perfect place to add stateful logic that should not be re-initialized across navigations, such as user authentication status. Swizzle it manually by creating a file at src/theme/Root.js. The code is provided below.

import React, { useState } from 'react';

import { PublicClientApplication, EventType } from '@azure/msal-browser';
import { MsalProvider, AuthenticatedTemplate, useMsal, UnauthenticatedTemplate } from "@azure/msal-react";
import { msalConfig } from '@site/authConfig';

/**
 * MSAL should be instantiated outside of the component tree to prevent it from being re-instantiated on re-renders.
 */
const msalInstance = new PublicClientApplication(msalConfig);

// Default to using the first account if no account is active on page load
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
    // Account selection logic is app dependent. Adjust as needed for different use cases.
    msalInstance.setActiveAccount(msalInstance.getActiveAccount()[0]);
}

// Listen for sign-in event and set active account
msalInstance.addEventCallback((event) => {
    if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
        const account = event.payload.account;
        msalInstance.setActiveAccount(account);
    }
});

// Default implementation, that you can customize
export default function Root({children}) {
    const activeAccount = msalInstance.getActiveAccount();
    const claims = activeAccount ? activeAccount.idTokenClaims : null;

    const handleRedirect = () => {
        //instance.loginRedirect()
        msalInstance.loginPopup({
                ...msalConfig,
                prompt: 'create',
            })
            .catch((error) => console.log(error));
    };

    return (
        <MsalProvider instance={msalInstance}>            
            <AuthenticatedTemplate>
                <>{children}</>
            </AuthenticatedTemplate>
            <UnauthenticatedTemplate>
                <div style={{margin:'auto'}}>
                    <button onClick={handleRedirect}>
                        Sign in
                    </button>
                </div>
            </UnauthenticatedTemplate>
        </MsalProvider>
    );
}
Enter fullscreen mode Exit fullscreen mode

Code Implementation

Let’s step into the code.

MSAL React instance should be instantiated outside of the component tree to prevent it from being re-instantiated on re-renders.

You can listen to authentication events and set an active account or do other actions depending on event type.

From the active account you can retrieve token claims that you can use further for role-based access control if desired.

MSAL supports two types of authentication interfaces: redirect (loginRedirect) and popup (loginPopup). In the end, the result is the same.

Use MsalProvider component with MSAL instance and AuthenticatedTemplate to display/hide content depending on user authentication status. “<>{children}</>” will render the Docusaurus-generated site.

Conclusion

And just like that, we added an Entra ID authentication to our documentation site. This setup enhances security and provides a seamless user experience.
Everyone loves Docusaurus!
Everyone loves Docusaurus

Top comments (0)