DEV Community

Hari Manoj
Hari Manoj

Posted on

Add Microsoft Authentication to Next.js in 5 Minutes

Add Microsoft Authentication to Next.js in 5 Minutes

No fluff. No theory. Just working code.

By the end of this tutorial, you'll have a Next.js app with Microsoft authentication that actually works.

What You'll Build

A Next.js app where users can:

  • ✅ Sign in with their Microsoft account
  • ✅ See their profile information
  • ✅ Sign out

Prerequisites

  • Node.js 18+ installed
  • A Microsoft account (personal or work)
  • 5 minutes

Step 1: Create Next.js App (30 seconds)

npx create-next-app@latest my-auth-app
cd my-auth-app
Enter fullscreen mode Exit fullscreen mode

Choose these options:

  • TypeScript: Yes
  • ESLint: Yes
  • Tailwind CSS: Yes
  • App Router: Yes
  • Everything else: Default

Step 2: Install Auth Package (15 seconds)

npm install @chemmangat/msal-next @azure/msal-browser @azure/msal-react
Enter fullscreen mode Exit fullscreen mode

Step 3: Get Azure Credentials (2 minutes)

  1. Go to Azure Portal
  2. Search for "Azure Active Directory"
  3. Click "App registrations" → "New registration"
  4. Fill in:
    • Name: my-auth-app
    • Supported account types: "Accounts in any organizational directory and personal Microsoft accounts"
    • Redirect URI: http://localhost:3000
  5. Click "Register"
  6. Copy the "Application (client) ID"

Step 4: Add Environment Variable (10 seconds)

Create .env.local:

NEXT_PUBLIC_CLIENT_ID=paste-your-client-id-here
Enter fullscreen mode Exit fullscreen mode

Step 5: Add Auth Provider (30 seconds)

Edit app/layout.tsx:

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { MsalAuthProvider } from '@chemmangat/msal-next';

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "My Auth App",
  description: "Microsoft authentication demo",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <MsalAuthProvider clientId={process.env.NEXT_PUBLIC_CLIENT_ID!}>
          {children}
        </MsalAuthProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Create Login Page (2 minutes)

Replace app/page.tsx:

'use client';

import { MicrosoftSignInButton, useMsalAuth, SignOutButton } from '@chemmangat/msal-next';

