Javascript/Next.js

[ Next.js ] - next-auth 사용법 (credentials)

algml0703 2022. 6. 18. 02:20
반응형

next-auth

next에서 사용되는 패키지의 일종으로 로그인과 같은 사용자 인증 기능을 제공하여 준다. Github, Google, Facebook, Credentials 등의 인증 옵션을 제공한다.

여러 인증 옵션 중에서 Credentials는 사용자로부터 아이디와 비밀번호를 받아 백엔드 서버를 통해 인증하는 것이다.

사용법

기본적으로 next 환경을 세팅하여 준다.

> npx create-next-app --typescript

해당 명령어 입력후 프로젝트 명을 입력하고 기본설정을 선택하여 준다.

next-auth사용을 위한 패키지를 설치하여 준다.

> npm install next-auth

or

> yarn add next-auth

.env파일에 NEXTAUTH_URL=http://localhost:3000을 추가하여 준다.  // 실제 배포시에는 배포된 url을 적어준다.

상위 컴포넌트인 _app.tsx 파일에서 컴포넌트를 SessionProvider로 감싸준다.

import { AppProps } from 'next/app';
import { SessionProvider } from "next-auth/react"

function App({ Component,pageProps} : AppProps) {
  return (
    <SessionProvider session={pageProps.session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

export default App

위와 같이 감싸줌으로써 프로젝트의 전역에서 로그인 상태값을 공유할 수 있게 된다.

pages 폴더에 api폴더 api폴더에 auth폴더 auth 폴더에 [...nextauth].ts 파일을 생성한다. 아래와 같은 폴더 구조가 된다.

[...nextauth].ts 파일의 내용을 아래와 같이 작성한다.

import NextAuth from "next-auth";
import CredentialsProvider from 'next-auth/providers/credentials';
import crypto from 'crypto';

// 비밀번호 암호화 함수
export async function hashPassword(password: string): Promise<string> {
    const hashedPassword = await hash(password, 12);
    return hashedPassword;
}

// 비밀번호 검증 함수
export async function verifyPassword(
    password: string,
    hashedPassword: string
): Promise<boolean> {
    const isValid = await compare(password, hashedPassword);
    return isValid
}

export default NextAuth({
    providers: [
        CredentialsProvider({
            id: 'user-credentials',
            name: "Credentials",
            credentials: {
                nick: { label: "Nick", type: "text", placeholder: "아이디를 입력하세요" },
                password: { label: "Password", type: "password" }
            },
            async authorize(credentials) {
                const { nick, password } = credentials!;
                // 기타 사용자 검증 로직 코드 ...
                const user = await prisma.admin.findUnique({
                    where:{
                        nick
                    }
                });
               
                if(!user) return null

                const verifyPw = await verifyPassword(password, user.password);
                if (!verifyPw) return null

                return user
            }
        }),
    ],
    secret: 'test',
    pages:{
        error: '/admin/error',
        signOut:'/admin'
    },
    session: {
        strategy: 'jwt',
        maxAge: 24 * 60 * 60,
        updateAge: 2 * 24 * 60 * 60
    },
    callbacks: {
        async jwt ({token, account}) {
            if (account) {
                token.accessToken = account.access_token
            }
            return token
        },
        async session({session, token, user}) {
            return session
        },
        async redirect ({url, baseUrl}) {
            if (url.startsWith("/")) {
                return `${baseUrl}${url}`
            }
            else if ( new URL(url).origin === baseUrl) {
                return `${baseUrl}`
            }
            return baseUrl
        }
    }
})

위와 같이 설정해준후 localhost:3000/api/auth/signin으로 들어가면 아래와 같은 로그인 창이 나오게 된다.

코드 관련 설명

name : [해당 인증을 구별할 명칭]   
 // 일반적으로 Credentials로 입력한다.
credentials: { }     
// 인증시 입력할 것을 설정하는 부분이다.
async authorize (credentials, req) {} 
// 인자값 credentials는 위의 인증시에 입력한 body값이 전달된다. 인증 관련 로직을 작성하는 곳이다.

실제 검증시 사용법

import { FC, memo } from 'react';
import { signIn, signOut, useSession } from 'next-auth/react'

interface IUserLogin{}

const UserLogin: FC<IUserLogin> = () => {
    const { data: session, status } = useSession()

    const handleLoginBtn = async (e: any) => {
        e.preventDefault();
        const nick = e.target.nick.value;
        const password = e.target.password.value;
        const response = await signIn('user-credentials', {
            nick,
            password,
            redirect: false
        })
    }

    if (status === 'loading') return <></>

    if (!session) {
        return (
            <>
                <form onSubmit={(ele) => handleLoginBtn(ele)}>
                    <label>
                        ID
                    </label>
                    <input name='nick' placeholder='아이디를 입력하세요' />
                    <label>
                        Password
                    </label>
                    <input name='password' type="password" placeholder='비밀번호를 입력하세요' />
                    <button type='submit'>로그인</button>
                </form>
            </>
        )
    };

    return (
        <>
            <div>
                사용자 추가
            </div>
            <div onClick={() => signOut()}>
                로그아웃
            </div>
        </>
    )
}


export default memo(UserLogin)
  • status 는 현재 로그인이 된 것인지 아닌지에 대한 정보를 제공한다. status값으로는 'authenticated' , 'loading', 'unauthenticated' 세가지 상태가 존재한다.
    • authenticated : 로그인이 완료된 상태이다.
    • loading : 로그인 처리중인 상태이다.
    • unauthedticated : 로그인 되어 있지 않은 상태이다.
  • signOut()은 인자값없이 사용시 단순히 로그아웃 처리되며, 인자값으로 url을 주면 로그아웃 처리되고 해당 url로 이동하게 된다.
  • signIn()은 클릭하면 로그인 화면으로 이동하여 준다.
 

 

출처

https://next-auth.js.org/getting-started/example

반응형