next-auth 란? [nextjs]

김대운·2023년 4월 12일
0

Nextjs

목록 보기
4/4
post-thumbnail

Next-auth 란 ??

Next-auth는 다양한 소셜 로그인 및 일반 로그인 인증 기능을 간단하게 현할 수 있도록 도와주는 라이브러리입니다.

이 라이브러리를 사용하면 사용자 인증 및 권한 부여를 쉽게 구현할 수 있습니다. 이를 통해, 사용자가 로그인하고 로그인 정보를 보유한 세션을 유지하는 것이 가능합니다.

Next-auth는 다양한 인증 프로토콜을 지원합니다. 예를 들어, Google, Facebook, Twitter, Github, Apple 등의 소셜 로그인 제공 업체에 대한 OAuth2 및 OpenID Connect를 지원합니다. 또한, 기본적인 이메일/비밀번호 로그인 기능도 제공합니다.

Next-auth는 Next.js와 함께 사용하기 적합한 라이브러리입니다. 따라서, Next.js에서 쉽게 구현할 수 있습니다.

Next-auth의 장점은 ?

  • 간단한 설정 및 사용법: 다양한 인증 방식을 제공하면서도 간단한 설정과 사용법을 가지고 있어 누구나 쉽게 사용할 수 있습니다.

  • 다양한 인증 방식 제공: Next-auth는 OAuth, JWT, Magic.link, Auth0, Google, Facebook, Twitter, Github, Apple, Discord, Slack 등 다양한 인증 방식을 지원합니다.

  • 확장성: 다른 프레임워크와 마찬가지로 Next.js와 함께 사용하기에 적합합니다.

  • 보안: 다양한 인증 방식 제공으로 인한 보안 취약점을 최소화할 수 있습니다.

  • 커뮤니티 지원: Next-auth는 큰 커뮤니티를 가지고 있으며, Github를 통해 문제점 해결 및 버그 수정, 기능 개발이 지속적으로 이루어지고 있습니다.

사용법

1. 설치

npm i next-auth

or

yarn add next-auth

2. [...nextauth].ts 파일 생성

설치가 완료되면 pages/api/auth/[...nextauth].ts 파일을 생성하고 next-auth를 구성합니다.

import bcrypt from 'bcrypt'; // bcrypt 라이브러리 import
import NextAuth, { AuthOptions } from 'next-auth'; // NextAuth 라이브러리 import
import CredentialsProvider from 'next-auth/providers/credentials'; // CredentialsProvider import
import GithubProvider from 'next-auth/providers/github'; // GithubProvider import
import GoogleProvider from 'next-auth/providers/google'; // GoogleProvider import
import { PrismaAdapter } from '@next-auth/prisma-adapter'; // PrismaAdapter import

import prisma from '@/app/libs/prismadb'; // PrismaClient 인스턴스 import

export const authOptions: AuthOptions = {
  adapter: PrismaAdapter(prisma), // PrismaAdapter 설정
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID as string, // GitHub OAuth 클라이언트 아이디
      clientSecret: process.env.GITHUB_SECRET as string, // GitHub OAuth 클라이언트 시크릿
    }),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string, // Google OAuth 클라이언트 아이디
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, // Google OAuth 클라이언트 시크릿
    }),
    CredentialsProvider({
      name: 'credentials', // CredentialsProvider 설정
      credentials: {
        email: { label: 'email', type: 'text' }, // 이메일 입력 필드
        password: { label: 'password', type: 'password' }, // 비밀번호 입력 필드
      },
      async authorize(credentials) { // 로그인 인증 함수
        if (!credentials?.email || !credentials?.password) { // 이메일과 비밀번호 값이 없는 경우
          throw new Error('Invalid credentials'); // 예외 처리
        }

        const user = await prisma.user.findUnique({ // PrismaClient를 사용해 이메일로 사용자 조회
          where: {
            email: credentials.email,
          },
        });

        if (!user || !user?.hashedPassword) { // 사용자가 존재하지 않거나, 비밀번호가 해시화되지 않은 경우
          throw new Error('Invalid credentials'); // 예외 처리
        }

        const isCorrectPassword = await bcrypt.compare( // 입력한 비밀번호를 해시화한 후, 사용자 비밀번호와 비교
          credentials.password,
          user.hashedPassword
        );

        if (!isCorrectPassword) { // 비밀번호가 일치하지 않은 경우
          throw new Error('Invalid credentials'); // 예외 처리
        }

        return user; // 사용자 반환
      },
    }),
  ],
  pages: {
    signIn: '/', // 로그인 페이지 경로
  },
  debug: process.env.NODE_ENV === 'development', // 개발 모드일 때 디버그 모드 활성화
  session: {
    strategy: 'jwt', // 세션 저장 방식 설정
  },
  secret: process.env.NEXTAUTH_SECRET, // NextAuth 시크릿
};

