본문 바로가기
Resource/웹 프론트엔드

[Next.js] OAuth 인증 매커니즘

by 우창욱 2024. 1. 15.

사전 지식

OAuth (Open Authorization) 2.0

OAuth는 인터넷 사용자가 웹사이트나 어플리케이션에 특정 정보를 제공할 수 있게 해주는 표준 인증 프로토콜입니다. 이 프로토콜은 사용자가 자신의 정보를 공유하는 데 있어 별도의 로그인 정보를 제공하지 않고도 서비스 제공업체(ex. Google, GitHub, Facebook, etc.)를 통해 안전하게 인증할 수 있도록 해줍니다. (OAuth 2.0은 기존 OAuth 1.0 프로토콜을 대체하는 프로토콜입니다.)

 

OAuth 2.0 프로토콜은 사용자의 자격 증명을 직접 공개하지 않으면서 안전하게 서드파티 어플리케이션에 리소스 접근 권한을 제공해줍니다. 

OAuth 매커니즘

1. 권한 요청 (Authorization Request)

클라이언트 어플리케이션은 사용자를 인증 서버의 로그인 및 권한 부여 페이지로 리디렉션합니다. 이때 클라이언트 ID가 URL 매개변수로 전송됩니다. 이 권한 부여 페이지는 사용자에게 로그인하고, 클라이언트 어플리케이션에 특정 권한 (ex. 프로필 정보 접근)을 부여할 지 묻습니다. 

2. 사용자 인증 및 권한 부여 (User Authentication and Authorization)

사용자는 인증 서버에서 자신의 계정으로 로그인합니다. 사용자가 로그인하면 인증 서버는 사용자에게 클라이언트 어플리케이션이 요구하는 권한을 부여할 것인지를 묻게되고, 사용자는 이 권한을 승인하거나 거부할 수 있습니다.

3. 인증 코드 발급 (Authorization Code Grant)

사용자가 권한을 부여하면 인증 서버는 사용자를 클라이언트 어플리케이션의 리디렉션 URL로 리디렉션합니다. 이 리디렉션 URL에는 인증 코드가 포함되는데 인증 코드는 엑세스 토큰을 얻기 위한 일회용 코드입니다.

4. 액세스 토큰 요청 (Access Token Request)

클라이언트 어플리케이션은 인증 코드를 사용하여 인증 서버에 액세스 토큰을 요청합니다. 이 요청은 클라이언트 ID, 클라이언트 비밀번호, 인증 코드를 포함합니다. 이는 서버 사이드에서 이루어지기 때문에, 사용자의 브라우저를 통하지 않아서 더 안전합니다.

5. 액세스 토큰 발급 (Access Token Response)

인증 서버는 클라이언트의 요청을 검증하고, 올바르다면 액세스 토큰(그리고 필요에 따라 리프레시 토큰)을 발급합니다. 액세스 토큰은 사용자의 자원에 접근할 수 있는 '키' 역할을 합니다. 리프레시 토큰은 액세스 토큰이 만료되었을 때 새로운 토큰을 받기 위해 사용합니다.

6. 리소스 서버 접근 (Accessing Resource Server)

클라이언트 어플리케이션은 발급받은 액세스 토큰을 사용하여 리소스 서버에 접근합니다. 액세스 토큰은 HTTP 요청의 Authorization 헤더에 포함되어 리소스 서버에 전송됩니다. 이 토큰은 리소스 서버가 사용자가 부여한 권한에 따라 클라이언트 어플리케이션에 특정 데이터에 대한 접근을 허용하도록 합니다.

7. 데이터 반환 (Returning Data)

리소스 서버는 액세스 토큰을 검증한 후, 요청된 데이터를 클라이언트 어플리케이션에게 제공합니다. 리소스 서버는 토큰의 유효성, 만료 시간, 그리고 토큰이 부여된 권한을 검사합니다. 모든 것이 유효하면 리소스 서버는 클라이언트의 요청에 따라 사용자 데이터를 제공합니다.


Next.js에서 OAuth를 구현하는 프로세스

Nextjs에서 OAuth를 구현하는 프로세스

사용 라이브러리

1. next-auth 5.0.0-beta.3

2. prisma^5.8.0

3. @auth/prisma-adapter

 

src/app/auth.ts

import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/db";

const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID;
const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET;

if (!GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET) {
  throw new Error("Missing github oauth credentials");
}

export const {
  handlers: { GET, POST },
  auth,
  signOut,
  signIn,
} = NextAuth({
  adapter: PrismaAdapter(db),
  providers: [
    GitHub({
      clientId: GITHUB_CLIENT_ID,
      clientSecret: GITHUB_CLIENT_SECRET,
    }),
  ],
});

 

NextAuth에는 NextAuthConfig 파라미터를 넘겨줄 수 있습니다. adapter 프로퍼티를 사용하면 사용하는 데이터베이스에 사용자 정보를 바로 저장할 수 있도록 해줍니다. 또한 providers 프로퍼티에 OAuth Provider를 넘겨주면, NextAuth 내부적으로 client_id, clientSecret을 넘겨주기 때문에 OAuth 인증과정이 매우 편해집니다.

 

src/app/api/auth/[...nextauth]/route.ts

export { GET, POST } from "@/auth";

 

[...nextauth] 형식은 Next.js에서 사용되는 특별한 파일 이름 규칙입니다. 이 규칙은 API 라우트나 페이지 라우터에서 "모든 경로를 매핑" 하기 위해 사용됩니다. 이 폴더를 사용하면 `/api/auth/callback/github`, `/api/auth/signin`, `/api/auth/signout` 과 같은 경로를 하나의 파일로 처리할 수 있게 됩니다. 즉 next-auth가 처리해야 하는 다양한 인증 관련 경로들을 효과적으로 관리하고 라우팅의 유연성을 제공하는 기능입니다.

 

src/app/providers.tsx

"use client";

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

interface ProvidersProps {
  children: React.ReactNode;
}

export default function Providers({ children }: ProvidersProps) {
  return <NextUIProvider>{children}</NextUIProvider>;
}

 

next-auth에서 제공하는 SessionProvider 컴포넌트를 RootLayout.tsx파일에 사용하면, 전체 어플리케이션의 사용자 인증을 구현할 수 있습니다.