azure-active-directorysingle-sign-onnext-authapp-routernextjs14

How to set Azure MSAL SSO for my Nextjs14 app using Approuter and next-auth


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


Solution

  • 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 :

    enter image description here

    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
    

    enter image description here

    Output :

    I successfully logged in and out using the Login and Logout buttons.

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Reference : SO answer by @Seeker.