[인증/인가] OAuth 2.1은 OAuth 2.0과 무엇이 달라졌을까

들어가며

로그인 기능을 구현하면서 JWT, Cookie, Spring Security를 공부했다.

그다음 자연스럽게 OAuth2 로그인으로 관심이 이어졌다.
특히 카카오, 네이버, 구글 로그인처럼 외부 서비스를 통한 로그인은 실제 서비스에서 자주 사용된다.

처음에는 OAuth2를 “소셜 로그인”이라고만 이해했다.
하지만 공부할수록 OAuth는 원래 인증(Authentication)이 아니라 인가(Authorization)를 위한 프레임워크라는 점이 중요했다.

이번 글에서는 OAuth 2.1을 공부하며 OAuth 2.0과 어떤 점이 달라지는지 정리해보려고 한다.

참고: OAuth 2.1은 이 글을 쓰는 시점 기준으로 완성된 RFC라기보다, OAuth Working Group에서 정리 중인 Internet-Draft로 보는 것이 맞다. 따라서 세부 내용은 이후 바뀔 수 있다.

OAuth는 무엇을 해결하려고 했나

OAuth의 핵심 문제는 이것이다.

1
내 비밀번호를 알려주지 않고, 제3자 애플리케이션에게 제한된 권한만 위임하고 싶다.

예를 들어 어떤 서비스가 내 Google Calendar 정보를 읽어야 한다고 해보자.
이때 서비스에 내 Google 비밀번호를 직접 알려주는 것은 위험하다.

OAuth는 사용자가 Authorization Server에서 동의하면, Client 애플리케이션이 제한된 권한의 Access Token을 받아 Resource Server에 접근할 수 있도록 한다.

주요 역할은 다음과 같다.

역할 의미
Resource Owner 보통 사용자
Client 권한을 위임받으려는 애플리케이션
Authorization Server 사용자를 인증하고 토큰을 발급하는 서버
Resource Server 보호된 API를 제공하는 서버

OAuth2와 로그인

OAuth2는 인가 프레임워크다.

하지만 실제 서비스에서는 OAuth2를 이용해 소셜 로그인을 구현하는 경우가 많다.
이때 주의할 점은 OAuth2 자체는 “이 사용자가 누구인지”를 표준화해서 알려주는 인증 프로토콜이 아니라는 점이다.

사용자 인증까지 명확히 다루려면 OpenID Connect(OIDC)를 함께 봐야 한다.

정리하면 다음과 같다.

  • OAuth2: 권한 위임과 Access Token 발급
  • OIDC: OAuth2 위에 사용자 인증 정보를 표준화한 계층

카카오나 네이버 로그인 구현을 할 때도 단순히 Access Token을 받는 것에서 끝나지 않고, 사용자 정보 API를 호출해 우리 서비스의 회원과 연결하는 흐름이 필요하다.

OAuth 2.1이 나오려는 이유

OAuth 2.0은 오래 사용되면서 다양한 보안 권장사항과 확장 RFC가 생겼다.

문제는 OAuth 2.0 본문만 보면 지금은 권장되지 않는 방식도 포함되어 있다는 점이다.
예를 들어 Implicit Grant나 Resource Owner Password Credentials Grant는 과거에는 사용되었지만, 현재는 보안상 권장되지 않는다.

OAuth 2.1은 OAuth 2.0을 완전히 새로 만드는 것이라기보다, 그동안의 보안 권장사항을 반영해 안전한 기본값으로 정리하려는 흐름에 가깝다.

변화 1. Authorization Code + PKCE 중심

OAuth 2.1에서 가장 중요한 흐름은 Authorization Code Grant에 PKCE를 함께 사용하는 것이다.

PKCE는 Proof Key for Code Exchange의 약자다.

기존 Authorization Code 방식에서는 인가 코드가 탈취되면 공격자가 토큰으로 교환할 수 있는 위험이 있었다.
PKCE는 이 문제를 줄이기 위해 Client가 code_verifiercode_challenge를 사용한다.

흐름을 단순화하면 다음과 같다.

  1. Client가 임의의 code_verifier를 만든다.
  2. 이를 변환해 code_challenge를 만든다.
  3. Authorization Request에 code_challenge를 포함한다.
  4. Authorization Server는 인가 코드를 발급한다.
  5. Client는 Token Request에서 원래 code_verifier를 보낸다.
  6. Authorization Server는 두 값이 맞는지 검증한 뒤 토큰을 발급한다.

이렇게 하면 인가 코드만 훔쳐서는 토큰을 발급받기 어렵다.

변화 2. Implicit Grant 제거 방향

OAuth 2.0에서는 브라우저 기반 애플리케이션을 위해 Implicit Grant가 사용되었다.

Implicit Grant는 Access Token이 브라우저 리다이렉트 URL을 통해 바로 전달되는 방식이다.

