I am building a nextjs 14 app which uses app router with src/app/.. structure Is using layout.tsx as entry point for my application.
I do not have pages folder or _app.tsx as mentioned in many articles.
For this i want to Single Sign On using Azure MSAL configuration with next-auth
I am confused with folder structure and the flow as all the articles I find is all over place, with no clear examples. Whats the best or easy way to setup this and correct folder structure?
I tried to follow the documentation on nextjs, next-auth and azure, but nothing is clear and end-to-end. I am more confused on project structure as most articles suggest with pages folder or _app.tsx or the react-msal configuartion way
I tried the below Next.js application using App Router and next-auth to set Azure MSAL SSO authentication.
Refer my GitHub repository for complete code.
Complete Project structure :
Code :
src/app/api/auth/[...nextauth]/route.ts :
The below code configured next-auth to use Azure AD for user authentication, manages token and session details, and ensures the environment is correctly configured with necessary Azure AD credentials.
import NextAuth from "next-auth";
import AzureADProvider from "next-auth/providers/azure-ad";
const { AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRET, AZURE_AD_TENANT_ID } =
process.env;
if (!AZURE_AD_CLIENT_ID || !AZURE_AD_CLIENT_SECRET || !AZURE_AD_TENANT_ID) {
throw new Error("The Azure AD environment variables are not set.");
}
const handler = NextAuth({
secret: AZURE_AD_CLIENT_SECRET,
providers: [
AzureADProvider({
clientId: AZURE_AD_CLIENT_ID,
clientSecret: AZURE_AD_CLIENT_SECRET,
tenantId: AZURE_AD_TENANT_ID,
}),
],
callbacks: {
async jwt({ token, account }) {
if (account) {
token = Object.assign({}, token, {
access_token: account.access_token,
});
}
return token;
},
async session({ session, token }) {
if (session) {
session = Object.assign({}, session, {
access_token: token.access_token,
});
console.log(session);
}
return session;
},
},
});
export { handler as GET, handler as POST };
types/next-auth.d.ts :
import NextAuth from "next-auth";
declare module "next-auth" {
interface Session {
access_token?: string;
}
}
src/app/page.tsx :
import React from 'react';
const HomePage = () => {
return (
<div>
<h1>Welcome to the Home Page</h1>
<a href="/api/auth/signin">Login</a>
</div>
);
};
export default HomePage;
src/app/components/Header.tsx :
The Header
component provides a user interface for managing authentication state, including displaying a welcome message when the user is logged in and offering a logout button, while handling the loading state appropriately.
"use client";
import React from "react";
import { signOut, useSession } from "next-auth/react";
const Header = () => {
const { data: session, status } = useSession();
const handleLogOutClick = async () => {
try {
await signOut();
} catch (error) {
console.error(error);
}
};
if (status === 'loading') {
return <div>Loading...</div>;
}
return (
<header>
{session ? (
<>
<p>Welcome, {session.user?.email || "User"}</p>
<button onClick={handleLogOutClick}>Logout</button>
</>
) : (
<p>You are not logged in</p>
)}
</header>
);
};
export default Header;
.env.local :
AZURE_AD_CLIENT_ID=<clienyID>
AZURE_AD_CLIENT_SECRET=<clientSecret>
AZURE_AD_TENANT_ID=<tenantID>
NEXTAUTH_URL=http://localhost:3000
I added the below URL in the App registration > Authentication > Web redirect URI as shown below,
http://localhost:3000/api/auth/callback/azure-ad
Output :
I successfully logged in and out using the Login and Logout buttons.
Reference : SO answer by @Seeker.