[OAuth] 토큰 관리 방식

Lil_Young·2025년 11월 28일

Spring

목록 보기
6/6
post-thumbnail

OAuth를 이용해 로그인 기능을 구현하다보면, 자연스럽게 생기는 의문이 있다.

Access Token은 어디에, Refresh Token은 어디에 저장할까?

이 내용을 알기 전에, 먼저 OAuth 정의부터 알아보자.

OAuth 정의

OAuth는 Open Authorization의 약자로, 신뢰할 수 있는 제 3자 인증 제공자(구글, 카카오, 네이버 등)를 통해 프로필 정보, 이메일 등에 대한 접근 권한을 위임받을 수 있도록 하는 인증 및 권한 부여 프로토콜이다.
SNS 로그인은 사용자가 구글, 네이버 등 SNS 서비스에 로그인을 하고, 이 로그인과 OAuth 기술을 통해 사용자 정보를 해당 SNS로 부터 받아와 웹 서비스에 회원가입과 로그인을 하는 것을 말한다.

정의에 대해 알아봤으니, 이제 어떻게 흘러가는지 알아보자.예시는 구글 OAuth를 기준으로 설명하겠다.

OAuth 동작 과정

  1. 사용자가 구글 로그인 버튼을 누른다.
  2. 우리 서버에서 구글에서 제공하는 로그인 화면으로 Redirect한다.
    • 구글 서버에 제어권이 넘어간다.
  3. 구글 로그인 화면에서 사용자가 로그인을 한다.
  4. 구글에서 인가 코드를 우리 서버에게 전달한다.
    • 구글에서 우리 서버에게 무언가를 전달하기 위해서는 Redirect 방식으로 전달해야 한다.
    • HTTP Redirect(302)는 GET 요청을 발생시키므로 Body를 포함할 수 없다. 그래서 구글은 Redirect 응답(302)을 통해 우리 서버의 redirect_uri로 이동시키면서, 인가 코드와 함께 state 값을 쿼리 파라미터(URL)에 전달한다.
    • Access Token이나 사용자 정보를 직접 전달하지 않는 이유는 URL 노출 위험(로그, 캐시 등)이 있기 때문이고, 대신 짧은 수명의 인가 코드를 주고 서버에서 안전하게 토큰으로 교환하도록 설계되어 있다.
  5. 우리 서버에서 인가 코드를 받으면 다음 두 가지 방식 중 하나로 처리할 수 있다.
    5-1. 인가 코드를 그대로 사용해 구글에 사용자 정보를 요청한다.
    5-2. 인가 코드를 사용해 Access Token과 Refresh Token을 발급받고, 필요한 경우 Refresh Token을 서버에 저장한 뒤, Access Token을 이용해 사용자 정보를 요청한다.

구글에서는 후자의 방식을 권장한다. 왜냐하면 인가 코드를 가지고 Access Token을 요청할 때는, 구글 서버가 아닌 우리 서버에서 요청을 보내는 것이므로 HTTP POST 요청을 해서, HTTP Response으로 데이터를 받을 수 있기 때문에, 인가 코드를 Body에 담아서 보낼 수 있으므로 보안상 안전하기 때문이다.

그럼 "인가 코드를 누군가 중간에 가로채면 위험한 거 아닌가?"
이런 의문이 들 수 있지만, 실제로는 크게 걱정할 필요가 없다.

예를 들어, 우리 서버 주소가 https://myserver.com이라고
가정해 보자.
사용자가 구글 로그인을 완료하면, 구글은 사전에 등록된 redirect_uri로만 인가 코드를 전송한다.
즉, 인가 코드는 등록된 서버가 아니면 절대 전달되지 않는다.

또한 우리 서버가 이 인가 코드를 들고 Google Token Endpoint에 토큰 발급 요청을 보내면, 구글은
1. 인가 코드가 유효한지
2. redirect_uri가 정확히 일치하는지
3. client_id가 맞는지

를 모두 검증한 뒤에만 Access Token을 발급해 준다.

따라서 인가 코드를 단순히 탈취하더라도,
redirect_uri·client_id가 맞지 않으면 토큰을 발급받을 수 없기 때문에 실제 보안 위협은 거의 없다.

이런 안전성이 가능한 이유는 인가 코드가 내부적으로 client_id, redirect_uri 등 검증용 정보와 강하게 묶여 있기 때문이다.

이처럼 인가 코드 흐름은 보안적으로 안전하게 설계되어 있다.
그렇다면 이제 우리 서버가 구글이나 카카오로부터 어떤 사용자 정보를 받아올 수 있는지,
즉 정보의 범위(scope) 가 무엇인지 살펴볼 필요가 있다.

