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:
Assign Roles at Sign-Up
Attach arole
property to the user record (e.g.,user.role = 'admin'
).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: {} };
}
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