J. Reku
J. Reku

Reputation: 529

Next.js/MSAL: Text does not match server-rendered html

I am trying to set up next.js with azure ad b2c using the MSAL (react) library. Msal provides a nice component <MsalAuthenticationTemplate /> to handle auth.

I made a minimum next.js page.

import { InteractionType } from "@azure/msal-browser";
import { MsalAuthenticationTemplate } from "@azure/msal-react";
import { NextPage } from "next";

const ErrorComponent = ({ error }: any) => {
  return <p>an Error occured: {error}</p>;
};

const LoadingComponent = () => {
  return <p>Authentication in progress...</p>;
};

const CreateAdvert: NextPage = () => {
  return (
    <>
      <MsalAuthenticationTemplate
        interactionType={InteractionType.Redirect}
        errorComponent={ErrorComponent}
        loadingComponent={LoadingComponent}
      >
        <p>content</p>
      </MsalAuthenticationTemplate>
    </>
  );
};

export default CreateAdvert;

If I reproduce it even simpler I still get the same error:

import { InteractionType } from "@azure/msal-browser";
import {
  useIsAuthenticated,
} from "@azure/msal-react";
import { NextPage } from "next";

const CreateAdvert: NextPage = () => {
  const isAuthenticated = useIsAuthenticated();

  if (!isAuthenticated) return <p>not logged in!</p>;

  return <p>logged in!</p>;
};

export default CreateAdvert;

In my browser console I can see the following errors.

Warning: Text content did not match. Server: "Authentication in progress..." Client: "content"

Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.

Uncaught Error: Text content does not match server-rendered HTML

Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering

I was wondering how to fix this problem?

I think this part holds the biggest clue

Warning: Text content did not match. Server: "Authentication in progress..." Client: "content"

Does this mean I can't use SSR or SSG ?

But there is an answer in the FAQ that says you can use MSAL with ssr.

https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/FAQ.md#does-azuremsal-react-support-server-side-rendering-ssr-or-static-site-generation

Yes! However, authentication cannot be done server side and you should avoid invoking msal APIs server side. @azure/msal-react abstracts some of this logic away from you but if you are building custom authentication logic please ensure all APIs are invoked when rendered in the browser. You can take a look at our Next.js sample and Gatsby sample for examples.

If I check the next.js sample in the github repository it pretty much looks the same as my application.

https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/samples/msal-react-samples/nextjs-sample

rest of my code.

// _app.tsx

const conf = new MSAL.PublicClientApplication({
  auth: {
    clientId: process.env.NEXT_PUBLIC_B2C_WEBAPP_APP_ID as string,
    redirectUri:
      process.env.NODE_ENV === "development"
        ? process.env.NEXT_PUBLIC_B2C_REDIRECT_URI
        : process.env.NEXT_PUBLIC_B2C_REDIRECT_URI,
    authority: `https://${process.env.NEXT_PUBLIC_B2C_TENANT_NAME}.b2clogin.com/${process.env.NEXT_PUBLIC_B2C_TENANT_NAME}.onmicrosoft.com/${process.env.NEXT_PUBLIC_B2C_SIGNIN_SIGNUP_POLICY}`,
    knownAuthorities: [
      `${process.env.NEXT_PUBLIC_B2C_TENANT_NAME}.b2clogin.com`,
    ],
    navigateToLoginRequestUrl: false,
  },
  cache: {
    cacheLocation: "sessionStorage",
    storeAuthStateInCookie: false,
  },

  system: {
    loggerOptions: {
      logLevel: 0,
      loggerCallback: (level, message, containsPii) => {
        if (containsPii) {
          return;
        }
        switch (level) {
          case MSAL.LogLevel.Error:
            console.error(message);
            return;
          case MSAL.LogLevel.Info:
            console.info(message);
            return;
          case MSAL.LogLevel.Verbose:
            console.debug(message);
            return;
          case MSAL.LogLevel.Warning:
            console.warn(message);
            return;
        }
      },
    },
  },
});

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <MsalProvider instance={MSALInstance}>
      <Component {...pageProps} />
    </MsalProvider>
  );
}

export default MyApp;

Upvotes: 2

Views: 630

Answers (1)

SuryasriKamini-MT
SuryasriKamini-MT

Reputation: 915

  • Wherever the React component is used, you may try to resolve this issue by dynamically importing it with 'next/dynamic' using { ssr: false }.
const Home = dynamic(
   () => import('../components/Home'),
   { ssr: false }
)
  • This stops the component from being added to the server and forces dynamic loading of it only on the client-side.

Upvotes: 0

Related Questions