Lina's Toolbox

[Python/Django] ValueError: You have multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user. 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/troubleshooting

[Python/Django] ValueError: You have multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user.

Woolina 2024. 10. 11. 01:23

 

Discord OAuth2 인증 구현 중..
자꾸 날 괴롭혔던 에러.

Redirects를 discordlogin/으로 연결했는데,

discordlogin은 URL 패턴인데 자꾸 discordlogin이라는 모듈이 없다고해서 헷갈렸다.

그래서 내가 어딘가에서 discordlogin을 모듈로 잘못 사용하고 있는 것 같아

내 코드에서 discordlogin을 검색해봤고

 

settings.py

AUTHENTICATION_BACKENDS = (
    'users.auth.DiscordAuthenticationBackend',  # discord 백엔드
    'django.contrib.auth.backends.ModelBackend',  # 기본 백엔드
    'allauth.account.auth_backends.AuthenticationBackend',  # Allauth 백엔드
)

AUTHENTICATION_BACKENDS = (
    'discordlogin.auth.DiscordAuthenticationBackend',
)

내가 실수로 이렇게 백엔드를 두개를 적었던 것이였다.

discordlogin이라는 앱은 있지도 않은데 실수로 저렇게 적어서 자꾸

discordlogin 모듈이 없다고 찾았던 것이다.

 

나는 users모듈(앱)에 auth.py에 정리했기 때문에 저 부분은 지워줘야 했다.

AUTHENTICATION_BACKENDS = (
    'users.auth.DiscordAuthenticationBackend',  # discord 백엔드
    'django.contrib.auth.backends.ModelBackend',  # 기본 백엔드
    'allauth.account.auth_backends.AuthenticationBackend',  # Allauth 백엔드
)

 

여기서 순서도 중요하다! 위에서부터 차례대로 인증을 시도하기 때문에..

디스코드를 마지막에 적었을 경우

 

AttributeError: 'User' object has no attribute 'backend'

ValueError: You have multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user.

 

이런 500번 에러가 발생했다.

Django가 다중 인증 백엔드(Multiple Authentication Backends)를 사용 중이기 때문에 발생한것이다.

(AUTHENTICATION_BACKENDS에 내가 적어준 값들이 2개이상이므로)

 

Django가 어떤 인증 백엔드를 사용해야 하는지 명확하지 않아서 발생하는 것.

구체적으로는, 로그인 시 login(request, user) 함수가 어떤 인증 백엔드를 사용해야 할지 모르기 때문에 backend를 명시적으로 제공해야 한다는 오류이다.

 

from django.contrib.auth import get_backends

class discordLoginView(generic.View):
    def get(self, request):
        # if the user is logged in, they will be redirected.
        if self.request.user.is_authenticated:
            return redirect("index")

        # If the 'QUERY_STRING' is > 0, that means the code is in the url ==> oauth2/login?code=********
        elif len(self.request.META['QUERY_STRING']) > 0:
            code = self.request.GET.get('code')
            getUser = self.exchangeCode(code)
            
            # 디스코드 사용자 정보로 User 검색
            user = User.objects.filter(discord_username=getUser['username'], discord_tag=getUser['discriminator']).first()

            # 사용자가 없으면 새로 생성
            if not user:
                user = User.objects.create(
                    username=getUser['username'],
                    discord_username=getUser['username'],
                    discord_tag=getUser['discriminator'],
                    email=getUser.get('email', ''),  # 이메일이 있으면 사용, 없으면 빈 문자열
                )
                user.set_unusable_password()  # 비밀번호를 사용할 수 없게 설정
                user.save()

            # 사용자의 backend 설정
            backend = get_backends()[0]  # 첫 번째 인증 백엔드 사용 (필요 시 수정)
            user.backend = f"{backend.__module__}.{backend.__class__.__name__}"
            
            login(request, user)
            return redirect("index")

        # redirects to discord api
        else:
            return redirect(DiscordOAuth2["DISCORD_OAUTH2_URL"])

나는 그래서 디스코드 인증관련 백엔드를 AUTHENTICATION_BACKENDS 가장 위에 적어준후,

login(request, user) 호출 시 인증 백엔드를 명시적으로 지정하는 코드를 추가했다.

settings.py에서 사용하는 인증 백엔드의 경로를 지정하면 된다.

 

구체적으로 바로 이부분이다!

backend = get_backends()[0]  # 첫 번째 인증 백엔드 사용 (필요 시 수정)
user.backend = f"{backend.__module__}.{backend.__class__.__name__}"

get_backends()[0] 

AUTHENTICATION_BACKENDS 중에 첫번째인 'users.auth.DiscordAuthenticationBackend'를 사용하도록 설정!

 

user.backend = f"{backend.__module__}.{backend.__class__.__name__}"

 로그인 함수가 어떤 인증 백엔드를 사용해야 할지 명확하게 설정

 

 

 

users/auth.py

from django.contrib.auth.backends import BaseBackend
from .models import DiscordUser
from django.contrib.auth import get_user_model

class DiscordAuthenticationBackend(BaseBackend):
    def authenticate(self, request, user) -> DiscordUser:
        User = get_user_model()  # 현재 설정된 사용자 모델을 가져옵니다.
        findUser = User.objects.filter(id=user['id']).first()  # Discord 사용자 ID로 사용자 검색

        if findUser is None:
            newUser = DiscordUser.objects.createNewUser(user)  # 새로운 사용자 생성
            return newUser
        return findUser  # 기존 사용자 반환

    def get_user(self, user_id):
        User = get_user_model()
        try:
            return User.objects.get(pk=user_id)  # 사용자 ID로 사용자 반환
        except User.DoesNotExist:
            return None

Discord OAuth 인증을 처리하는 사용자 정의 백엔드코드

 

 

해결완료!!