OAuth 인증부터 쿠키 저장 관리까지

단단·2024년 12월 22일
3

글또

목록 보기
6/9

안녕하세요. 단단입니다.

저는 요즘 자동 포스팅 사이트를 구현하는 프로젝트를 하고 있습니다.
우선, 해당 프로젝트에 Google OAuth 인증과 인가를 구현했습니다.

저는 이전 '포잉마켓' 프로젝트에서도 Google, Kakao Talk OAuth를 구현했는데, 여기서 백엔드 개발자와 Flow를 명확하게 '소통'하는 게 중요하다는 것을 깨달았습니다.

OAuth 인증을 구현하고, access token을 저장하는 방식도 다양하기 때문에 둘이 인식한 Flow를 잘 맞추는 게 구현 시간을 단축할 수 있기 때문입니다.

그래서 오늘은 구글 OAuth 인증 후 토큰을 쿠키에 저장해 인가까지 구현한 과정을 정리해보려고 합니다.

자동 포스팅 사이트 로그인 페이지

용어 정의

OAuth: 유저 인증 정보를 다른 사이트에 제공하지 않으면서 제 3자가 보호된 리소스에 접근할 수 있게 하는 절차 / 인가를 위한 표준
인증: 리퀘스트한 유저가 누구인지 파악하는 기능 / ex: 사이트 가입 여부 확인
인가: 리퀘스트 내용을 요청할 권한이 있는지 확인하는 기능 / ex: 게시글 삭제 권한이 있는 유저 확인
쿠키: 서버 리스폰스나 클라이언트 코드에 따라 브라우저에 저장되는 작은 단위의 문자열 파일들
*쿠키 사용 시 장점

  • CSRF 공격을 예방할 수 있는 옵션 SameSite를 설정할 수 있습니다.
  • 쿠키는 클라이언트가 서버에 요청을 보낼 때 자동으로 포함되므로 별도로 헤더에 담지 않아도 요청을 보낼 때 자동으로 담아서 보낼 수 있습니다.

OAuth Flow

  1. 유저가 버튼을 클릭하면 클라이언트에서 Google OAuth 인증 페이지로 redirect 합니다.
  2. 유저의 Google Login 후 서버에서 Redirect URI를 클라이언트의 callback 페이지로 설정하고, JWT 토큰을 생성해 클라이언트에 전달합니다.
  3. 클라이언트는 callback 페이지에서 인증 여부에 따라 라우팅을 하고, 쿠키에 JWT 토큰을 저장합니다.

쿠키 관리 주체

  • 서버가 쿠키를 관리할 때: 처음에 쿠키를 서버에서 만들어서 인증을 구현했습니다. 이렇게 하면 CSRF 공격 등에 대응해 보안을 강화할 수 있다는 장점이 있습니다. 그러나 클라이언트에서 쿠키를 직접 조작할 수 없어 기능을 구현하는 데 불편함이 있었습니다. 로그아웃 기능을 구현할 때 클라이언트에서 쿠키를 삭제할 수 없으니 서버에 요청을 보내야 했고, 이런 부분이 유저가 많아지면 서버 부하가 증가할 수 있다고 생각했습니다. 아울러 CORS 에러가 계속 발생해 이때마다 CORS 설정을 백엔드 개발자에게 요청하기도 했습니다.

  • 로직 수정 과정: 우선, 계속 발생하는 CORS 에러를 새결하기 위해 클라이언트에서 요청을 보낼 때 withCredentials 속성을 true로 설정했습니다. 그런데 이렇게 속성을 설정해도 서버의 설정의 수정해야 하는 상황이 계속 발생했습니다. 그래서 결국 호환성과 기능 구현 편의성을 위해 클라이언트에서 쿠키를 관리하는 로직으로 수정했습니다.

  • 클라이언트가 쿠키를 관리할 때: 클라이언트에서 쿠키를 삭제할 수 있어 추가적인 서버 요청없이 기능 구현을 할 수 있고, 서버 부하를 줄일 수 있다는 게 장점인 것 같습니다. 아울러 요청을 보낼 때 발생하던 CORS 에러도 서버의 CORS 설정에 의존하지 않아서 관리하기 효율적이었습니다.

구현 코드

  • 클라이언트에서 Google OAuth 인증 페이지로 redirect
<Button
        className="w-72 mt-20 px-4 flex items-center gap-10 h-12 bg-transparent border border-gray-300"
        href={`${API_BASE_URL}/api/auth/google/login${isLocal ? '/local' : ''}`}>
        <img src={googleLogo} alt="Google Logo" className="rounded-full" />
        <p className="pr-8">Google로 계속하기</p>
</Button>
  • 클라이언트 callback 페이지에서 라우팅, 쿠키에 JWT 토큰 저장
useEffect(() => {
    if (data) {
      if (data.success) {
        setCookie('access_token', data.tokens.access, { path: '/', secure: true, httpOnly: true, sameSite: 'lax' });
        setCookie('refresh_token', data.tokens.refresh, { path: '/', secure: true, httpOnly: true, sameSite: 'lax' });
        navigate('/my');
      } else {
        navigate('/login');
      }
    }
  }, [data, navigate, setCookie]);

인가 기능 구현

  • cookie에 저장한 access token이 없을 때 로그인 외 페이지에 접근하지 못하게 인가 기능을 구현했습니다.
  • React-Router-Dom(7.02 버전)을 사용해 라우팅했습니다.
function ProtectedRoute({ element }: { element: JSX.Element }) {
  const [cookies] = useCookies(['access_token']);
  return cookies.access_token && cookies.access_token !== 'undefined' ? element : <Navigate to="/login" />;
}

const router = createBrowserRouter([
  {
    path: '/login',
    element: <Login />,
  },
  {
    path: '/auth/google/callback',
    element: <GoogleCallback />,
  },
  {
    path: '/',
    element: <ProtectedRoute element={<Layout />} />,
    children: [
      {
        path: '',
        element: <ProtectedRoute element={<My />} />,
      },
      {
        path: 'my',
        element: <ProtectedRoute element={<My />} />,
      },
      {
        path: 'setting',
        element: <ProtectedRoute element={<Setting />} />,
      },
      {
        path: '*',
        element: <Login />,
      },
    ],
  },
]);

export default router;

마무리

오늘은 인증 인가를 구현해본 경험을 정리해봤습니다.
이 글을 읽는 분들께 조금이나마 도움이 됐길 바라며, 모든 피드백 환영합니다~

profile
반드시 해내는 프론트엔드 개발자

0개의 댓글

관련 채용 정보