Auth.js Credential 로그인 구현하기

no-pla·2024년 2월 25일
0

TIL

목록 보기
5/14
post-thumbnail

로그인 로직을 구현해 보자.

  1. 먼저 api/[...nextauth]/route.js파일을 생성해 준다.
import NextAuth from "next-auth/next";
import { PrismaClient } from "@prisma/client";
import { PrismaAdapter } from "@auth/prisma-adapter";
import CredentialsProvider from "next-auth/providers/credentials";

const prisma = new PrismaClient();

export const authOptions = {
	adapter: PrismaAdapter(prisma),
	providers: [],
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

Next.js api route에서는 메서드 명으로 접근하기 때문에 위와 같이 export를 해준다.

이제 이메일과 비밀번호를 이용해, 로그인 로직을 구현하기 위해 providers 안에 CredentialsProvider를 추가한다.

    CredentialsProvider({
      credentials: {
        email: { label: "email", type: "text", placeholder: "email" },
        password: { label: "password ", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error("이메일 혹은 비밀번호가 비어있습니다.");
        }

        const user = await prisma.user.findUnique({
          where: {
            email: credentials.email,
          },
        });

        if (!user) {
          throw new Error("이메일 혹은 비밀번호를 다시 확인해 주세요.");
        }

        const passwordsMatch = await bcrypt.compare(
          credentials.password,
          user.password!
        );

        if (!passwordsMatch) {
          throw new Error("이메일 혹은 비밀번호를 다시 확인해 주세요.");
        }

        return user;
      },
    }),

사용하려는 필드를 CredentialsProvider 내부의 credentials 안에 생성해 준다. 내 경우에는 이메일과 비밀번호 필드만 생성했다.

      credentials: {
        email: { label: "email", type: "text", placeholder: "email" },
        password: { label: "password ", type: "password" },
      },

다음은 실제 로그인 로직이다.

먼저 클라이언트 단에서 확인을 했지만, 추가로 서버 측에서도 이메일과 비밀번호가 모두 존재하는지 확인 후, 이메일을 기반으로 존재하는 유저인지 확인 후, 비밀번호 일치를 확인하는 로직 순서다.

이전에 회원가입 로직에서 암호화하여 저장했기 때문에, 디코드하여 비교 후, 일치 시 user를 반환해 준다.

  1. session
    토큰에 접근하기 위해 session을 설정한다. jwt 방식을 사용했다.
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user, session, account }) {
      // 로그인 하면서 토큰의 user에 id 추가
      if (user) {
        return {
          ...token,
          id: user.id,
        };
      }
      return token;
    },
    async session({ session, token, user }) {
      // session에 토큰에 저장한 id를 포함해서 반환하여 session에서 접근 가능하도록
      return {
        ...session,
        user: {
          ...session.user,
          id: token.id,
        },
      };
    },
  },

로그인 로직은 authorize => jwt callback => session callback 순서로 실행된다.

기본 session의 user는 이메일, 사진, 이메일의 정보만 저장하고 있다.

여기에 다른 유저의 정보를 가져오고 싶다면, authorize에서 반환한 userjwt callback 단계에서 user의 id를 추가하여 session callback으로 전달하여 클라이언트 단에서 user의 id에 접근할 수 있도록 할 수 있다.

로그아웃은 제공하는 훅을 사용하면 쉽게 구현할 수 있다.

+) onUnauthenticated

로그인이 되지 않은 상태로 접근하지 않아야 하는 경우, useSession의 onUnauthenticated를 사용하면 초기 로드 상태에서 session이 없는 경우, 즉 로그인이 되지 않은 경우를 처리할 수 있다.

  const { data: session } = useSession({
    required: true,
    onUnauthenticated() {
      router.push("/login");
    },
  });

0개의 댓글