export default function Home() {
  const { isAuthenticated, account } = useMsalAuth();

  if (!isAuthenticated) {
    return (
      <main className="flex min-h-screen flex-col items-center justify-center p-24">
        <div className="text-center">
          <h1 className="text-4xl font-bold mb-8">Welcome!</h1>
          <p className="text-xl mb-8">Sign in to continue</p>
          <MicrosoftSignInButton variant="dark" size="large" />
        </div>
      </main>
    );
  }

  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24">
      <div className="text-center">
        <h1 className="text-4xl font-bold mb-4">Hello, {account?.name}!</h1>
        <p className="text-xl mb-2">Email: {account?.username}</p>
        <p className="text-gray-600 mb-8">ID: {account?.homeAccountId}</p>
        <SignOutButton variant="light" size="medium" />
      </div>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Run It! (5 seconds)

npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:3000

Click "Sign in with Microsoft" and you're done! 🎉

What Just Happened?

  1. MsalAuthProvider initialized MSAL with your client ID
  2. MicrosoftSignInButton handled the entire login flow
  3. useMsalAuth gave you access to user data
  4. SignOutButton handled logout

That's it. Production-ready Microsoft authentication in 5 minutes.

Bonus: Add User Profile (2 more minutes)

Want to show more user info? Add this:

'use client';

import {
  MicrosoftSignInButton,
  useMsalAuth,
  SignOutButton,
  useUserProfile,
  UserAvatar,
} from '@chemmangat/msal-next';

export default function Home() {
  const { isAuthenticated } = useMsalAuth();
  const { profile, loading } = useUserProfile();

  if (!isAuthenticated) {
    return (
      <main className="flex min-h-screen flex-col items-center justify-center p-24">
        <div className="text-center">
          <h1 className="text-4xl font-bold mb-8">Welcome!</h1>
          <MicrosoftSignInButton variant="dark" size="large" />
        </div>
      </main>
    );
  }

  if (loading) {
    return (
      <main className="flex min-h-screen flex-col items-center justify-center">
        <div>Loading profile...</div>
      </main>
    );
  }

  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24">
      <div className="text-center">
        <UserAvatar size={100} className="mb-4" />
        <h1 className="text-4xl font-bold mb-4">{profile?.displayName}</h1>
        <div className="text-left mb-8 space-y-2">
          <p>📧 Email: {profile?.mail}</p>
          <p>💼 Job Title: {profile?.jobTitle || 'N/A'}</p>
          <p>🏢 Office: {profile?.officeLocation || 'N/A'}</p>
          <p>📱 Phone: {profile?.mobilePhone || 'N/A'}</p>
        </div>
        <SignOutButton variant="light" size="medium" />
      </div>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now you get:

  • ✅ User's profile photo
  • ✅ Job title
  • ✅ Office location
  • ✅ Phone number
  • ✅ All from Microsoft Graph API

Bonus: Protect a Page (1 minute)

Create app/dashboard/page.tsx:

'use client';

import { AuthGuard } from '@chemmangat/msal-next';

export default function DashboardPage() {
  return (
    <AuthGuard>
      <main className="p-24">
        <h1 className="text-4xl font-bold">Protected Dashboard</h1>
        <p className="mt-4">Only authenticated users can see this!</p>
      </main>
    </AuthGuard>
  );
}
Enter fullscreen mode Exit fullscreen mode

Try visiting /dashboard without logging in - you'll be redirected to login automatically.

Bonus: Add Role-Based Access (2 minutes)

First, add roles in Azure AD (see enterprise guide for details).

Then use them:

'use client';

import { useRoles, AuthGuard } from '@chemmangat/msal-next';

export default function AdminPage() {
  const { hasRole } = useRoles();

  return (
    <AuthGuard>
      <main className="p-24">
        {hasRole('Admin') ? (
          <div>
            <h1 className="text-4xl font-bold">Admin Panel</h1>
            <p>Welcome, administrator!</p>
          </div>
        ) : (
          <div>
            <h1 className="text-4xl font-bold">Access Denied</h1>
            <p>You need admin role to access this page.</p>
          </div>
        )}
      </main>
    </AuthGuard>
  );
}
Enter fullscreen mode Exit fullscreen mode

Common Issues

"Invalid client" error

  • Double-check your client ID in .env.local
  • Make sure you added http://localhost:3000 as a redirect URI in Azure

"AADSTS50011" error

  • Add http://localhost:3000 to redirect URIs in Azure Portal
  • Make sure there's no trailing slash

Hydration errors

  • Make sure you're using 'use client' directive
  • The provider is in the root layout

"No active account" error

  • User needs to log in first
  • Check isAuthenticated before calling acquireToken

What's Next?

You now have working Microsoft authentication! Here's what to explore:

  1. MS Graph API - Access user's emails, calendar, files
  2. Middleware - Protect routes at the edge
  3. Server Components - Check auth server-side
  4. Role-Based Access - Implement RBAC with Azure AD roles

Full Code

Here's the complete app/page.tsx:

'use client';

import {
  MicrosoftSignInButton,
  useMsalAuth,
  SignOutButton,
  useUserProfile,
  UserAvatar,
} from '@chemmangat/msal-next';

export default function Home() {
  const { isAuthenticated } = useMsalAuth();
  const { profile, loading } = useUserProfile();

  // Not logged in
  if (!isAuthenticated) {
    return (
      <main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gradient-to-b from-blue-50 to-white">
        <div className="text-center max-w-md">
          <h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
            Welcome!
          </h1>
          <p className="text-xl text-gray-600 mb-8">
            Sign in with your Microsoft account to continue
          </p>
          <MicrosoftSignInButton variant="dark" size="large" />
          <p className="text-sm text-gray-500 mt-8">
            Secure authentication powered by Microsoft Azure AD
          </p>
        </div>
      </main>
    );
  }

  // Loading profile
  if (loading) {
    return (
      <main className="flex min-h-screen flex-col items-center justify-center">
        <div className="animate-pulse">Loading your profile...</div>
      </main>
    );
  }

  // Logged in
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gradient-to-b from-blue-50 to-white">
      <div className="bg-white rounded-2xl shadow-xl p-8 max-w-md w-full">
        <div className="text-center mb-6">
          <UserAvatar size={120} className="mx-auto mb-4" />
          <h1 className="text-3xl font-bold text-gray-800">
            {profile?.displayName}
          </h1>
        </div>

        <div className="space-y-3 mb-8">
          <div className="flex items-center gap-3 text-gray-700">
            <span className="text-2xl">📧</span>
            <div>
              <p className="text-sm text-gray-500">Email</p>
              <p className="font-medium">{profile?.mail}</p>
            </div>
          </div>

          {profile?.jobTitle && (
            <div className="flex items-center gap-3 text-gray-700">
              <span className="text-2xl">💼</span>
              <div>
                <p className="text-sm text-gray-500">Job Title</p>
                <p className="font-medium">{profile.jobTitle}</p>
              </div>
            </div>
          )}

          {profile?.officeLocation && (
            <div className="flex items-center gap-3 text-gray-700">
              <span className="text-2xl">🏢</span>
              <div>
                <p className="text-sm text-gray-500">Office</p>
                <p className="font-medium">{profile.officeLocation}</p>
              </div>
            </div>
          )}

          {profile?.mobilePhone && (
            <div className="flex items-center gap-3 text-gray-700">
              <span className="text-2xl">📱</span>
              <div>
                <p className="text-sm text-gray-500">Phone</p>
                <p className="font-medium">{profile.mobilePhone}</p>
              </div>
            </div>
          )}
        </div>

        <div className="text-center">
          <SignOutButton variant="light" size="medium" />
        </div>
      </div>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Resources


That's it! You just added Microsoft authentication to Next.js in 5 minutes.

Questions? Drop them in the comments! 👇

Found this helpful? Give it a ⭐ on GitHub!

Top comments (0)