동현 유
척척석사
동현 유
전체 방문자
오늘
어제
  • 분류 전체보기 (181) N
    • BlockChain (48)
      • [paper] Consensus (13)
      • [paper] Execution (19)
      • [paper] Storage (5)
      • [paper] ZKP (1)
      • [paper] Oracle (1)
      • Blockchains (9)
    • Java (19)
      • Java의 정석 (13)
      • Java 파헤치기 (5)
    • Python (20)
      • Python 뜯어보기 (6)
      • 데이터 분석 기초 (5)
      • Python 기초 강의 (6)
      • Python 기초 강의 부록 (3)
    • Golang (0)
    • MySQL (3)
      • programmers (2)
      • 기본 문법 (0)
    • 웹 프로젝트 (IBAS) (36)
      • Django 레거시 (14)
      • SpringBoot api 개편 (14)
      • Infra (3)
      • 서버 장애 기록 (4)
      • 신입팀원 교육 자료 (1)
    • CS (30)
      • Operating System (22)
      • Computer Security (3)
      • Network (4)
      • DBMS (1)
    • 책 (10)
      • 도메인 주도 설계 철저 입문 (9)
      • Real MySQL 8.0 (1)
    • BOJ 문제 풀이 (3)
    • 이러쿵저러쿵 (10) N
    • 회고 (1)

인기 글

최근 댓글

최근 글

hELLO · Designed By 정상우.
동현 유

척척석사

웹 프로젝트 (IBAS)/Django 레거시

[Django 웹 프로젝트] 9. 소셜 로그인 관련 오류 수정 (2022-03-13)

2022. 3. 13. 03:57

 [문제 상황1] (선조치 후분석)

  : 회원 A가 소셜로그인을 통해 회원가입을 진행했는데, 이메일만 None 으로 받아졌던 상황.(재현 실패)

입부 완료 메일을 보내지 못해서 서버 오류 로그가 나고 있었다.

 

(대처)

  : 일단 빠르게 조치하기 위해 해당 회원의 이메일 값을 받아서 db 에 직접 넣어줬다.

 

(분석)

  : 소셜로그인을 통해 받은 이메일 정보는 회원가입 중에 임의 수정할 수 없도록 설정해두었다. 그래서 회원 정보를 저장하는 중에 오류가 난 것으로는 보이지 않는다. 정보동의를 하지 않거나 이메일이 넘어오지 않으면 아예 회원가입을 할 수 없도록 해놨었기 때문에, 이메일이 없이 회원가입 페이지로 넘어가는 것도 이해가 잘 되지 않았다.

  - https://developers.naver.com/forum/posts/28349 에서 확인한 결과, 회원 A가 이메일 공개를 거부했다거나 또는 공개용 이메일을 제대로 설정하지 않았을 가능성이 있었다. => 연락해서 확인 중이다.

  - allauth 패키지 코드를 확인했는데 내부에서 signal() 을 사용해서 일종의 이벤트를 발생시키는 것 같았다.(signal 은 자세히 보지 않아서 잘 모르겠다.) 로컬에서 테스트해보니 소셜로그인 시에 대기시간이 길어지거나 통신 혼잡 등이 벌어졌을 때, 페이지가 그대로 멈췄다. 소셜로그인 실패 엔드포인트까지 도달하지 못하는 셈이다. 이런 상황에서 연계되는 오류일 수 있다는 생각이 잠깐 들었지만, 절대 그럴 수 없다고 생각이 바뀌었닼ㅋ 왜냐면 다시 소셜로그인을 시도해서 성공했을 때 받아오는 유저의 정보로 db 를 최신화하기 때문이다...

 

 


 

[문제 상황2] (선분석 후조치)

  : 위와 동일한 회원 A가 [문제상황1]의 대한 조치가 끝난 후, 로그인이 되지 않는다는 불편을 접수. 서버 로그를 확인해보니 소셜로그인 후에 allauth 가 제공하는 /accounts/social/signup 페이지로 리다이렉트 되는 현상이 발생했다. (재현 가능)

