Creating an authentication and authorization feature in Next.js 14 involves several steps. Here’s a comprehensive guide to help you implement it:
Step 1: Set Up a New Next.js Project
- Initialize a New Project:
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
- Install Required Packages:
npm install next-auth mongoose bcryptjs
Step 2: Configure MongoDB
-
Create a
.env.local
File:
MONGODB_URI=mongodb://localhost:27017/auth-db
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_secret_key
Step 3: Set Up Mongoose Models
- Create a Directory Structure:
mkdir -p src/models src/lib
-
Create User Model:
Create
src/models/User.js
:
import mongoose from 'mongoose';
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
export default mongoose.models.User || mongoose.model('User', UserSchema);
-
Create MongoDB Connection Utility:
Create
src/lib/mongodb.js
:
import mongoose from 'mongoose';
const connectDB = async () => {
if (mongoose.connections[0].readyState) {
return;
}
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
};
export default connectDB;
Step 4: Configure NextAuth.js
-
Create the Auth API Route:
Create
src/pages/api/auth/[...nextauth].js
:
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import bcrypt from 'bcryptjs';
import connectDB from '../../../lib/mongodb';
import User from '../../../models/User';
export default NextAuth({
providers: [
Providers.Credentials({
async authorize(credentials) {
await connectDB();
const user = await User.findOne({ email: credentials.email });
if (user && (await bcrypt.compare(credentials.password, user.password))) {
return { name: user.name, email: user.email };
}
throw new Error('Invalid email or password');
},
}),
],
database: process.env.MONGODB_URI,
secret: process.env.NEXTAUTH_SECRET,
session: {
jwt: true,
},
callbacks: {
async jwt(token, user) {
if (user) {
token.id = user.id;
}
return token;
},
async session(session, token) {
session.user.id = token.id;
return session;
},
},
});
Step 5: Set Up Registration API Route
-
Create Registration API Route:
Create
src/pages/api/auth/register.js
:
import connectDB from '../../../lib/mongodb';
import User from '../../../models/User';
export default async function handler(req, res) {
if (req.method === 'POST') {
await connectDB();
const { name, email, password } = req.body;
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ message: 'User already exists' });
}
const user = await User.create({ name, email, password });
if (user) {
res.status(201).json({
_id: user._id,
name: user.name,
email: user.email,
});
} else {
res.status(400).json({ message: 'Invalid user data' });
}
} else {
res.status(405).json({ message: 'Method not allowed' });
}
}
Step 6: Set Up Client-Side Authentication
-
Create a Registration Page:
Create
src/pages/register.js
:
import { useState } from 'react';
import { useRouter } from 'next/router';
const Register = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, email, password }),
});
if (res.ok) {
router.push('/login');
} else {
const data = await res.json();
setError(data.message);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
</div>
<div>
<label>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div>
<label>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
{error && <p>{error}</p>}
<button type="submit">Register</button>
</form>
);
};
export default Register;
-
Create a Login Page:
Create
src/pages/login.js
:
import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/router';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
const result = await signIn('credentials', {
redirect: false,
email,
password,
});
if (result.ok) {
router.push('/');
} else {
setError(result.error);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div>
<label>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
{error && <p>{error}</p>}
<button type="submit">Login</button>
</form>
);
};
export default Login;
- Protecting Pages (Client-Side): Create a higher-order component (HOC) to protect pages:
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
const withAuth = (WrappedComponent) => {
return (props) => {
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
if (status === 'unauthenticated') {
router.replace('/login');
}
}, [status, router]);
if (status === 'authenticated') {
return <WrappedComponent {...props} />;
}
return null;
};
};
export default withAuth;
Usage:
import withAuth from '../path/to/withAuth';
const ProtectedPage = () => {
return (
<div>
<h1>Protected Content</h1>
</div>
);
};
export default withAuth(ProtectedPage);
Step 7: Start the Application
- Run the Next.js Application:
npm run dev
Step 8: Test the Application
-
Register a User:
- Navigate to
/register
and create a new user.
- Navigate to
-
Login a User:
- Navigate to
/login
and log in with the newly created user.
- Navigate to
-
Access Protected Page:
- Navigate to a protected page, e.g.,
/profile
, to ensure it redirects to/login
when not authenticated and displays the content when authenticated.
- Navigate to a protected page, e.g.,
This guide provides a foundational approach to implementing authentication and authorization in a Next.js 14 application. You can further expand and customize it based on your application's requirements.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content is generated by AI.