This guide walks you through integrating Azure Single Sign-On (SSO) into your React web application built with Next.js 14 and App Router.

Prerequisites:

  • React 18
  • Next.js 14
  • App Router (using app folder structure)

Understanding App Router and NextAuth Configuration:

App Router can be slightly trickier for handling redirections during SSO implementation. We’ll leverage next-auth to simplify the process.

Step 1: Installation

Begin by installing the next-auth package using npm:

npm install next-auth

Step 2: Configuring NextAuth for App Router

Since you’re using App Router, navigate to the app folder within your project’s src directory. Here’s how to set up the configuration:

  1. Create an api folder inside app.
  2. Within api, create an auth folder.
  3. Inside auth, create a folder named [...nextauth].
  4. Finally, create a file named router.ts inside [...nextauth].

The complete path should resemble

src/app/api/auth/[...nextauth]/router.ts

Step 3: Setting Up NextAuth Configuration (router.ts):

Here’s the router.ts code with explanations for each section:

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 used to sign and encrypt tokens (replace with a strong secret)
  secret: AZURE_AD_CLIENT_SECRET,
  providers: [
    AzureADProvider({
      clientId: AZURE_AD_CLIENT_ID,
      clientSecret: AZURE_AD_CLIENT_SECRET,
      tenantId: AZURE_AD_TENANT_ID,
    }),
  ],
  // Callbacks for modifying JWT and Session objects
  callbacks: {
    async jwt({ token, account }) {
      if (account) {
        // Add access token to the JWT token
        token = Object.assign({}, token, {
          access_token: account.access_token,
        });
      }
      return token;
    },
    async session({ session, token }) {
      if (session) {
        // Add access token to the session object
        session = Object.assign({}, session, {
          access_token: token.access_token,
        });
        console.log(session); // Optional: Log session data for debugging
      }
      return session;
    },
  },
});

export { handler as GET, handler as POST };

Note: You can decode access_token and id_token to fetch groups, token_expiry etc with help of jwt-decode subject to your requirements.
  • Environment Variables: Replace placeholders like AZURE_AD_CLIENT_ID with your actual Azure AD application credentials.
  • Secret: Set a strong secret value for signing and encrypting tokens.
  • Azure AD Provider: This configures Azure AD as the authentication provider for your application.
  • JWT and Session Callbacks: These functions allow you to customize the data stored in the JWT (JSON Web Token) and session objects. In this case, we’re adding the access token received from Azure AD to both.

Step 4: Wrapping Your App with SessionProvider (layout.tsx):

Next, we need to wrap your application with SessionProvider from next-auth/react in your layout.tsx file:

import React from "react";
import { SessionProvider } from "next-auth/react";
// ... other imports

export default function RootLayout({ children }) {
  // ... other component logic

  return (
    <html lang="en">
      <body>
        <SessionProvider>
          {/* Your application components go here */}
          {children}
        </SessionProvider>
      </body>
    </html>
  );
}

This ensures the session information is accessible throughout

Step 5: Setting up login and logout methods.

// login.tsx

import { signIn } from "next-auth/react";
const handleLoginClick = async () => {
  try {
    signIn();
  } catch (error) {
    console.error(error);
  }
};
<Button
  variant="contained"
  color="primary"
  fullWidth
  onClick={handleLoginClick}
>
  Login
</Button>;
// logout.tsx

import { signOut } from "next-auth/react";
const handleLogOutClick = async () => {
  try {
    signOut();
  } catch (error) {
    console.error(error);
  }
};
<Button
  variant="contained"
  color="primary"
  fullWidth
  onClick={handleLogOutClick}
>
  Login
</Button>;

This is the basic set up required to enable SSO with Azure AD . Additional configurations can be configured. guide here

Please comment your questions/clarifications.