(- 회원 A에게만 일어났던 문제.)

 

(분석)

  • 소셜로그인이 잘 끝나고 allauth 에서 해당 정보를 잘 처리했으면, 직접 정의한 콜백 url 로 리다이렉트 되어야한다.
  • 콜백 url 로 리다이렉트 되지 않고, 그 전에 allauth 단에서 /accounts/social/signup 로 리다이렉트 시켰다.
  • 확인해보니 소셜로그인은 정상적으로 잘 진행되었다.

위의 사항을 파악하고 allauth 를 디버깅해보기 시작했다.

signup 페이지로 리다이렉트 시키는 코드를 찾고, 그 조건이 무엇인지 파악하기로 했다.

 

소셜로그인이 끝난 후에 호출 순서는 아래와 같다.

"""
1. oauth2.views.OAuth2CallbackView.dispatch 호출
2. helpers.complete_social_login 호출
3. helpers._complete_social_login 호출
"""

def _complete_social_login(request, sociallogin):
    if request.user.is_authenticated:
        get_account_adapter(request).logout(request)
    if sociallogin.is_existing:
        # Login existing user
        ret = _login_social_account(request, sociallogin)
        signals.social_account_updated.send(
            sender=SocialLogin, request=request, sociallogin=sociallogin
        )
    else:
        # New social user
        ret = _process_signup(request, sociallogin)
    return ret

기존에 선배님께서 작성했던 코드를 보니, 소셜로그인이 끝나면 관련 모든 정보를 db에서 지우고 있었다.

그래서 매번 소셜로그인을 진행할 때마다 else 절인 회원가입절차 부분이 실행됐다.

 

회원가입 절차부분인 _process_signup 코드는 아래와 같다.

"""
1. oauth2.views.OAuth2CallbackView.dispatch 호출
2. helpers.complete_social_login 호출
3. helpers._complete_social_login 호출
4. helpers._process_signup 호출
"""


def _process_signup(request, sociallogin):
    auto_signup = get_adapter(request).is_auto_signup_allowed(request, sociallogin) # False 였던 부분
    if not auto_signup:
        request.session["socialaccount_sociallogin"] = sociallogin.serialize()
        url = reverse("socialaccount_signup")
        ret = HttpResponseRedirect(url)
    else:
        (...생략...)
        ret = complete_social_signup(request, sociallogin)
    return ret

auto_signup 값이 True 이면 complete_social_signup을 호출하는데,

해당 소셜 계정 정보를 자동으로 db 에 저장하고 로그인 성공시킨다.

 

문제는 auto_signup 이 계속 False 였다는 점이다! (잡았다 요놈!)

is_auto_signup_allowed() 를 확인해본 결과,

db 에 해당 이메일을 갖고 있는 유저가 이미 존재해서 False 가 반환되었던 것이다.

 

아래의 email_address_exist(email) 부분이다.

(인증 유저 전용 테이블인 auth_user 와 소셜이메일 테이블인 account_emailaddress 두개의 테이블을 본다.)

"""
1. oauth2.views.OAuth2CallbackView.dispatch 호출
2. helpers.complete_social_login 호출
3. helpers._complete_social_login 호출
4. helpers._process_signup 호출
5. adapter.DefaultSocialAccountAdapter.is_auto_signup_allowed 호출
"""

def is_auto_signup_allowed(self, request, sociallogin):
    # If email is specified, check for duplicate and if so, no auto signup.
    auto_signup = app_settings.AUTO_SIGNUP
    if auto_signup:
        email = user_email(sociallogin.user)
        # Let's check if auto_signup is really possible...
        if email:
            if account_settings.UNIQUE_EMAIL:
                if email_address_exists(email): # <-------------------- 여기가 계속 True 였다!
                    # Oops, another user already has this address.
                    # We cannot simply connect this social account
                    # to the existing user. Reason is that the
                    # email adress may not be verified, meaning,
                    # the user may be a hacker that has added your
                    # email address to their account in the hope
                    # that you fall in their trap.  We cannot
                    # check on 'email_address.verified' either,
                    # because 'email_address' is not guaranteed to
                    # be verified.
                    auto_signup = False
                    # FIXME: We redirect to signup form -- user will
                    # see email address conflict only after posting
                    # whereas we detected it here already.
        elif app_settings.EMAIL_REQUIRED:
            # Nope, email is required and we don't have it yet...
            auto_signup = False
    return auto_signup

 

