Go Back

Apr 17, 2025

Authentication and Authorization

we’ll cover common auth strategies, integrating with providers, protecting routes, and role-based access control.

Authentication and Authorization

1. Authentication Strategies

a. Third-Party Providers
Integrate OAuth providers (e.g., Google, GitHub) for user sign-in:

npm install next-auth
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

export default NextAuth({
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    Providers.GitHub({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
  ],
  session: { jwt: true },
});

b. Email & Password
Implement credentials-based auth or use services like Clerk:

npm install @clerk/nextjs
// pages/_app.js
import { ClerkProvider } from '@clerk/nextjs';

function MyApp({ Component, pageProps }) {
  return (
    <ClerkProvider {...pageProps}>
      <Component {...pageProps} />
    </ClerkProvider>
  );
}
export default MyApp;

c. JWT Tokens
Use JSON Web Tokens for stateless auth in APIs:

// lib/jwt.js
import jwt from 'jsonwebtoken';
export function signToken(payload) {
  return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
}
export function verifyToken(token) {
  return jwt.verify(token, process.env.JWT_SECRET);
}

2. Protecting Pages and API Routes

a. Client-Side Guards
Redirect unauthenticated users on the client:

import { useSession } from 'next-auth/client';
import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function Dashboard() {
  const [session, loading] = useSession();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !session) router.replace('/api/auth/signin');
  }, [session, loading]);

  if (loading) return <p>Loading...</p>;
  return <h1>Your Dashboard</h1>;
}

b. Server-Side Guards
Use getServerSideProps to enforce auth on SSR pages:

// pages/profile.js
import { getSession } from 'next-auth/client';

export async function getServerSideProps(context) {
  const session = await getSession(context);
  if (!session) {
    return { redirect: { destination: '/api/auth/signin', permanent: false } };
  }
  return { props: { user: session.user } };
}

export default function Profile({ user }) {
  return <div>Welcome, {user.name}</div>;
}

c. API Route Protection
Check auth in API handlers:

// pages/api/secure-data.js
import { getToken } from 'next-auth/jwt';

export default async (req, res) => {
  const token = await getToken({ req, secret: process.env.JWT_SECRET });
  if (!token) return res.status(401).json({ error: 'Unauthorized' });
  res.status(200).json({ data: 'Secure Data' });
};

3. Middleware for Route Protection

Next.js middleware can intercept requests at the edge:

// middleware.js
import { NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';

export async function middleware(req) {
  const token = await getToken({ req, secret: process.env.JWT_SECRET });
  const { pathname } = req.nextUrl;

  // Protect /admin paths
  if (pathname.startsWith('/admin') && !token) {
    const signInUrl = new URL('/api/auth/signin', req.url);
    return NextResponse.redirect(signInUrl);
  }
  return NextResponse.next();
}

In next.config.js, define matcher:

module.exports = {
  experimental: { middleware: true },
  matcher: ['/admin/:path*'],
};

4. Role-Based Access Control (RBAC)

Implement user roles to restrict resources:

  1. Assign Roles at Sign-Up
    Attach a role property to the user record (e.g., user.role = 'admin').

  2. Check Roles in Pages/API

// pages/admin/dashboard.js
import { getSession } from 'next-auth/client';

export async function getServerSideProps(context) {
  const session = await getSession(context);
  if (!session || session.user.role !== 'admin') {
    return { notFound: true };
  }
  return { props: {} };
}
  1. Hide UI Elements
    On the client, conditionally render admin links:

{session?.user?.role === 'admin' && <Link href="/admin">Admin</Link>}

With these patterns, you can secure your Next.js app, integrate popular providers, and implement fine-grained access control. In Next Article, we’ll dive into testing and performance optimization