Как я могу использовать провайдеры учетных данных и Google вместе с nextjs 14 и next-auth?

Вопрос или проблема

Я пытаюсь конвертировать приложение mern в nextjs, но у меня возникли проблемы с next-auth. Я создаю пользователя с электронной почтой и паролем в отдельной форме регистрации, и если я перехожу к форме входа и на страницу AuthOptions, если я закомментирую колбэки внизу, вход с электронной почтой и паролем работает.

Провайдер Google работает без проблем, сохраняя пользователя в mongoDB, если его там нет.

Если я не закомментирую колбэки и попытаюсь войти, используя электронную почту и пароль, то выполняется часть профиля учетных данных, но затем пытается выполнить колбэки. Есть ли лучший способ? Я мучился, пытаясь заставить это работать, используя один учебник для настройки GoogleProvider и отдельный для настройки CredentialsProvider, но не могу заставить их работать вместе.

Вот мой /api/auth/[…nextauth]/route.js

import { authOptions } from "@/utils/authOptions";
import NextAuth from "next-auth";

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

А вот мой файл authOptions:

import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import connectDB from "@/lib/database";
import User from "@/models/User";
import bcrypt from "bcryptjs";

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: {
          label: "email:",
          type: "email",
          name: "email",
          placeholder: "your-email",
        },
        password: {
          label: "password:",
          type: "password",
          name: "password",
          placeholder: "your-password",
        },
      },
      async authorize(credentials) {
        try {
          const foundUser = await User.findOne({
            email: credentials.email,
          }).lean();

          if (foundUser) {
            console.log("найден пользователь ", foundUser);
            console.log("Пользователь существует");
            const match = await bcrypt.compare(
              credentials.password,
              foundUser.password
            );

            if (match) {
              console.log("Хороший пароль");
              delete foundUser.password;

              foundUser["role"] = "Неконтролируемая электронная почта";
              return foundUser;
            }
          }
        } catch (error) {
          console.log(error);
        }
        return null;
      },
    }),

    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorization: {
        params: {
          prompt: "согласие",
          access_type: "offline",
          response_type: "code",
        },
      },
    }),
  ],

  callbacks: {
    async signIn({ profile }) {
      await connectDB;
      const userExists = await User.findOne({ email: profile.email });

      if (!userExists) {
        const username = profile.name.slice(" ");
        await User.create({
          firstName: username[0],
          lastName: username[1],
          email: profile.email,
          image: profile.picture,
        });
      }
      return true;
    },
    async session({ session }) {
      const user = await User.findOne({ email: session.user.email });
      session.user.id = user._id.toString();
      return session;
    },
  },
};

Ответ или решение

Интеграция Credential Provider и Google Provider в Next.js 14 с использованием NextAuth

При переходе вашего MERN приложения на Next.js и использовании NextAuth для аутентификации, важно понимать, как одновременно интегрировать Credential Provider и Google Provider. Ниже приведено подробное руководство по настройке этих провайдеров в Next.js 14 и NextAuth.

Шаг 1: Настройка окружения

Убедитесь, что в вашем проекте установлены все необходимые зависимости:

npm install next-auth bcryptjs mongoose

Также, убедитесь, что у вас правильно настроены переменные окружения в файле .env.local:

GOOGLE_CLIENT_ID=ваш_google_client_id
GOOGLE_CLIENT_SECRET=ваш_google_client_secret
MONGODB_URI=ваш_mongodb_uri

Шаг 2: Настройка базовых провайдеров

Убедитесь, что ваши провайдеры (GoogleProvider и CredentialsProvider) правильно настроены в файле authOptions. Основная часть вашей настройки выглядит корректно, но давайте добавим несколько улучшений.

import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import connectDB from "@/lib/database";
import User from "@/models/User";
import bcrypt from "bcryptjs";

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "email", placeholder: "your-email" },
        password: { label: "Password", type: "password", placeholder: "your-password" },
      },
      async authorize(credentials) {
        await connectDB(); // Убедитесь, что соединение с БД произошло
        const { email, password } = credentials;

        const foundUser = await User.findOne({ email }).lean();
        if (foundUser && await bcrypt.compare(password, foundUser.password)) {
          delete foundUser.password; // Удаляем пароль перед возвратом
          return { ...foundUser, role: "Unverified Email" }; // Добавляем роль пользователя
        }

        return null; // Возвращаем null, если пользователь не найден
      },
    }),

    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code",
        },
      },
    }),
  ],

  callbacks: {
    async signIn({ profile }) {
      await connectDB(); // Обязательно вызывайте connectDB в каждом колбэке
      const userExists = await User.findOne({ email: profile.email });
      if (!userExists) {
        const username = profile.name.split(" ");
        await User.create({
          firstName: username[0],
          lastName: username[1],
          email: profile.email,
          image: profile.picture,
        });
      }
      return true;
    },

    async session({ session }) {
      const user = await User.findOne({ email: session.user.email });
      session.user.id = user._id.toString();
      return session;
    },
  },
};

Шаг 3: Конфигурация NextAuth API

Убедитесь, что ваш файл API корректно возвращает аутентификацию:

import { authOptions } from "@/utils/authOptions";
import NextAuth from "next-auth";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

Шаг 4: Обработка вызовов колбэков

Основная проблема, с которой вы столкнулись, заключается в конфликте между обработкой аутентификации через Credential Provider и вызовами колбэков. Убедитесь, что ваши колбэки обрабатываются корректно и не вызывают ошибок. Возможно, мы добавим обработку ошибок в колбэки, чтобы они не прерывали выполнение другого кода.

Шаг 5: Управление пользовательским интерфейсом

Когда вы работаете с формами входа и регистрации, убедитесь, что у вас есть правильная логика для обработки успешного и неудачного входа. Например, при неудачной попытке входа можно вернуть сообщение об ошибке, чтобы информировать пользователя.

Заключение

Теперь вы должны иметь работающее решение, которое интегрирует как Credential Provider, так и Google Provider для аутентификации пользователей в Next.js 14 с использованием NextAuth. Убедитесь, что вы протестировали оба метода аутентификации, чтобы гарантировать, что все работает корректно. В случае возникновения проблем, проверьте логи на наличие ошибок и убедитесь, что соединение с базой данных установлено корректно.

Если вы столкнетесь с дальнейшими проблемами, не стесняйтесь обращаться за помощью к сообществу разработчиков Next.js или просмотреть официальную документацию NextAuth для более глубокого понимания работы этих провайдеров.

Оцените материал
Добавить комментарий

Капча загружается...