문제는 Access Token이 브라우저 환경에 노출되기 쉽다는 점이다.
URL fragment나 브라우저 기록, 스크립트 환경 등 여러 지점에서 토큰이 새어 나갈 가능성이 있다.

현재는 브라우저 기반 애플리케이션에서도 Authorization Code + PKCE 방식이 권장된다.

OAuth 2.1은 이런 흐름을 반영해 Implicit Grant를 제거하는 방향으로 정리된다.

변화 3. Password Grant 제거 방향

Resource Owner Password Credentials Grant는 사용자의 ID와 비밀번호를 Client가 직접 받아 Authorization Server에 전달하는 방식이다.

이 방식은 OAuth가 해결하려던 문제와 충돌한다.

OAuth의 목적은 사용자의 비밀번호를 제3자 애플리케이션에 주지 않고 권한을 위임하는 것이다.
그런데 Password Grant는 Client가 사용자 비밀번호를 직접 다룬다.

그래서 현재는 이 방식도 권장되지 않는다.

OAuth 2.1에서는 더 안전한 흐름을 기본으로 삼기 위해 Password Grant를 제거하는 방향을 가진다.

변화 4. Redirect URI 검증 강화

OAuth 흐름에서 Redirect URI는 매우 중요하다.

Authorization Server는 인가 코드를 Client가 등록한 Redirect URI로 돌려보낸다.
만약 Redirect URI 검증이 느슨하면 공격자가 인가 코드를 자신의 서버로 받도록 유도할 수 있다.

그래서 정확한 Redirect URI 등록과 검증이 중요하다.

실제 소셜 로그인 구현에서도 Redirect URI가 조금만 달라도 오류가 발생한다.

1
2
http://localhost:8080/login/oauth2/code/kakao
https://api.example.com/login/oauth2/code/kakao

로컬과 운영 환경의 Redirect URI를 구분해서 관리해야 하는 이유도 여기에 있다.

변화 5. Bearer Token 보안 고려

OAuth에서 많이 사용하는 Access Token은 Bearer Token 형태다.

Bearer Token은 이름 그대로 “가진 사람이 사용할 수 있는” 토큰이다.
즉, 토큰을 탈취당하면 공격자가 그 토큰으로 API를 호출할 수 있다.

그래서 다음이 중요하다.

  • HTTPS 사용
  • 토큰 저장 위치 주의
  • Access Token 만료 시간 짧게 설정
  • Refresh Token 보호
  • 필요 시 sender-constrained token 같은 강화된 방식 고려

JWT를 공부하면서도 느꼈지만, 토큰 기반 인증은 토큰을 발급하는 것보다 토큰이 새어 나가지 않게 관리하는 것이 더 중요하다.

Spring Security와 연결해서 생각하기

Spring Security에서 OAuth2 Login을 사용하면 많은 흐름을 프레임워크가 처리해준다.

하지만 내부 흐름을 모르면 문제가 생겼을 때 원인을 찾기 어렵다.

예를 들어 다음 지점들을 이해해야 한다.

  • Authorization Request가 어디로 리다이렉트되는지
  • Redirect URI는 어떻게 구성되는지
  • Authorization Code는 어디서 처리되는지
  • Access Token은 어디서 발급받는지
  • 사용자 정보는 어떤 API에서 가져오는지
  • 우리 서비스의 User와 어떻게 매핑하는지

OAuth2 Login은 편리하지만, 결국 내부에는 OAuth의 Authorization Code 흐름이 있다.

OAuth 2.1을 공부하며 느낀 점

OAuth 2.1은 새로운 기술이라기보다, OAuth 2.0을 안전하게 쓰기 위한 정리본처럼 느껴졌다.

과거에는 여러 방식이 선택지로 열려 있었다면, 지금은 실제 운영에서 안전한 방식이 무엇인지 점점 명확해지고 있다.

내가 이해한 핵심은 다음이다.

  • Authorization Code + PKCE를 기본으로 생각하자.
  • Implicit Grant는 사용하지 않는 방향으로 가자.
  • Password Grant는 OAuth의 목적과 맞지 않다.
  • Redirect URI 검증은 매우 중요하다.
  • Bearer Token은 탈취되면 바로 위험하므로 저장과 전달을 조심해야 한다.
  • OAuth2와 OIDC의 차이를 구분하자.

참고 자료

마무리

OAuth 2.1을 공부하면서 인증/인가 분야는 한 번 구현하고 끝나는 것이 아니라 계속 업데이트되는 영역이라는 생각이 들었다.

Spring Security에서 OAuth2 Login을 쓰면 많은 부분을 쉽게 구현할 수 있지만, 그 뒤에 있는 OAuth 흐름을 이해해야 더 안전하게 사용할 수 있다.

앞으로 OAuth 로그인 기능을 구현할 때는 단순히 “카카오 로그인 성공”에서 멈추지 않고, Authorization Code, PKCE, Redirect URI, Token 저장 방식까지 함께 확인해야겠다.

Categories:

Updated:

Leave a comment