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)