DEV Community

Nadim Chowdhury
Nadim Chowdhury

Posted on • Edited on

Create an alternative of Next Auth

Below is a fully functional example of how you can create an authentication system in a Next.js application without using NextAuth. This example uses JWT (JSON Web Tokens) for authentication and cookie-based session management.

1. Install Dependencies

First, install the required dependencies:

npm install next react react-dom bcryptjs jsonwebtoken cookie
Enter fullscreen mode Exit fullscreen mode

2. Directory Structure

Your project structure should look like this:

/pages
  /api
    /auth
      login.js
      register.js
      logout.js
  /protected
    index.js
  _app.js
  index.js
/utils
  auth.js
  db.js
/middleware
  auth.js
Enter fullscreen mode Exit fullscreen mode

3. Create a Simple User Database

For simplicity, let's create a fake user database.

/utils/db.js

const users = [];

module.exports = {
  users,
};
Enter fullscreen mode Exit fullscreen mode

4. Create Authentication Utilities

/utils/auth.js

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const JWT_SECRET = 'your_jwt_secret';

const hashPassword = async (password) => {
  return await bcrypt.hash(password, 10);
};

const verifyPassword = async (password, hashedPassword) => {
  return await bcrypt.compare(password, hashedPassword);
};

const generateToken = (user) => {
  return jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: '1h' });
};

const verifyToken = (token) => {
  try {
    return jwt.verify(token, JWT_SECRET);
  } catch (error) {
    return null;
  }
};

module.exports = {
  hashPassword,
  verifyPassword,
  generateToken,
  verifyToken,
};
Enter fullscreen mode Exit fullscreen mode

5. Create API Routes for Authentication

/pages/api/auth/register.js

import { users } from '../../../utils/db';
import { hashPassword } from '../../../utils/auth';

export default async (req, res) => {
  if (req.method === 'POST') {
    const { email, password } = req.body;

    const userExists = users.find(user => user.email === email);
    if (userExists) {
      return res.status(400).json({ message: 'User already exists' });
    }

    const hashedPassword = await hashPassword(password);
    const newUser = { id: users.length + 1, email, password: hashedPassword };
    users.push(newUser);

    res.status(201).json({ message: 'User registered successfully' });
  } else {
    res.status(405).json({ message: 'Method not allowed' });
  }
};
Enter fullscreen mode Exit fullscreen mode

/pages/api/auth/login.js

import { users } from '../../../utils/db';
import { verifyPassword, generateToken } from '../../../utils/auth';
import cookie from 'cookie';

export default async (req, res) => {
  if (req.method === 'POST') {
    const { email, password } = req.body;

    const user = users.find(user => user.email === email);
    if (!user) {
      return res.status(400).json({ message: 'Invalid credentials' });
    }

    const isValid = await verifyPassword(password, user.password);
    if (!isValid) {
      return res.status(400).json({ message: 'Invalid credentials' });
    }

    const token = generateToken(user);

    res.setHeader('Set-Cookie', cookie.serialize('token', token, {
      httpOnly: true,
      secure: process.env.NODE_ENV !== 'development',
      maxAge: 3600,
      sameSite: 'strict',
      path: '/'
    }));

    res.status(200).json({ message: 'Logged in successfully' });
  } else {
    res.status(405).json({ message: 'Method not allowed' });
  }
};
Enter fullscreen mode Exit fullscreen mode

/pages/api/auth/logout.js

import cookie from 'cookie';

export default (req, res) => {
  if (req.method === 'POST') {
    res.setHeader('Set-Cookie', cookie.serialize('token', '', {
      httpOnly: true,
      secure: process.env.NODE_ENV !== 'development',
      maxAge: -1,
      sameSite: 'strict',
      path: '/'
    }));

    res.status(200).json({ message: 'Logged out successfully' });
  } else {
    res.status(405).json({ message: 'Method not allowed' });
  }
};
Enter fullscreen mode Exit fullscreen mode

6. Middleware for Protected Routes

/middleware/auth.js

import { verifyToken } from '../utils/auth';
import cookie from 'cookie';

const authMiddleware = (handler) => {
  return async (req, res) => {
    const cookies = cookie.parse(req.headers.cookie || '');
    const token = cookies.token;

    if (!token) {
      return res.status(401).json({ message: 'Authentication required' });
    }

    const user = verifyToken(token);

    if (!user) {
      return res.status(401).json({ message: 'Authentication required' });
    }

    req.user = user;
    return handler(req, res);
  };
};

export default authMiddleware;
Enter fullscreen mode Exit fullscreen mode

7. Create a Protected Route

/pages/protected/index.js

import React from 'react';
import authMiddleware from '../../middleware/auth';

const ProtectedPage = ({ user }) => {
  return (
    <div>
      <h1>Protected Page</h1>
      <p>Welcome, {user.email}</p>
    </div>
  );
};

export const getServerSideProps = authMiddleware(async (context) => {
  return {
    props: { user: context.req.user },
  };
});

export default ProtectedPage;
Enter fullscreen mode Exit fullscreen mode

8. Create the Main Application File

/pages/_app.js

import '../styles/globals.css';

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

export default MyApp;
Enter fullscreen mode Exit fullscreen mode

9. Create the Home Page with Login and Registration Forms

/pages/index.js

import { useState } from 'react';
import axios from 'axios';

const Home = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [message, setMessage] = useState('');

  const handleRegister = async () => {
    try {
      const response = await axios.post('/api/auth/register', { email, password });
      setMessage(response.data.message);
    } catch (error) {
      setMessage(error.response.data.message);
    }
  };

  const handleLogin = async () => {
    try {
      const response = await axios.post('/api/auth/login', { email, password });
      setMessage(response.data.message);
    } catch (error) {
      setMessage(error.response.data.message);
    }
  };

  return (
    <div>
      <h1>Authentication Example</h1>
      <input
        type="email"
        placeholder="Email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button onClick={handleRegister}>Register</button>
      <button onClick={handleLogin}>Login</button>
      {message && <p>{message}</p>}
    </div>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

10. Test Your Application

Now you can run your Next.js application and test the authentication system.

npm run dev
Enter fullscreen mode Exit fullscreen mode

Navigate to http://localhost:3000 and test the registration and login functionality.

This setup provides a basic authentication system using JWT and cookies in a Next.js application. You can extend this example to include more features such as password reset, email verification, and social login integration.

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.

Top comments (0)