Published On

NextAuth.js 인증 구현하기


NextAuth.js는 Next.js 애플리케이션에서 쉽게 인증 기능을 구현할 수 있도록 도와주는 라이브러리입니다.

NextAuth.js v5(beta)가 최신 버전이지만 v4를 가장 많이 사용하여 v4에 대해 알아보겠습니다.

NextAuth.js란?

NextAuth.js는 Next.js 애플리케이션을 위한 완전한 인증 솔루션입니다.

OAuth, 이메일/비밀번호, SMS 등 다양한 인증 방식을 지원하며, 보안성과 확장성을 갖춘 라이브러리입니다.

  • 다양한 인증 제공자 지원: Google, Facebook, Twitter, GitHub 등 여러 소셜 로그인 제공자를 지원합니다.
  • 보안성: JWT(JSON Web Token)와 세션을 모두 지원하여 안전한 인증 관리를 제공합니다.
  • 확장성: 커스터마이징이 용이하며, 다양한 설정 옵션을 제공합니다.

프로젝트 설정

  1. NextAuth.js 설치 NextAuth.js를 프로젝트에 추가합니다. 다음 명령어를 실행하여 NextAuth.js와 관련 종속성을 설치합니다.

prisma는 개발에서만 필요하므로 --save-dev로 설치해주세요.

npm install next-auth
npm install --save-dev prisma
npm install @prisma/client @auth/prisma-adapter
  1. API 라우트 설정

NextAuth.js를 설정하려면 API 라우트를 생성해야 합니다.

pages/api/auth/[...nextauth].js 혹은 app/api/auth/[...nextauth]/route.js 파일을 생성합니다.

  • 그리고 prisma client와, PrismaAdapter를 불러고 옵션을 설정합니다.
app/api/auth/[...nextauth]/route.tsjavascript
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
 
const prisma = new PrismaClient()
 
export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [],
})
  • 단, 대부분 authOption 부분은 따로 빼서 작성합니다.
app/api/auth/[...nextauth]/route.tsjavascript
import NextAuth from 'next-auth';
import { authOptions } from '@/libs/auth-options';

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
  1. 환경 변수 설정

사용할 Oauth가 있다면 환경변수를 config 파일, .env 파일 등에 설정해 줍니다.

next.config.mjsjavascript
module.exports = {
  env: {
    GOOGLE_CLIENT_ID: 'your-google-client-id',
    GOOGLE_CLIENT_SECRET: 'your-google-client-secret',
    DATABASE_URL: 'your-database-url', 
    NEXTAUTH_SECRET: 'your-nextauth-secret',
  },
};
  1. prisma 설정
  • npx prisma init -> schema 설정 -> npx prisma migrate dev --name init

  • 아래의 예시 코드는 mysql을 기준으로 작성 및 설명하였습니다.

prisma/schema.prisma
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}
 
generator client {
  provider = "prisma-client-js"
}
 
model User {
  id            String          @id @default(cuid())
  name          String?
  username      String?         @unique
  email         String?         @unique
  emailVerified DateTime?
  image         String?
  Session       Session[]
  Account       Account?
  // Optional for WebAuthn support
  Authenticator Authenticator[]
 
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
 
model Account {
  id                       String  @id @default(cuid())
  userId                   String  @unique
  type                     String
  provider                 String
  providerAccountId        String
  refresh_token            String? @db.Text
  access_token             String? @db.Text
  expires_at               Int?
  token_type               String?
  scope                    String?
  id_token                 String? @db.Text
  session_state            String?
  refresh_token_expires_in Int?
  user                     User?   @relation(fields: [userId], references: [id])
 
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
 
  @@unique([provider, providerAccountId])
  @@index([userId])
}
 
model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id])
 
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
 
  @@index([userId])
}
 
model VerificationToken {
  identifier String
  token      String
  expires    DateTime
 
  @@unique([identifier, token])
}
 
// Optional for WebAuthn support
model Authenticator {
  credentialID         String  @unique
  userId               String
  providerAccountId    String
  credentialPublicKey  String
  counter              Int
  credentialDeviceType String
  credentialBackedUp   Boolean
  transports           String?
 
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
 
  @@id([userId, credentialID])
}

여기서는 가장 많이 사용되는 PrismaAdapter를 기준으로 설명합니다.

PrismaAdapter는 기본적으로 위의 Table형식에 맞추서 데이터가 저장되며, 이를 수정하고 싶으면 authOptions을 커스터마이징 하여야합니다.

모델설명옵션
model User유저의 기본 정보가 저장되는 모델입니다.필수
model AccountOAuth로 로그인/회원가입 시 정보가 저장되는 모델입니다.Oauth 사용 시
model SessionauthOptions에서 저장 방식을 Session으로 할 경우 필요한 모델입니다.session 저장 사용 시
model VerificationTokenauthOptions에서 이메일 인증을 구현한 경우 필요한 모델입니다.이메일 인증 사용 시

authOptions

authOptions.tsjavascript
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import { prisma } from '@/lib/prisma'; // Prisma 클라이언트 불러오기

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    // 다른 제공자 설정 가능
  ],
  adapter: PrismaAdapter(prisma), // Prisma 어댑터 설정
  secret: process.env.NEXTAUTH_SECRET, // 보안을 위한 secret
  session: {
    strategy: 'jwt', // JWT 사용 여부
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      session.user.id = token.id;
      return session;
    },
  },
};

export default NextAuth(authOptions);
  1. providers

설명: 인증 제공자를 정의합니다. 여러 소셜 로그인 제공자(Google, Facebook, GitHub 등)를 추가할 수 있습니다.

예시: GoogleProvider, FacebookProvider 등.

  1. adapter

설명: 데이터베이스 어댑터를 설정합니다. NextAuth.js는 Prisma, TypeORM, MongoDB 등 다양한 어댑터를 지원합니다.

예시: PrismaAdapter, TypeORMSessionAdapter 등

  1. secret

설명: JWT 또는 세션의 암호화에 사용되는 비밀 키입니다. 보안을 위해 환경 변수로 설정하는 것이 좋습니다.

예시: process.env.NEXTAUTH_SECRET

  1. session

설명: 세션 관리 방법을 정의합니다. JWT를 사용할지 또는 데이터베이스 세션을 사용할지 설정합니다.

옵션:

  • strategy: 'jwt' 또는 'database'(session)

  • maxAge: 세션의 최대 유효 기간(초)

  • updateAge: 세션 갱신 간격(초)

session: {
  strategy: 'jwt', // JWT 사용 여부
  maxAge: 30 * 24 * 60 * 60, // 30일
  updateAge: 24 * 60 * 60, // 하루
}
  1. callbacks

설명: 인증 과정에서 호출되는 콜백 함수들을 정의합니다. 주로 JWT와 세션을 커스터마이징하는 데 사용됩니다.

옵션:

  • jwt: JWT를 생성하거나 수정할 때 호출됩니다.
  • session: 세션(db)을 생성하거나 수정할 때 호출됩니다.
  • signIn: 사용자가 로그인할 때 호출됩니다.
  • redirect: 로그인 후 리디렉션할 URL을 설정합니다.
  • signOut: 사용자가 로그아웃할 때 호출됩니다.

jwt 콜백 함수 예시

사용자가 로그인할 때 JWT가 생성되며, 이후 요청마다 이 콜백이 호출되어 JWT를 수정할 수 있습니다.

async jwt({ token, user }) {
  if (user) {
    token.id = user.id;
  }
  return token;
}