export default NextAuth(authOptions); // NextAuth 설정을 사용해 인증 서버 생성

adapter : PrismaAdapter(prisma)는 next-auth에서 사용하는 인증 정보를 데이터베이스에 저장하기 위한 adapter입니다. PrismaAdapter를 사용하여 Prisma ORM을 사용하여 유저 정보를 저장하고 가져오도록 설정합니다.

저는 prisma를 사용하여 데이터베이스를 관리 했으므로 adapter에 추가 해줬습니다

Providers:
AuthOptions 객체에는 Providers 배열이 포함됩니다. Providers는 다양한 소셜 로그인 및 자체 로그인 시스템과 연동하기 위한 객체입니다. 이 코드에서는 GithubProvider, GoogleProvider, 그리고 CredentialsProvider를 설정합니다.

GithubProvider와 GoogleProvider는 소셜 로그인 기능을 제공하기 위해 사용됩니다. 각각 Github와 Google API를 사용하여 사용자 정보를 가져오는 방식으로 로그인을 처리합니다.

CredentialsProvider는 자체 로그인 시스템을 구현하기 위한 Provider입니다. 이 코드에서는 email과 password 필드를 사용하여 인증을 수행합니다. authorize 메서드에서는 입력된 이메일과 비밀번호를 기반으로 유저를 인증하고, Prisma를 사용하여 DB에서 유저 정보를 가져옵니다.

pages:
AuthOptions 객체의 pages 속성은 사용자 인증 페이지를 지정합니다. 이 코드에서는 사용자 인증 페이지를 홈페이지('/')로 설정합니다.

위 코드에서 signIn 속성에 /를 할당하면, 사용자가 로그인을 시도할 때 브라우저는 "/" 경로로 리디렉션됩니다.

만약, signIn에 다른 경로를 할당하면 해당 경로에 맞게 리디렉션이 이루어집니다.

다른 페이지도 지정할 수 있으며, signOut은 로그아웃에 사용되는 페이지 경로를 지정하고, error는 오류가 발생했을 때 사용되는 페이지 경로를 지정합니다.

debug:
AuthOptions 객체의 debug 속성은 디버그 모드를 활성화합니다. 이 속성은 개발 환경에서 유용합니다.

session: AuthOptions 객체의 session 속성은 세션 관리 전략을 설정합니다. 여기서는 JWT(Json Web Token) 전략을 사용합니다. JWT는 서버가 클라이언트에게 발급한 JSON 형식의 토큰을 사용하여 인증하는 방식입니다.

secret:
AuthOptions 객체의 secret 속성은 JWT 토큰에 사용되는 비밀키를 설정합니다. 이 비밀키는 랜덤한 문자열로 구성됩니다.

3. 로그인 창 만들고 signIn 함수 사용하기

'use client'

import { useCallback, useState } from 'react'
import { toast } from 'react-hot-toast'
import { signIn } from 'next-auth/react' // 중요 !!!!
import { FieldValues, SubmitHandler, useForm } from 'react-hook-form'
import { useRouter } from 'next/navigation'

import useRegisterModal from '@/app/hooks/useRegisterModal'
import useLoginModal from '@/app/hooks/useLoginModal'

import Modal from './Modal'


