안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

장고 (Django)

[Django] django-allauth 소셜 로그인 구현 원리 (OAuth 2.0 기반)

피그브라더 2021. 4. 8. 09:39

Django의 라이브러리 중 하나인 django-allauth는 여러 종류의 소셜 로그인을 구현해두었다. 카카오 로그인, 페이스북 로그인, 구글 로그인, 네이버 로그인 등 아주 많은 종류의 소셜 서비스와 편리하게 연동이 가능하도록 기능을 제공하고 있는 것이다. 이번 포스팅에서는 해당 라이브러리가 그러한 소셜 로그인을 어떠한 원리로 구현하고 있는지, 카카오 로그인을 예시로 한 번 살펴볼 것이다. 카카오 로그인을 예시로 설명하지만 대부분의 소셜 로그인은 그 구현 원리가 비슷하다는 점을 기억하기 바란다.

 

※ django-allauth의 소셜 로그인 구현 원리를 살펴보기에 앞서, 그 구현 원리의 바탕이라고 할 수 있는 OAuth 2.0의 개념을 완벽히 이해하는 것이 먼저이다. 해당 개념에 대한 설명은 이 포스팅에서 다루고 있으니 참고 바란다. 여기서는 해당 개념에 대해 알고 있다는 것을 전제로 설명을 진행하겠다.

 

1. django-allauth 카카오 로그인 구현 원리 (OAuth 2.0 기반)

먼저, Client가 Resource Owner의 권한 제공 동의에 의해 발급된 인증 코드를 받으며 Redirect URL로 리다이렉트 될 때까지의 과정은 다음과 같다. 지난 OAuth 2.0 포스팅에서 다뤘던 것과 동일하다.

 

  1. Resource Owner가 [카카오 로그인] 버튼을 클릭하여 카카오 계정 로그인 페이지로 이동한다. 이 버튼의 링크는 Resource Server의 주소이며, GET 파라미터로 Client ID 값, 필요한 권한들, Redirect URL 등이 전달된다.
  2. 카카오 계정 로그인 페이지에서 이메일과 비밀번호를 입력하여 로그인한다. 그런데 만약 카카오 계정 로그인을 한 적이 있어서 카카오 계정 세션의 ID가 브라우저에 남아 있다면 이 과정이 생략되고 자동으로 로그인된다.
  3. Resource Server는 GET 파라미터를 통해 전달받은 Client ID 값, 필요한 권한들, Redirect URL 등을 내부에 저장하고 있는 데이터와 비교하여 올바른 요청인지 판단한다.
  4. 그리고 Resource Owner로부터 Client에서 필요한 권한들의 제공에 대한 동의를 구한다. 예를 들어, Resource Owner 카카오 계정의 프로필 정보를 Client가 참조할 수 있도록 할 것인지 물어보게 된다.
  5. Resource Owner가 해당 권한들의 제공에 동의를 하면, Resource Server는 내부에 해당 카카오 계정과 해당 Client의 연결 정보를 저장한다(= 연결 형성). 여기에는 해당 계정이 어떠한 권한들의 제공에 동의를 했는지가 저장된다. 그리고 이곳에 임시 비밀번호 역할을 하는 인증 코드를 발급하여 저장해 둔다. - ⓑ
  6. Resource Server는 Location 헤더에 Redirect URL을 넣어서 리다이렉트 응답을 전달한다. 이때 해당 URL의 GET 파라미터에 방금 발급한 인증 코드를 심어둔다.
  7. Resource Owner는 리다이렉트에 의해 Client의 주소로 이동하면서 발급받은 인증 코드를 Client에게 전달한다.

 

