728x90

Next.auth를 통해 인증인가 설정하는 이유

→ 유저의 Role에 따라 페이지 접근을 제어하기 때문입니다.

회원 정보를 담아야 하기 때문에 샘플 DB와 ORM을 설정할 필요가 있었습니다. 따라서, Prisma ORM 과 Postgre를 통해 입력값을 담을 준비를 마쳤습니다.

 

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}
enum UserType {
  USER
  ADMIN
}

model User {
  id            String    @id @default(cuid())
  name          String?
  hashedPassword String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
  UserType      UserType  @default(USER)
}

 

먼저 Prisma ORM의 스키마를 설정해서 디비에 저장할 테이블을 생성할 준비를 합니다.

version: "3"
services:
  db:
    image: postgres:latest
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: "root"
      POSTGRES_PASSWORD: "1234"
    volumes:
      - ./data:/var/lib/postgresql/data

 

그리고 DB는 따로 설치를 하지 않고 도커컴포즈 파일을 작성해서 띄워 사용하였습니다.

DATABASE_URL="postgresql://root:1234@localhost:5432/next2db"

NEXTAUTH_SECRET=nextAuthSecret
NEXTAUTH_URL=http://localhost:3000

JWT_SECRET=jwt-secret

 

마지막으로 .env파일을 작성해 Next.js 프로젝트를 DB와 연결하였습니다.

 

본격적으로 NextAuth를 통해 회원 가입 / 로그인 기능 개발

const prisma = new PrismaClient()

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    CredentialsProvider({
      // The name to display on the sign in form (e.g. "Sign in with...")
      name: "Credentials",
      // `credentials` is used to generate a form on the sign in page.
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      // You can pass any HTML attribute to the <input> tag through the object.
      credentials: {
        username: { label: "Username", type: "text", placeholder: "jsmith" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials, req) {
        // Add logic here to look up the user from the credentials supplied
        const user = { id: "1", name: "J Smith", email: "jsmith@example.com", role: "USER" }
  
        if (user) {
          // Any object returned will be saved in `user` property of the JWT
          return user
        } else {
          // If you return null then an error will be displayed advising the user to check their details.
          return null
  
          // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
        }
      }
    })
  ],

 

https://next-auth.js.org/providers/credentials

 

Credentials | NextAuth.js

Overview

next-auth.js.org

 

NextAuth 공식 문서에는 NextAuth를 연결하기만 하면 기본적으로 회원가입과 로그인 기능이 제공되고 있습니다.

공식문서에서 제공해주는 구글 로그인과 자체 로그인 코드를 토대로 개발을 진행했습니다.

 

// 세션 관리는 jwt로 지정
  session: {
    strategy: 'jwt',
  },
// jwt 설정 -> 시크릿은 env파일에 설정해둠 + 유효기간 30일
  jwt: {
    secret: process.env.JWT_SECRET,
    maxAge: 30 * 24 * 60 * 60 // 30 days
  },
//토큰 내에 사용자 정보가 포함되어, 이후 세션 생성 시 이용
  callbacks: {
    async jwt({token, user}) {
      return {...token, ...user}

    },
    async session({session, token}) {
      session.user = token
      return session
    }
  }
}

 

제공되는 코드에 추가로 설정할 부분은 로그인 이후 세션을 어떻게 관리할 지 정의하는 것이었습니다.

JWT 토큰을 통해 세션 정보를 유지하기 위해 콜백함수로 관리했습니다.

 

JWT 콜백 : 사용자가 로그인할 때 또는 토큰이 갱신될 때 실행
→ 사용자가 로그인하는 순간, user 객체에 포함된 정보를 token 객체와 병합하여 반환
session 콜백 : 서버 측에서 생성된 세션 정보를 클라이언트 측에 맞게 조정하거나 추가 데이터를 세션에 포함
→ session 객체 내에 user 속성을 추가하고, 이를 jwt 콜백에서 반환된 token 객체로 설정

 

import { getToken } from 'next-auth/jwt';
import { NextRequest, NextResponse } from 'next/server';

export {default} from 'next-auth/middleware'

export async function middleware(req: NextRequest) {
  const session = await getToken({req, secret: process.env.JWT_SECRET});
  
  const pathName = req.nextUrl.pathname;

  // 관리자 페이지 접근 권한이 없는 경우 메인 페이지로 리다이렉트
  if (pathName.startsWith('/admin') && session?.role !== 'ADMIN') {
    return NextResponse.redirect(new URL("/", req.url));
  }
  // 로그인되지 않은 상태에서 유저 페이지 접근시 로그인 페이지로 리다이렉트
  if (pathName.startsWith('/user') && !session) {
    return NextResponse.redirect(new URL("/auth/login", req.url));
  }
  // 로그인 상태에서 로그인 페이지, 회원가입 페이지 접근시 메인 페이지로 리다이렉트
  if (pathName.startsWith('/auth') && session) {
    return NextResponse.redirect(new URL("/", req.url));
}
  return NextResponse.next();
}

 

미들웨어 파일을 두고 세션의 속성 값에 따라 접근 가능한 경로를 제어했습니다

/admin:path*와 같은 와일드카드 패턴을 사용해, 하위 모든 경로를 제어할 수 있었습니다.

 

 

+ Recent posts