Protecting routes is a critical aspect of web application security, ensuring that only authorized users can access certain pages or resources. In this section, we will explore various methods to protect routes in a Next.js application using the modern App Router, React Server Components, and async components.
Before diving into the implementation details, ensure you have the following:
npx create-next-app@latest my-nextjs-app --typescript
cd my-nextjs-app
One of the most common ways to protect routes is by using authentication libraries. We will explore two popular libraries: next-auth and firebase.
next-auth is a lightweight, open-source authentication library for Next.js applications.
First, install next-auth:
npm install next-auth
Create an [...nextauth].js file in the app/api/auth directory:
// app/api/auth/[...nextauth]/route.js
import { NextAuth } from 'next-auth';
import Providers from 'next-auth/providers';
export const authOptions = {
providers: [
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
To protect a route, use the useSession hook provided by next-auth:
// app/protected/page.js
import { useSession } from 'next-auth/react';
import Link from 'next/link';
export default function Protected() {
const { data: session, status } = useSession();
if (status === 'loading') return <p>Loading...</p>;
if (!session) return <Link href="/api/auth/signin">Sign in</Link>;
return (
<div>
<h1>Protected Page</h1>
<p>Welcome, {session.user.email}!</p>
</div>
);
}
Firebase is another powerful tool for authentication and can be integrated with Next.js.
Install Firebase:
npm install firebase
Set up Firebase in your project:
// app/firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID"
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
Create a custom hook to check authentication status:
// app/hooks/useAuth.js
import { useState, useEffect } from 'react';
import { auth } from '../firebase';
export function useAuth() {
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((authUser) => {
if (authUser) {
setUser(authUser);
} else {
setUser(null);
}
});
return () => unsubscribe();
}, []);
return user;
}
Use this hook to protect routes:
// app/protected/page.js
import { useAuth } from '../hooks/useAuth';
import Link from 'next/link';
export default function Protected() {
const user = useAuth();
if (!user) return <Link href="/login">Sign in</Link>;
return (
<div>
<h1>Protected Page</h1>
<p>Welcome, {user.email}!</p>
</div>
);
}
Next.js also allows you to create custom middleware for route protection.
Create a middleware.js file in the root of your project:
// middleware.js
import { NextResponse } from 'next/server';
export function middleware(req) {
const token = req.cookies.get('auth_token');
if (!token) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'],
};
Ensure your next.config.js includes the middleware configuration:
// next.config.js
module.exports = {
reactStrictMode: true,
};
Protecting routes on the server side can add an extra layer of security.
Modify your page to fetch user data and check authentication status:
// app/protected/page.js
export async function getServerSideProps(context) {
const token = context.req.cookies.auth_token;
if (!token) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
// Fetch user data using the token
return { props: {} };
}
export default function Protected() {
return (
<div>
<h1>Protected Page</h1>
<p>Welcome, User!</p>
</div>
);
}
Protecting routes is essential for maintaining the security of your Next.js application. By using authentication libraries, custom middleware, and server-side rendering techniques, you can effectively control access to sensitive pages and resources. Always follow best practices to ensure a secure application environment.