Authentication is a critical part of any web application, ensuring that only authorized users can access certain areas and functionalities. In this tutorial, we'll explore how to implement secure authentication in a Next.js application using NextAuth.js, a flexible and easy-to-use authentication library for Next.js.
Setting Up Your Next.js Project
Before we dive into authentication, let's setup a basic Next.js project. If you haven't already, install the Next.js CLI and create a new project:
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
npm install
Installing NextAuth.js
Next, install NextAuth.js and its required dependencies:
npm install next-auth
Configuring NextAuth.js
Create a new file called next-auth.js in the pages/api/auth directory. This file will configure NextAuth.js for your application:
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
error: '/auth/error',
verifyRequest: '/auth/verify-request',
newUser: '/auth/new-user',
},
callbacks: {
async signIn(user, account, profile) {
return true;
},
async redirect(url, baseUrl) {
return baseUrl;
},
async session(session, user) {
session.userId = user.id;
return session;
},
async jwt(token, user, account, profile, isNewUser) {
return token;
},
},
});
Environment Variables
NextAuth.js requires environment variables for the authentication providers. Create a .env.local file in the root of your project and add the following:
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
Make sure to replace your-google-client-id and your-google-client-secret with your actual Google OAuth credentials. You can obtain these from the Google Developer Console.
Creating Authentication Pages
NextAuth.js provides default authentication pages, but you can customize them by creating your own. Create the following files in the pages/auth directory:
signin.js
signout.js
error.js
verify-request.js
new-user.js
Here's an example of a custom sign-in page:
import { providers, signIn, getSession, csrfToken } from 'next-auth/client';
export default function SignIn({ providers, csrfToken }) {
return (
<div>
<h1>Sign In</h1>
{Object.values(providers).map((provider) => (
<div key={provider.name}>
<button onClick={() => signIn(provider.id)}>
Sign in with {provider.name}
</button>
</div>
))}
</div>
);
}
export async function getServerSideProps(context) {
const { req } = context;
const session = await getSession({ req });
const providers = await providers();
const csrfToken = await csrfToken(context);
if (session) {
return { redirect: { destination: '/' } };
}
return {
props: { providers, csrfToken },
};
}
Protecting Pages
To protect certain pages or routes, you can use the useSession hook provided by NextAuth.js. Here’s an example of how to protect a page:
import { useSession, signIn } from 'next-auth/client';
export default function ProtectedPage() {
const [session, loading] = useSession();
if (loading) return <div>Loading...</div>;
if (!session) {
return (
<div>
<h1>You are not signed in</h1>
<button onClick={() => signIn()}>Sign in</button>
</div>
);
}
return (
<div>
<h1>Welcome, {session.user.name}</h1>
</div>
);
}
Conclusion
In this tutorial, we've covered how to set up secure authentication in a Next.js application using NextAuth.js. We've seen how to configure authentication providers, create custom authentication pages, and protect routes. NextAuth.js makes it easy to implement robust authentication in your Next.js applications, allowing you to focus on building great features for your users.