const LoginModal = () => {
  const router = useRouter()
  const loginModal = useLoginModal()
  const registerModal = useRegisterModal()
  const [isLoading, setIsLoading] = useState(false)

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FieldValues>({
    defaultValues: {
      email: '',
      password: '',
    },
  })

  const onSubmit: SubmitHandler<FieldValues> = (data) => {
    setIsLoading(true)

    signIn('credentials', {
      ...data,
      redirect: false,
    }).then((callback) => {
      setIsLoading(false)

      if (callback?.ok) {
        toast.success('Logged in')
        router.refresh()
        loginModal.onClose()
      }

      if (callback?.error) {
        toast.error(callback.error)
      }
    })
  }

  ...
  
  
  const footerContent = (
    <div className="flex flex-col gap-4 mt-3">
      <hr />
      <Button
        outline
        label="Continue with Google"
        icon={FcGoogle}
        onClick={() => signIn('google')}
      />
      <Button
        outline
        label="Continue with Github"
        icon={AiFillGithub}
        onClick={() => signIn('github')}
      />
      <div
        className="
      text-neutral-500 text-center mt-4 font-light"
      >
        <p>
          First time using Airbnb?
          <span
            onClick={onToggle}
            className="
              text-neutral-800
              cursor-pointer 
              hover:underline
            "
          >
            {' '}
            Create an account
          </span>
        </p>
      </div>
    </div>
  )

  return (
    <Modal
      disabled={isLoading}
      isOpen={loginModal.isOpen}
      title="Login"
      actionLabel="Continue"
      onClose={loginModal.onClose}
      onSubmit={handleSubmit(onSubmit)}
      body={bodyContent}
      footer={footerContent}
    />
  )
}

export default LoginModal

위 코드에서 signIn은 next-auth/react에서 제공하는 함수로, 사용자를 로그인시키는 함수입니다.
이 함수는 첫 번째 매개변수로 로그인 전략을 받고, 두 번째 매개변수로 해당 로그인 처리에 필요한 정보를 전달합니다.
처리방식은 로그인 방식에 따라 다르며, 이메일과 비밀번호를 이용한 로그인의 경우 credentials 로그인 처리 방식을 사용합니다.
이 경우에는 로그인 정보를 객체 형태로 전달해야 합니다.

signIn 함수의 반환 값은 Promise 객체이며, 로그인 성공 여부와 에러 정보를 포함하는 객체를 반환합니다. 이를 통해 성공적으로 로그인한 경우 toast.success를 호출하여 알림을 띄우고, 실패한 경우 toast.error를 호출하여 사용자에게 알릴 수 있습니다.

4. 로그인 유무 확인하기

로그인 확인 하는 방법은 서버에서 확인 하는 방법과 클라이언트에서 확인하는 방법 이렇게 두가지의 방법이 있는데 저는 서버에서 확인하는 방법을 예시로 설명하겠습니다 .

file : app/actions/getCurrentUser.ts
import { getServerSession } from 'next-auth/next'

import { authOptions } from '@/pages/api/auth/[...nextauth]'
import prisma from '@/app/libs/prismadb'

export async function getSession() {
  return await getServerSession(authOptions)
}

export default async function getCurrentUser() {
  try {
    const session = await getSession()

    if (!session?.user?.email) {
      return null
    }

    const currentUser = await prisma.user.findUnique({
      where: {
        email: session.user.email as string,
      },
    })

    if (!currentUser) {
      return null
    }

    return {
      ...currentUser,

    }
  } catch (error: any) {
    return null
  }
}

먼저, getServerSession() 함수를 사용하여 현재 로그인한 사용자의 세션을 가져옵니다.
authOptions 객체는 pages/api/auth/[...nextauth] 아까 생성한 파일에 설정된 options 객체를 가져와 사용합니다.

그 다음, session 객체에서 사용자의 이메일을 확인합니다. 이메일이 없는 경우 null을 반환하고, 이메일이 있는 경우 Prisma를 사용하여 해당 사용자를 찾습니다.

마지막으로, 사용자의 정보를 return하여 반환합니다.

5. 로그인 유무가 필요한 컴포넌트에서 getCurrentUser 함수를 가져와서 사용

... 

const PropertiesPage = async () => {
  const currentUser = await getCurrentUser()

  if (!currentUser) {
    return <EmptyState title="Unauthorized" subtitle="Please login" />
  }


export default PropertiesPage

next-auth를 사용하면 이렇게 간편하게 다양한 소셜 로그인 기능과 이메일 패스워드 로그인 기능을 구현할 수 있습니다 .

0개의 댓글