그러면 이제 Client는 해당 인증 코드를 가지고 Redirect URL에 해당하는 뷰를 호출함으로써 다음과 같은 과정을 진행하게 된다. 그 뷰가 바로 django-allauth의 OAuth2CallbackView이다. 조금 더 정확히는 OAuth2CallbackView의 dispatch() 메소드를 호출하는 것을 말한다. 지금부터가 중요하다. django-allauth가 어떠한 원리로 소셜 로그인을 구현하는지 본격적으로 알아보자.

 

  1. 인증 코드를 이용하여 액세스 토큰을 요청하는 API를 호출한다. - ⓐ
  2. 받아온 액세스 토큰을 이용하여 SocialToken 인스턴스를 생성한다.
  3. SocialLogin(데이터베이스에 저장되는 모델이 아닌, 일반적인 Python 클래스) 인스턴스를 생성한다.
    1. 액세스 토큰을 이용하여 해당 소셜 계정의 정보를 조회하는 API를 호출한다.
    2. 받아온 소셜 계정의 정보를 가지고 SocialAccount 인스턴스를 생성한다.
    3. SocialLogin 인스턴스에 SocialAccount 인스턴스를 연결한다.
    4. 비어 있는 User 인스턴스를 생성한다.
    5. SocialLogin 인스턴스에 User 인스턴스를 연결한다.
    6. DefaultSocialAccountAdapter.populate_user() 메소드를 호출하여 해당 User 인스턴스의 필드 값들을 채워준다. 이때 인자로 SocialLogin 인스턴스를 넘겨주기 때문에 아까 액세스 토큰으로 받아온 소셜 계정의 정보를 참조하는 것이 가능하다.
  4. SocialLogin 인스턴스에 SocialToken 인스턴스를 연결한다.
  5. complete_social_login() 함수를 호출한다. 이때 인자로 SocialLogin 인스턴스를 넘겨준다.
    1. SocialLogin 인스턴스의 lookup() 메소드를 호출한다.
      1. 소셜 서비스와 소셜 계정의 ID가 동일한 SocialAccount 인스턴스가 데이터베이스에 이미 존재하는지 검사한다. 존재하지 않는다면 바로 리턴하고, 그렇지 않다면 다음 과정을 진행한다.
      2. 아까 생성했던 SocialAccount 인스턴스의 정보를 기존의 SocialAccount 인스턴스에 갱신해준다.
      3. SocialLogin 인스턴스에 기존의 SocialAccount 인스턴스를 연결한다(= 연결 교체).
      4. SocialLogin 인스턴스에 기존의 SocialAccount 인스턴스가 가리키는 User 인스턴스를 연결한다(= 연결 교체).
      5. 동일한 소셜 서비스와 소셜 계정을 가리키는 SocialToken 인스턴스가 데이터베이스에 이미 존재하는지 검사한다.
      6. 존재하지 않는다면 SocialLogin 인스턴스에 연결되어 있는 SocialToken 인스턴스에 아까 찾은 SocialAccount 인스턴스를 연결해주고, 해당 SocialToken 인스턴스를 데이터베이스에 저장한다.
      7. 존재한다면 아까 생성했던 SocialToken 인스턴스의 정보를 기존의 SocialToken 인스턴스에 갱신해준다.
      8. SocialLogin 인스턴스에 기존의 SocialToken 인스턴스를 연결한다(= 연결 교체).
    2. DefaultSocialAccountAdapter.pre_social_login() 메소드를 호출한다. 이때 인자로 SocialLogin 인스턴스를 넘겨준다. (단 이는 아무런 구현부가 없는 빈 메소드로, 개발자가 오버라이딩을 통해 추가 기능 구현이 가능하다.)
    3. _complete_social_login() 함수를 호출한다. 이때 인자로 SocialLogin 인스턴스를 넘겨준다.
      1. SocialLogin 인스턴스에 연결된 SocialAccount 인스턴스가 데이터베이스에 이미 존재한다면 로그인 과정으로, 그렇지 않다면 회원가입 과정으로 인식한다.
      2. 로그인 과정이라면 User 인스턴스도 이미 데이터베이스에 존재한다는 의미이므로 일반적인 로그인 과정과 마찬가지의 과정을 밟는다. 세션을 만들고 세션 ID를 브라우저에게 넘겨주는 것이 대표적인 예시이다.
      3. 회원가입 과정이라면 DefaultSocialAccountAdapter.save_user() 메소드를 호출하여 자동 회원가입을 진행한다. 이때 인자로 SocialLogin 인스턴스를 넘겨준다. 여기서 SocialLogin 인스턴스의 save() 메소드를 호출하게 된다. 그러면 다음과 같은 과정으로 각 인스턴스가 데이터베이스에 저장이 된다. 이후 동일한 로그인 과정을 밟는다.
        1. SocialLogin 인스턴스에 연결된 User 인스턴스를 데이터베이스에 저장한다.
        2. SocialLogin 인스턴스에 연결된 SocialAccount 인스턴스를 데이터베이스에 저장한다.
        3. SocialLogin 인스턴스에 연결된 SocialToken 인스턴스에 방금 전에 저장한 SocialAccount 인스턴스를 연결하고, 해당 SocialToken 인스턴스를 데이터베이스에 저장한다.

 

2. 카카오 로그인 관련 용어 정리 (카카오 Developers 문서)

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

마지막으로, 카카오 Developers 사이트에서 공식적으로 사용하는 몇몇 헷갈리는 용어들을 정리해보겠다. 위에서 설명한 과정을 이해하고, 다음의 용어 설명까지 이해하고 나면 카카오 Developers 사이트에서 설명하는 카카오 로그인의 개념을 정확히 이해할 수 있게 된다.

 

  • 카카오 로그인 : 위의 과정을 의미한다. 즉, 액세스 토큰(과 리프레시 토큰)의 발급을 요청하는 것이다.
  • 카카오 로그아웃 : 액세스 토큰(과 리프레시 토큰)을 만료시키는 것이다.
  • 연결 : 위의 과정을 의미한다. 즉, 카카오 서버에 해당 서비스를 위한 연결 정보를 생성하는 것이다.
  • 연결 끊기 : 카카오 서버에 존재하는 연결 정보를 삭제하는 것이다. 곧 탈퇴를 의미한다고 볼 수 있다. 연결 끊기는 API 요청으로 할 수도 있고, 사용자가 직접 카카오톡 앱에 들어가서 할 수도 있다. 연결 끊기를 하면 당연히 해당 연결 정보에 대해 발급되어 있던 액세스 토큰(과 리프레시 토큰)도 삭제되므로 카카오 로그아웃도 함께 진행된다. 연결 끊기 후 다시 [카카오 로그인] 버튼으로 로그인을 시도하면 권한 제공 동의 화면이 다시 뜰 것이다. 이때 동의를 하면 사실상 새로운 연결 정보를 생성하는 셈이 된다.