문제 상황을 정리를 해보자면 이렇다.

  1. 기존의 레거시 코드에서는 소셜로그인하고, 회원가입이 완료되는 시점에 관련 정보를 다 지워버렸다.
  2. 그래서 allauth 에서는 소셜로그인 할때 마다 매번 새로운 회원으로 여긴다.
  3. 다시 로그인을 진행할 때, 회원 A의 이메일이 auth_user 테이블 또는 account_emailaddress 테이블에 존재했다. 따라서auto_signup = False 가 되어, allauth 가 제공하는 /accounts/social/signup 페이지로 리다이렉트 된다.

회원가입할 때, 정상적으로 완료되지 않아서, 해당 계정 정보가 다 지워지지 않은 것으로 파악된다.

(구체적으로 어떤 상황인지는 추정 불가하다.)

 

(조치)

  아까 봤던 코드를 다시 보자.

"""
1. oauth2.views.OAuth2CallbackView.dispatch 호출
2. helpers.complete_social_login 호출
3. helpers._complete_social_login 호출
"""

def _complete_social_login(request, sociallogin):
    if request.user.is_authenticated:
        get_account_adapter(request).logout(request)
    if sociallogin.is_existing:  # <------ 소셜계정정보가 남아있을 때
        # Login existing user
        ret = _login_social_account(request, sociallogin)
        signals.social_account_updated.send(
            sender=SocialLogin, request=request, sociallogin=sociallogin
        )
    else:
        # New social user
        ret = _process_signup(request, sociallogin)
    return ret

기존 코드에서 소셜계정정보를 지우기 때문에

무조건 회원가입 절차를 밟는 코드를 실행시키고 -> auto_signup 을 이용해 로그인을 통과시키는 로직이었다.

 

그런데, 소셜계정정보가 남아있다면,

_login_social_account(request, sociallogin) 를 실행시키는데

해당 함수 내부에는 회원가입 페이지로 리다이렉트할만한 코드가 없다.

내부에서 바로 로그인 처리하고, 우리가 직접 설정한 콜백 url 로 리다이렉트 된다.

 

따라서 기존 소셜계정 정보를 지우는 코드를 삭제하면 된다.

 

아래는 해당 커밋 내역이다.

 

[bugfix] 소셜 로그인 이후에, django allauth signUp page 로 가는 것 방 · InhaBas/Inhabas.com@82adfba

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files [bugfix] 소셜 로그인 이후에, django allauth signUp page 로 가는 것 방 Loading branch information Showing 1

github.com

 

'웹 프로젝트 (IBAS) > Django 레거시' 카테고리의 다른 글

[Django 웹프로젝트] 10. 호환성을 고려한 소셜로그인 버그 수정 (2022-03-14~15)  (0) 2022.03.16
[Django 웹 프로젝트] 트래픽 및 방문자 수 (2022.02.11 ~ 2022.03.12)  (2) 2022.03.13
[Django 웹 프로젝트] 8. 점진적으로 api 로 교체 가능? (2021-11-21)  (0) 2022.03.13
[Django 웹 프로젝트] 7. 유지 보수를 위한 새로운 인가인증 체계 고민 (2021-10-31)  (0) 2022.03.01
[Django 웹 프로젝트] 6. 유지 보수를 위한 새로운 아키텍처 고민 (2021-10-21)  (0) 2022.03.01
    동현 유
    동현 유
    Fault Tolerant System Researcher for more Trustful World and Better Lives. (LinkedIn: https://www.linkedin.com/in/donghyeon-ryu-526b8a276/)

    티스토리툴바