본문 바로가기

개발 공부/React

NextAuth.js 사용자 인증 로직 (1)

코드 출처 : https://github.com/Cluster-Taek/next14-boilerplate

 

GitHub - Cluster-Taek/next14-boilerplate

Contribute to Cluster-Taek/next14-boilerplate development by creating an account on GitHub.

github.com

 

import { tokenRefresh } from '@/lib/common/account';
import dayjs from 'dayjs';
import { jwtDecode } from 'jwt-decode';
import { NextAuthOptions, User } from 'next-auth';
import CredentialsProvider, { CredentialsConfig } from 'next-auth/providers/credentials';

const credentialsProviderOption: CredentialsConfig = {
  type: 'credentials',
  id: 'login-credentials',
  name: 'login-credentials',
  credentials: {
    login: { label: 'Email', type: 'text' },
    password: { label: 'Password', type: 'password' },
  },
  async authorize(credentials: Record<string, unknown> | undefined) {
    try {
      if (credentials?.login === 'test@gmail.com' && credentials?.password === '1234') {
        const user = {
          name: 'John Doe',
          accessToken:
            'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
          refreshToken: 'refreshToken',
        };
        return {
          id: credentials?.login as string,
          name: user.name,
          accessToken: user.accessToken,
          refreshToken: user.refreshToken,
        };
      } else {
        return null;
      }
    } catch (error) {
      console.error(error);
      return null;
    }
  },
};
export const authOptions: NextAuthOptions = {
  pages: {
    signIn: '/login',
    error: '/login',
  },
  providers: [CredentialsProvider(credentialsProviderOption)],
  callbacks: {
    async jwt({ token, user, account, trigger, session }) {
      if (trigger === 'update') {
        token.user = session.user;
        return token;
      }

      // [1] 최초 로그인 시
      if (account && user) {
        return {
          user,
          accessToken: user.accessToken,
          refreshToken: user.refreshToken,
          exp: jwtDecode(user.accessToken as string).exp as number,
        };
      }

      // [2] 로그인 이후 토큰 만료 전
      if (dayjs().isBefore(dayjs((token.exp as number) * 1000))) {
        return token;
      }

      // [3] 로그인 이후 토큰 만료 후
      const { accessToken, refreshToken } = await tokenRefresh(token.refreshToken as string);

      token.accessToken = accessToken;
      token.refreshToken = refreshToken;
      token.exp = jwtDecode(accessToken).exp as number;

      return token;
    },

    async session({ session, token }) {
      if (token.user) {
        session.user = token.user as User;
      }
      return session;
    },
  },
};
  1. 로그인:
    • 사용자가 입력한 자격 증명(credentials)을 검증하여 사용자 정보 및 토큰을 반환.
  2. JWT 관리:
    • 최초 로그인 시 토큰 생성.
    • 토큰 만료 전에는 기존 토큰 유지.
    • 만료 후에는 새 토큰 갱신(tokenRefresh 함수 호출).
  3. 세션 관리:
    • 사용자 정보를 세션에 포함하여 클라이언트에서 접근 가능하도록 설정.

 

1. CredentialsProvider 설정

credentialsProviderOption은 사용자 로그인을 처리하기 위한 CredentialsProvider의 옵션을 정의합니다.

  • type: 로그인 방식으로 credentials(사용자 입력 정보 기반)을 사용.
  • id, name: 제공자 식별자와 이름을 정의.
  • credentials: 사용자로부터 받을 입력 필드를 설정 (login과 password).
  • authorize(credentials):
    • 사용자가 입력한 자격 증명을 기반으로 인증을 처리합니다.
    • 예제에서는 login이 "test@gmail.com"이고, password가 "1234"일 때만 인증 성공 처리합니다.
    • 성공 시, 사용자 정보를 반환하고 실패 시 null을 반환.

2. authOptions 설정

authOptions는 NextAuth의 설정 객체로 다음과 같은 요소를 포함합니다.

(1) pages

  • signIn: 로그인 페이지 경로 (/login).
  • error: 인증 오류 시 표시할 페이지 경로 (/login).

(2) providers

  • CredentialsProvider(credentialsProviderOption)를 추가하여 사용자 정의 자격 증명 방식을 사용.

(3) callbacks

  • 콜백은 특정 인증 단계에서 커스터마이징이 필요할 때 사용됩니다.
  • (a) jwt
    • JWT(JSON Web Token)를 생성하거나 갱신합니다.
    • trigger 값에 따라 동작이 다르게 정의됩니다.
      • 최초 로그인 시:
        • 사용자 정보(user)와 인증 정보(account)가 전달됩니다.
        • 반환된 토큰에 사용자 정보, accessToken, refreshToken, 만료 시간(exp)을 저장합니다.
      • 로그인 이후, 토큰이 유효한 경우:
        • 현재 시간이 만료 시간보다 이전일 경우(dayjs().isBefore(dayjs((token.exp as number) * 1000))), 기존 토큰을 반환합니다.
      • 로그인 이후, 토큰이 만료된 경우:
        • tokenRefresh 함수를 호출해 새로운 accessToken과 refreshToken을 발급받습니다.
        • 토큰 정보를 갱신하고 반환합니다.
  • (b) session
    • 세션 객체에 사용자 정보를 포함시킵니다.
    • JWT 토큰에서 사용자 정보를 추출하여 session.user에 저장.

 

다음 글에서 어떻게 이 코드가 상호작용하는지 작성하겠습니다