0

I am currently working on a React Next.js web application and I am attempting to integrate Microsoft Authentication Library (MSAL) login with Azure. The application uses Next.js with the App Router, which is the latest routing mechanism. However, whenever I try to run the app, I encounter the following error: createContext only works in Client Components. Add the 'use client' directive at the top of the file to use it.

My project folder structure is as follows:

- app
  - _app.tsx
  - layout.tsx
  - page.tsx
- config
  - msal.ts

_app.tsx



import { msalConfig } from "@/config/msal";
import { PublicClientApplication } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
import { AppProps } from "next/app";
import Layout from "./layout";

const MyApp = ({ Component, pageProps }: AppProps) => {
    const msalInstance = new PublicClientApplication(msalConfig);
    return (
      <MsalProvider instance={msalInstance}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </MsalProvider>
    );
  };
  export default MyApp;

layout.tsx

import { Footer, Navbar } from "@/components";
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>  
        <Navbar/>
        {children}
        <Footer/>
        </body>
    </html>
  );
}

page.tsx

import { loginRequest } from "@/config/msal";
import { useMsal } from "@azure/msal-react";

const Home = () => {

  const { instance, accounts } = useMsal();
  const isAuthenticated = accounts.length > 0;
  
  const handleLoginClick = async () => {
    await instance.loginRedirect(loginRequest).catch((err) => console.error('login failed', err));
  };
  const handleLogoutClick = async () => {
    await instance.logout().catch((err) => console.error('logout failed', err));
  };
  return (
    <div>
      <h1> Login here </h1>
      {isAuthenticated ? (
        <button onClick={handleLogoutClick}>Logout</button>
      ) : (
        <button onClick={handleLoginClick}>Login</button>
      )}
    </div>
  );
};
export default Home;

msal.ts

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

export const msalConfig: Configuration = {
    auth: {
        clientId: 'clientId' || '',
        authority:
            'https://login.microsoftonline.com/<Id>' || '',
        redirectUri: '/' || '',
        postLogoutRedirectUri: '/' || '',
    },
    cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: false,
    },
    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;
                }
            },
            logLevel: LogLevel.Verbose,
            piiLoggingEnabled: false,
        },
    },
};
export const loginRequest = {
    scopes: ['User.Read'],
};
export const graphConfig = {
    graphMeEndpoint: 'https://graph.microsoft.com/v1.0/me',
};

error:

⨯ node_modules\@azure\msal-react\dist\MsalContext.js (20:20) @ eval
 ⨯ createContext only works in Client Components. Add the "use client" directive at the top of the file to use it.

I'm looking for guidance on how to properly structure my React Next.js app with MSAL authentication that uses App Router and where to place the MSAL configuration file (msal.ts). Additionally, I'm seeking advice on how to resolve the "createContext only works in Client Components" error. I have already tried adding the "use client" directive as suggested in the error message, but it doesn't seem to resolve the issue.Any insights or examples on integrating MSAL with the Next.js App Router would be greatly appreciated.

when i use use client in page.tsx I am getting following error:

page.tsx:12 
 login failed BrowserConfigurationAuthError: stubbed_public_client_application_called: Stub instance of Public Client Application was called. If using msal-react, please ensure context is not used without a provider. For more visit: aka.ms/msaljs/browser-errors
    at createBrowserConfigurationAuthError (BrowserConfigurationAuthError.mjs:46:1)
    at Object.loginRedirect (IPublicClientApplication.mjs:45:66)
    at handleLoginClick (page.tsx:12:20)
    at HTMLUnknownElement.callCallback (react-dom.development.js:20498:1)
    at Object.invokeGuardedCallbackImpl (react-dom.development.js:20547:1)
    at invokeGuardedCallback (react-dom.development.js:20622:1)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:20636:1)
    at executeDispatch (react-dom.development.js:31986:1)
    at processDispatchQueueItemsInOrder (react-dom.development.js:32018:1)
    at processDispatchQueue (react-dom.development.js:32031:1)
    at dispatchEventsForPlugins (react-dom.development.js:32042:1)
    at eval (react-dom.development.js:32232:1)
    at batchedUpdates$1 (react-dom.development.js:24843:1)
    at batchedUpdates (react-dom.development.js:28703:1)
    at dispatchEventForPluginEventSystem (react-dom.development.js:32231:1)
    at dispatchEvent (react-dom.development.js:29999:1)
    at dispatchDiscreteEvent (react-dom.development.js:29970:1)


1 Answer 1

0

I don't have enough rep. But I would try this.

//Imports
const msalInstance = new PublicClientApplication(msalConfig);
const MyApp = ({ Component, pageProps }: AppProps) => {
  const [mounted, setMounted] = useState<boolean>(false);
  async function initialize() {
    // This is for the microsoft authentication on web.
    await pca.initialize();
    setMounted(true);
  }

  useEffect(() => {
    initialize();
  }, []);
  if (!mounted) {
    return null; //This could also be a loading screen
  }
  return (
    <MsalProvider instance={msalInstance}>
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </MsalProvider>
  );
};
export default MyApp;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.