Scope

Scope란 자사 서비스가 구글, 카카오 등 OAuth 제공자에게 요청할 수 있는 사용자 정보의 범위를 의미한다.
예를 들어 이메일, 프로필, 닉네임 등의 항목이 여기에 해당한다.
이 Scope는 인가 코드를 요청할 때 함께 지정하며, OAuth 제공자는 이 Scope에 따라 사용자에게 “어떤 정보를 제공해도 되는지”에 대한 동의를 받는다.


OAuth 구현 방식

이제 OAuth 구현 방식에 대해 알아보자.

프론트(인가코드 수신) + 서버(Access Token 교환) 방식

가장 일반적으로 사용하는 구조로, 프론트엔드에서 인가 코드를 받고, 실제 토큰 교환과 인증 처리(Access Token 발급, 사용자 정보 요청)는 백엔드 서버에서 담당하는 방식이다.

동작 방식
1. 프론트엔드에서 구글 로그인 화면을 구글에 요청해 구글 로그인 페이지로 사용자를 Redirect 한다.
2. 사용자가 구글 로그인 화면에서 로그인을 완료한다.
3. 구글에서 내 프론트화면으로 인가코드를 URL에 담아 Redirect한다.
4. 프론트엔드가 받은 인가코드를 Spring(백엔드) 서버로 전달한다.
5. 서버에서 인가코드를 사용해 구글 서버에 Access Token.(필요 시 Refresh Token 포함)을 요청한다.
6. 발급받은 Access Token을 통해 구글 API에서 사용자 정보(id, email, name 등)를 구글 서버에 요청 후 인증하여 조회한다.
7. 사용자 정보가 확인되면 서버는 자체 로그인 처리 후 JWT 토큰을 클라이언트에게 발급한다.

장점

  • JWT를 서버에서 직접 발급하므로, 클라이언트는 안전하게 Body를 통해 토큰을 전달받을 수 있다.
  • 인가 코드 → 토큰 교환 → 사용자 정보 요청까지 모든 단계가 서버에서 이루어지므로
    흐름이 명확하며, 디버깅과 로깅에 매우 유리하다.
  • 프론트에는 인가 코드만 잠시 노출되고, Access/Refresh Token은 모두 서버에서 처리되어 보안성이 높다.

단점

  • 프론트엔드에 client_id가 노출되는 구조이므로 완전한 보안 환경은 아니다. (여기서 말하는 client_id는 구글 API를 사용하기 위해 발급받는 공개용 키이며, 단독으로는 큰 위험 요소는 아니다. redirect_uri 검증이 있기 때문에 악용도 어렵다.)
  • 인증 로직 대부분이 백엔드에 집중되므로 서버 구현이 상대적으로 더 복잡해진다.

서버에서 인가코드, Access Token 모두 처리하는 방식

Spring의 oauth2-client 의존성을 활용해, 서버에서 인가코드, AccessToken 발급, 사용자 요청까지 모든 절차를 서버에서 자동 처리하는 방식이다.

장점

  • Spring Security OAuth2가 전체 흐름을 자동으로 처리하기 때문에 구현 코드가 간결하다.
  • client_id, client_secret 등 민감한 키 값을 서버 내부에서만 보관하므로 노출 위험이 적다.
  • 인증 과정 대부분이 백엔드 내부에서 이루어져, 프론트 노출 요소가 적다.

단점

  • 최종적으로 사용자에게 JWT 토큰을 줄 때, Redirect 방식을 취할 수 밖에 없어서 보안상 취약점이 존재한다.
    - 이유는, 사용자가 구글 로그인 버튼을 누르면 클라이언트에서 Spring 서버로 이동하라고 Redirect를 하고, 서버에서 구글 로그인 페이지로 이동하기 위해 다시 Redirect를 한다. 이후 인가 코드를 받고, 이 코드를 이용해 서버에서 Access Token 발급과 사용자 정보 요청을 처리한 뒤 클라이언트로 Access Token을 전달해야 하는데, 이때도 Redirect 방식으로 전달할 수밖에 없기 때문에 body에 넣어 보낼 수 없다.
  • 모든 절차가 라이브러리를 통해 통합되고 자동화되어 있어 코드 파악과 디버깅이 어렵다.
  • 인가코드가 Redirect될 때, 인가코드 요청을 했던 동일 서버가 아닌 경우 문제가 발생한다.

    여러 서버를 띄운다는 가정하에, 동일 서버로 인식하기 위해서는 로드밸런서에서 별도로 sticky 옵션 지정이 필요하다.

2개의 댓글

comment-user-thumbnail
2025년 11월 29일

유익하네요

1개의 답글