[문제 상황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
문제 상황을 정리를 해보자면 이렇다.
- 기존의 레거시 코드에서는 소셜로그인하고, 회원가입이 완료되는 시점에 관련 정보를 다 지워버렸다.
- 그래서 allauth 에서는 소셜로그인 할때 마다 매번 새로운 회원으로 여긴다.
- 다시 로그인을 진행할 때, 회원 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 |