Post

OAuth2 Authorization Code Grant 흐름은 왜 그렇게 동작하는가

들어가며


직접 사용자 서버를 구축해 보며 oauth2 authorization code grant 흐름이 왜 그런가에 대해 생각해 볼 수 있었던 실화를 바탕으로 포스팅을 하고자 한다.

소셜 로그인 기능 도입은 개발자에게 사용자의 인증과 인가 기능을 구현하는 데 있어서 큰 편리함을 가져다준다. 별도의 데이터베이스 구성 없이 소셜 로그인 API 를 통해서 어플리케이션을 개발할 수 있다는 특징도 가지고 있다.하지만 어플리케이션 요구사항에 맞게 소셜 로그인에서 제공하지 않는 사용자 정보를 관리하고 싶을 수도 있다.이경우 사용자 데이테베이스를 설계하고 정보를 저장해야 한다. 사용자 입장에서 소셜로그인은 편리한 경험을 제공하지만 개발자가 별도의 데이터베이스를 구축해야 한다면 소셜 로그인을 사용한다고 해도 애플리케이션마다 사용자 데이터베이스를 관리를 해줘야 한다는 아쉬움이 남아있다.

예를 들어 소셜 로그인 기능을 사용하면서
“ 내 서비스에서는 사용자 프로필 사진과 블로그 URL 정보를 추가로 관리하고 싶다 “
했을 때 각 서비스마다 소셜 로그인 기능과 데이터베이스를 매번 구성해줘야 한다는 불편함이 있다.
이러한 문제를 해결하기 위해 필자는 소셜 로그인 기능을 이용하며 impati 시스템의 인증과 인가를 담당하는 사용자 서버를 별도로 구축하고자 했다.

impati 사용자 서버 구축


사용자 서버의 핵심 요구 사항은 다음과 같다.

  • 여러 소셜 로그인 기능을 사용할 수 있어야 한다.
  • 소셜 로그인 기능을 사용하면서 사용자 정보를 데이터베이스에서 관리한다.
  • 여러 클라이언트 서버에서 이용할 수 있어야 한다.

여러 소셜 로그인 기능을 사용할 수 있어야 한다.

여러 소셜 로그인 기능을 구현하는 방법은 어렵지 않다.

google , naver , kakao 소셜 로그인 기능을 이용하고자 했을 때 모두 OAuth2 authorization code grant 흐름으로 동작하므로 서로 다른 소셜 로그인에 대해 공통 처리를 수행할 수 있다.

소셜 로그인 기능을 제공하는 서버를 Provider 라고 정의하겠다.

  1. 각 Provider 별로 요구되는 redirect-uri , client-id , client-secret 등등 설정을 수행해 준다.
  2. 소셜 로그인을 위해서 사용자 로그인 요청을 Provider 가 정의한 약속된 앤드포인트로 리다이렉트를 수행해 준다.
  3. 사용자는 리다이렉트 된 Provider 별 로그인 페이지에서 로그인을 수행하고 성공 시 Provider 서버는 사전에 정의한redirect-uri 을 통해 사용자 서버인가 코드를 보내게 된다.
  4. 사용자 서버는 Provider로부터 받은 인가 코드를 Provider 서버에게 전달하여 인가 코드에 대한 검증과 엑세스 토큰을 발급받는다

엑세스 토큰을 발급받은 이후에는 Provider 에게 엑세스 토큰을 전달하여 사용자 정보를 가져와야 한다. 하지만 Provider 마다 사용자 정보를 가져올 수 있는 방법은 다르다. 예를 들어 구글 같은 경우 사용자 email 을 가져오기 위해 email 를 , 카카오는 kakao_account.email naver 는 response.email 으로 가져와야 한다. 이러한 문제는 공통으로 가져올 정보를 추상화하고 Provider 마다 어떻게 가져와야 할지는 하위 타입에서 구현하도록 한 뒤에 Factory 객체를 이용해서 사용자 정보를 가져오면 공통 처리를 수행할 수 있다.

1
ProviderCustomer providerCustomer = ProviderCustomerFactory.create(providerType ,사용자 정보);

소셜 로그인 기능을 사용하면서 사용자 정보를 데이터베이스에서 관리한다.

사용자 정보를 가져온 이후에는 사용자 정보를 이용하여 데이터베이스에 데이터를 조회하거나 새롭게 저장하거나 이미 관리되고 있는 유저라고 한다면 적절한 액션을 수행해 주면 된다.

여러 클라이언트 서버에서 이용할 수 있어야 한다.

사용자 정보를 가져오고 데이터베이스에 사용자 정보를 저장하는 것까지 완료할 수 있었다. 여러 클라이언트 서버에서 사용자 서버를 이용할 수 있으려면 크게 두 가지 문제를 해결해야 한다.
클라이언트 식별 문제와 인증된 요청 문제이다.

클라이언트 식별 문제


클라이언트 서버가 하나라면 어디로 이 요청을 리다이렉트 할 것인지는 분명하지만 클라이언트 서버가 여러 개인 경우를 다루기 때문에 클라이언트를 식별해줘야 한다. 즉 클라이언트 서버를 식별하고 올바른 리다이렉트를 수행해주어야 한다.

클라이언트 식별 문제 해결하기


클라이언트를 식별하는 문제를 해결하는 방법은 의외로 간단하다. 사용자 서버를 이용하기 전에 클라이언트 정보와 리다이렉트 정보를 등록하도록 하고 client-id 를 발급해 주어 요청마다 client-id 를 전달하도록 하는 것이다. 사용자 서버는 client-id 를 확인하고 리다이렉트 정보를 가져와 해당 요청을 리다이렉트 해주는 방법으로 클라이언트 식별 문제를 해결할 수 있다.

인증된 요청 문제


클라이언트 서버에서 사용자 서버 데이터를 안전하게 이용하기 위해서는 어떻게 해야 할까? 인증된 클라이언트 서버의 요청인지 그렇지 않은지 구별하기 위해서는 사용자 서버만의 엑세스 토큰을 발급하고 이를 클라이언트 서버에게 전달한 뒤 클라이언트 서버가 유효한 엑세스 토큰을 주었을 때 사용자 서버의 데이터베이스에서 사용자 정보를 전달해 주는 방법으로 해결할 수 있다 하지만 이런 방법은 한 가지 더 큰 문제가 있는데 바로 엑세스 토큰이 라다이렉트 시 URL 에 노출이 된다는 점이다.이는 https 를 사용하더라도 엑세스 토큰이 해킹될 수 있음을 의미한다.

인증된 요청 문제 해결하기


엑세스 토큰이 라다이렉트시 URL 에 노출이 되는 문제를 해결하기 위해 라다이렉트시 아주 짧은 만료 시간을 가진 코드를 발급하고 전달한다. 그리고 리다이렉트 시 전달한 코드로 엑세스 토큰을 발급할 수 있도록 제어한다면 엑세스 토큰이 라다이렉트시 URL 에 노출이 되는 문제를 해결할 수 있다.

마치며


소셜 로그인 기능도 이용해보고 싶고 별도의 데이터베이스로 사용자 정보를 관리하고 싶고 매 어플리케이션 서비스마다 같은 작업을 반복하기 싫어서 사용자 서버 구축을 결심했었다. 사용자 서버를 구축하면 지겨운 소셜 로그인 설정 작업을 반복하지 않아도 되고 사용자 서버에서 제공하는 간단한 절차와 API 를 지키면서 소셜 로그인의 인증과 인가 기능을 모두 누릴 수 있을 것이라고 생각했다.
실제로 여러 소셜 로그인을 제공하기 위한 코드와 설정은 사용자 서버를 구축하면서 훨씬 간단해졌다. 하지만 간소화하고자 했던 redirect-uri 나 code 를 받아오는 방법은 위에서 설명한 이유들로 인해 간소화할 수 없었다.
클라이언트 식별 문제와 인증 요청 문제의 해결 방법이 authorization code grant 방식과 유사해 보이지 않는가? 필자는 이것이 authorization code grant 방식이 왜 그렇게 동작하는지에 대한 답이 라고 생각한다.

This post is licensed under CC BY 4.0 by the author.