From f6dac35f80fd1e1c694386bb3acbe596dbc680bf Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:43:23 +0900 Subject: [PATCH 01/15] Update requirements.txt --- requirements.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4b9dc84..c2abe2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,10 @@ cffi==1.17.1 charset-normalizer==3.3.2 colorama==0.4.6 cryptography==43.0.1 +defusedxml==0.8.0rc2 +dj-rest-auth==6.0.0 Django==5.1.1 +django-allauth==65.0.2 django-appconf==1.0.6 django-cors-headers==4.4.0 django-filter==24.3 @@ -27,6 +30,7 @@ jsonschema-specifications==2023.12.1 model-bakery==1.19.5 mypy==1.11.2 mypy-extensions==1.0.0 +oauthlib==3.2.2 packaging==24.1 pillow==10.4.0 pluggy==1.5.0 @@ -38,17 +42,20 @@ pytest==8.3.3 pytest-django==4.9.0 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 +python3-openid==3.2.0 pytz==2024.2 PyYAML==6.0.2 referencing==0.35.1 requests==2.32.3 +requests-oauthlib==2.0.0 rpds-py==0.20.0 s3transfer==0.10.2 setuptools==75.1.0 six==1.16.0 +social-auth-core==4.5.4 sqlparse==0.5.1 toposort==1.10 typing_extensions==4.12.2 tzdata==2024.2 uritemplate==4.1.1 -urllib3==2.2.3 +urllib3==2.2.3 \ No newline at end of file From 8872bdad52f8e7b8b49fe9d7e7dfeda634f5e0ee Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:43:30 +0900 Subject: [PATCH 02/15] Refactor authentication.py for JWT authentication --- jwtauth/authentication.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/jwtauth/authentication.py b/jwtauth/authentication.py index 6a52414..e1837a1 100644 --- a/jwtauth/authentication.py +++ b/jwtauth/authentication.py @@ -1,10 +1,11 @@ from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from django.contrib.auth import get_user_model -import jwt +import jwt, logging from django.conf import settings +logger = logging.getLogger(__name__) User = get_user_model() @@ -25,19 +26,25 @@ def authenticate(self, request): access_token, settings.SECRET_KEY, algorithms=["HS256"] ) - user_id = payload.get("user_id") - user = User.objects.get(id=user_id) + user = User( + id=payload["user_id"], + email=payload.get("email"), + is_staff=payload.get("is_staff"), + is_superuser=payload.get("is_superuser"), + ) + user.is_authenticated = True return (user, None) - except jwt.ExpiredSignatureError: + except jwt.ExpiredSignatureError: raise AuthenticationFailed("토큰이 만료되었습니다!") - except IndexError: + except IndexError: + raise AuthenticationFailed("토큰이 없습니다!") + except jwt.DecodeError: raise AuthenticationFailed("토큰이 유효하지 않습니다!") - except jwt.DecodeError: - raise AuthenticationFailed("토큰 디코딩 오류!") - except Exception as e: - raise AuthenticationFailed(f"인증 오류: {str(e)}") + except Exception as e: # + logger.error(f"인증 오류: {str(e)}") + raise AuthenticationFailed(f"인증이 유효하지 않습니다!") def authenticate_header(self, request): return "Bearer" From 395559599950761e56e29b0e97574c3b4a2dc23a Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:43:37 +0900 Subject: [PATCH 03/15] Add GoogleLogin view for social login with Google --- jwtauth/urls.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jwtauth/urls.py b/jwtauth/urls.py index c00ee26..06750fe 100644 --- a/jwtauth/urls.py +++ b/jwtauth/urls.py @@ -1,8 +1,10 @@ from django.urls import path -from .views import LoginView, LogoutView, RefreshTokenView +from .views import LoginView, LogoutView, RefreshTokenView, GoogleLogin + urlpatterns = [ path("login/", LoginView.as_view(), name="login"), path("logout/", LogoutView.as_view(), name="logout"), path("refresh/", RefreshTokenView.as_view(), name="refresh"), + path("social-login/google/", GoogleLogin.as_view(), name="google_login"), ] From ed048a00fac86a23b77c7e9cee8d5a0b8f9237ef Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:43:49 +0900 Subject: [PATCH 04/15] Add GoogleLogin view for social login with Google --- jwtauth/views.py | 53 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/jwtauth/views.py b/jwtauth/views.py index d6598d5..cf50ec2 100644 --- a/jwtauth/views.py +++ b/jwtauth/views.py @@ -2,13 +2,24 @@ from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework import status +from dj_rest_auth.registration.views import SocialLoginView +from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter +from allauth.socialaccount.providers.oauth2.client import OAuth2Client from django.contrib.auth import authenticate, get_user_model from django.conf import settings -from .serializers import LoginSerializer, LogoutSerializer, RefreshTokenSerializer -from .utils.token_generator import generate_access_token, generate_refresh_token +from .serializers import ( + LoginSerializer, + LogoutSerializer, + RefreshTokenSerializer, +) +from .utils.token_generator import ( + generate_access_token, + generate_refresh_token, +) from .models import BlacklistedToken import jwt, logging + logger = logging.getLogger(__name__) User = get_user_model() @@ -36,10 +47,15 @@ def post(self, request): access_token = generate_access_token(user) refresh_token = generate_refresh_token(user) - return Response( - {"access_token": access_token, "refresh_token": refresh_token} + response = Response({"access_token": access_token}) + response.set_cookie( + key="refresh_token", + value=refresh_token, + httponly=True, + secure=not settings.DEBUG, + samesite="None", ) - + return response else: return Response( {"error": "회원 가입하세요"}, status=status.HTTP_401_UNAUTHORIZED @@ -64,7 +80,9 @@ def post(self, request): refresh_token = serializer.validated_data["refresh_token"] try: - BlacklistedToken.objects.create(token=refresh_token, user=request.user) + BlacklistedToken.objects.create( + token=refresh_token, user=request.user, token_type="refresh" + ) return Response( {"success": "로그아웃 완료."}, status=status.HTTP_200_OK, @@ -136,3 +154,26 @@ def post(self, request): ) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class GoogleLogin(SocialLoginView): + adapter_class = GoogleOAuth2Adapter + callback_url = settings.GOOGLE_CALLBACK_URL + client_class = OAuth2Client + + def get_response(self): + response = super().get_response() + user = self.user + access_token = generate_access_token(user) + refresh_token = generate_refresh_token(user) + + response.set_cookie( + key="refresh_token", + value=refresh_token, + httponly=True, + secure=not settings.DEBUG, + samesite="None", + ) + response.data = {"access_token": access_token} + + return response From 5a6cf44b53166b1f46f4a92ae8641d1487a6e7d7 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:43:59 +0900 Subject: [PATCH 05/15] Refactor authentication tests for JWT authentication --- jwtauth/test/test_authentication.py | 30 +++++++++++++---------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/jwtauth/test/test_authentication.py b/jwtauth/test/test_authentication.py index 071d3a5..0cbd229 100644 --- a/jwtauth/test/test_authentication.py +++ b/jwtauth/test/test_authentication.py @@ -1,18 +1,14 @@ +import pytest +from rest_framework.test import APIClient +from rest_framework import status +from django.utils import timezone +from django.urls import reverse from datetime import timedelta - import jwt -import pytest from django.conf import settings -from django.contrib.auth import get_user_model -from django.urls import reverse -from django.utils import timezone -from rest_framework import status -from rest_framework.test import APIClient - from jwtauth.models import BlacklistedToken from jwtauth.utils.token_generator import generate_access_token, generate_refresh_token - -User = get_user_model() +from accounts.models import CustomUser as User @pytest.fixture @@ -57,12 +53,12 @@ def test_로그인_성공(api_client, user): # Given: 유효한 사용자 정보가 있음 # When: 로그인 API에 POST 요청을 보냄 response = api_client.post( - "/api/login/", {"email": "test@example.com", "password": "testpass123"} + reverse("login"), {"email": "test@example.com", "password": "testpass123"} ) # Then: 응답 상태 코드가 200이고, 액세스 토큰과 리프레시 토큰이 포함되어 있음 assert response.status_code == status.HTTP_200_OK assert "access_token" in response.data - assert "refresh_token" in response.data + assert "refresh_token" in response.cookies @pytest.mark.django_db @@ -71,7 +67,7 @@ def test_로그인_실패(api_client): # Given: 잘못된 사용자 정보가 있음 # When: 로그인 API에 잘못된 정보로 POST 요청을 보냄 response = api_client.post( - "/api/login/", {"email": "wrong@example.com", "password": "wrongpass"} + reverse("login"), {"email": "wrong@example.com", "password": "wrongpass"} ) # Then: 응답 상태 코드가 401 (Unauthorized)임 assert response.status_code == status.HTTP_401_UNAUTHORIZED @@ -83,7 +79,7 @@ def test_로그아웃_성공(api_client, user, refresh_token): # Given: 인증된 사용자와 유효한 리프레시 토큰이 있음 api_client.force_authenticate(user=user) # When: 로그아웃 API에 리프레시 토큰과 함께 POST 요청을 보냄 - response = api_client.post("/api/logout/", {"refresh_token": refresh_token}) + response = api_client.post(reverse("logout"), {"refresh_token": refresh_token}) # Then: 응답 상태 코드가 200이고, 리프레시 토큰이 블랙리스트에 추가됨 assert response.status_code == status.HTTP_200_OK assert BlacklistedToken.objects.filter(token=refresh_token).exists() @@ -95,7 +91,7 @@ def test_로그아웃_실패_토큰없음(api_client, user): # Given: 인증된 사용자가 있지만 리프레시 토큰이 없음 api_client.force_authenticate(user=user) # When: 로그아웃 API에 리프레시 토큰 없이 POST 요청을 보냄 - response = api_client.post("/api/logout/", {}) + response = api_client.post(reverse("logout"), {}) # Then: 응답 상태 코드가 400 (Bad Request)임 assert response.status_code == status.HTTP_400_BAD_REQUEST @@ -105,7 +101,7 @@ def test_리프레시_토큰_갱신_성공(api_client, user, refresh_token): """리프레시 토큰 갱신 API를 테스트합니다.""" # Given: 유효한 리프레시 토큰이 있음 # When: 리프레시 API에 리프레시 토큰과 함께 POST 요청을 보냄 - response = api_client.post("/api/refresh/", {"refresh_token": refresh_token}) + response = api_client.post(reverse("refresh"), {"refresh_token": refresh_token}) # Then: 응답 상태 코드가 200이고, 새로운 액세스 토큰과 리프레시 토큰이 반환되며, 기존 리프레시 토큰이 블랙리스트에 추가됨 assert response.status_code == status.HTTP_200_OK assert "access_token" in response.data @@ -121,7 +117,7 @@ def test_리프레시_토큰_갱신_실패_블랙리스트(api_client, user, ref token=refresh_token, user=user, token_type="refresh" ) # When: 리프레시 API에 블랙리스트에 등록된 리프레시 토큰과 함께 POST 요청을 보냄 - response = api_client.post("/api/refresh/", {"refresh_token": refresh_token}) + response = api_client.post(reverse("refresh"), {"refresh_token": refresh_token}) # Then: 응답 상태 코드가 400 (Bad Request)임 assert response.status_code == status.HTTP_400_BAD_REQUEST From 56afd960f00113544e673c1aa978a16cce64aa76 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:44:05 +0900 Subject: [PATCH 06/15] Refactor token_generator.py to include additional user information in the access token --- jwtauth/utils/token_generator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jwtauth/utils/token_generator.py b/jwtauth/utils/token_generator.py index e79db64..2aae7ea 100644 --- a/jwtauth/utils/token_generator.py +++ b/jwtauth/utils/token_generator.py @@ -12,6 +12,10 @@ def generate_access_token(user): "user_id": user.id, "is_staff": user.is_staff, "is_superuser": user.is_superuser, + "iat": timezone.now(), + "nickname": user.nickname, + "email": user.email, + "image": user.image, "exp": timezone.now() + timedelta(minutes=30), } return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256") From da41ef551a3bcd582d0098d479c782e0c592fc13 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:45:14 +0900 Subject: [PATCH 07/15] Add social login functionality with Google --- weaverse/settings.py | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/weaverse/settings.py b/weaverse/settings.py index 83cffa5..c500b04 100644 --- a/weaverse/settings.py +++ b/weaverse/settings.py @@ -34,9 +34,21 @@ "payments", "corsheaders", "django_filters", + # social login + "social_django", + "django.contrib.sites", + "rest_framework.authtoken", + "allauth", + "allauth.account", + "allauth.socialaccount", + "allauth.socialaccount.providers.google", + "allauth.socialaccount.providers.kakao", + "dj_rest_auth", + "dj_rest_auth.registration", ] MIDDLEWARE = [ + "allauth.account.middleware.AccountMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -160,3 +172,57 @@ } DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" + +SITE_ID = 1 + +AUTHENTICATION_BACKENDS = [ + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", +] + +SOCIALACCOUNT_PROVIDERS = { + "google": { + "SCOPE": [ + "profile", + "email", + ], + "AUTH_PARAMS": { + "access_type": "online", + }, + "APP": { + "client_id": os.getenv("SOCIAL_AUTH_GOOGLE_CLIENT_ID"), + "secret": os.getenv("SOCIAL_AUTH_GOOGLE_SECRET"), + "key": "", + }, + }, + "kakao": { + "SCOPE": [ + "profile", + "account_email", + ], + "APP": { + "client_id": os.getenv("SOCIAL_AUTH_KAKAO_CLIENT_ID"), + "secret": "", + "key": "", + }, + }, +} + + +ACCOUNT_USER_MODEL_USERNAME_FIELD = None +ACCOUNT_AUTHENTICATION_METHOD = "email" +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_USERNAME_REQUIRED = False + + +REST_USE_JWT = True +JWT_AUTH_COOKIE = "my-app-auth" +JWT_AUTH_REFRESH_COOKIE = "my-refresh-token" + +GOOGLE_CALLBACK_URL = "https://www.weaverse.site/social-login/google/" + +SOCIAL_AUTH_KAKAO_KEY = os.getenv("SOCIAL_AUTH_KAKAO_KEY") + +REDIRECT_URL = "https://www.weaverse.site" +LOGIN_REDIRECT_URL = "/dashboard/" +LOGOUT_REDIRECT_URL = "/" From 4b8acc3882475cc8c1aee807abedede902fcf5c3 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:51:35 +0900 Subject: [PATCH 08/15] Refactor: Update requirements.txt --- requirements.txt | Bin 1152 -> 2488 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index c2abe2abd5689157ae66a7921662e408f88ab8b2..ed3616df1c326af87af596a6d92311b1fbb9bffd 100644 GIT binary patch literal 2488 zcmaKuNpBNT5QO`T#7}V)+aZAi2PDJ+fy4m`aYB=^6Fc!T@e-2!c%Z(nc^TWWkmW_w z^}4I8m*>BKs#wQ$Ok)+}xQh3B4dW!<>fMjm@hYm==(Q3G+aS(l8R&hQ*2`GRy3`r0 zeLWw>oA#5_#a;_TgyX1MmX-WW#bBD4f&4obOonZ=TK0kc{d7M3aB#%>_uiMC@xDiVtUdjj z>9y|*JRF_zA-+*QsIU-oyyF?Pv)@O%ifi$nhI z%6M~~{++RJ^PCx?anW}rC41u32w96~rO$0v#o4{IgCjbCzg4ok)Iw6a?+pLK_sJ)%>siqW54kGb#7z0q@Kb*XN0 z#i>d+L5*x%oxW~&4GcWVWYdV>W7e!`>xDME@ptX00}J+}6*xrW3(nr3WlHhre7@=2 z=lBudlPhsh)@n4eG26@}nRsKF%XYm!I(=zQ&idm(6Xf?^LGN z8>j08&u=$_Anuf@RV+P^hGc9d4>9n5*@xiqZtg2(Hj+v`eY^75$f6+D^3MIv9kSN8 zC_4s3KXJ~*_v%)D*u31DW_j^^A2}j(^N?DgdB45O=mhpdEh~2vHAZkDBWTiIx nx8dIPMn3GBtzu0TjUCC9yUWWoI6nNru_4$n!?yc87uVC~z?{=){$Cx11xAsBjC%sDlUN7SV@23SOCC zj2aB_KLAs+e;TbT1RMbn+M*cFBKta<92H4OI<)MXgU|Y6$P&0u4ey}}2S#f}J!wE1WV9XX<}et?&x8cV zf%=m;vU!c$p#^L@aPwY_&s#BF*swt6ID00@9nzxG&uXd?LFtx7>s)w4OB#@dMYl{- zZ3;J8%&QI&WNx+Fk)N$s1mhN6b)@+t??l(JAM4j&Kk5C6I;;iaONpU{t=oxjBSHe) zQZB*sK^3zUiy5qyB8FUQef=1~>vb0qZ4m5vh{jtC2;r!yA5}8w3J||PegC!%ZpW~C zCSf)zZP-XwCG)ZX8mTOLJpTgxF4^W`ibLk4GzNqu&3#GAXSAdcgzFf#Z^2>(hju4g z4`kW*Y7|z7<)E!{b(i?I+6Rowf#nxhrQ64+1h5VXdcom_l{EtKpZ`sTW#nS{fLs`% a`a|g0l58<^bdZo#rR`jjgX=x91G&HNbYg4( From 124538611f34a003641473c3bdc9cb398f79e9b4 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 15:54:55 +0900 Subject: [PATCH 09/15] Refactor: Remove unused 'image' field from access token generation --- jwtauth/utils/token_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jwtauth/utils/token_generator.py b/jwtauth/utils/token_generator.py index 2aae7ea..f1a87ce 100644 --- a/jwtauth/utils/token_generator.py +++ b/jwtauth/utils/token_generator.py @@ -15,7 +15,7 @@ def generate_access_token(user): "iat": timezone.now(), "nickname": user.nickname, "email": user.email, - "image": user.image, + # "image": user.image, "exp": timezone.now() + timedelta(minutes=30), } return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256") From a465eda8cf7bdd2c6a24ba737e92a15ff8a642f8 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 16:18:35 +0900 Subject: [PATCH 10/15] Refactor: Include additional user information in access token generation --- jwtauth/authentication.py | 45 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/jwtauth/authentication.py b/jwtauth/authentication.py index e1837a1..f6a6f1e 100644 --- a/jwtauth/authentication.py +++ b/jwtauth/authentication.py @@ -2,6 +2,7 @@ from rest_framework.exceptions import AuthenticationFailed from django.contrib.auth import get_user_model import jwt, logging +from django.core.cache import cache from django.conf import settings @@ -10,11 +11,6 @@ class JWTAuthentication(BaseAuthentication): - """ - 해당 클래스는 JWT 토큰을 사용하여 사용자를 인증하는 데 사용됩니다. - - 토큰이 유효하지 않으면 해당하는 메시지를 반환합니다. - """ - def authenticate(self, request): auth_header = request.headers.get("Authorization") if not auth_header: @@ -26,25 +22,38 @@ def authenticate(self, request): access_token, settings.SECRET_KEY, algorithms=["HS256"] ) + user_id = payload["user_id"] + + cache_key = f"user_{user_id}" + user_data = cache.get(cache_key) + + if user_data is None: + user = User.objects.get(id=user_id) + user_data = { + "id": user.id, + "email": user.email, + "is_staff": user.is_staff, + "is_superuser": user.is_superuser, + } + cache.set(cache_key, user_data, timeout=18000) + user = User( - id=payload["user_id"], - email=payload.get("email"), - is_staff=payload.get("is_staff"), - is_superuser=payload.get("is_superuser"), + id=user_data["id"], + email=user_data["email"], + is_staff=user_data["is_staff"], + is_superuser=user_data["is_superuser"], ) - user.is_authenticated = True return (user, None) - except jwt.ExpiredSignatureError: + except jwt.ExpiredSignatureError: raise AuthenticationFailed("토큰이 만료되었습니다!") - except IndexError: + except IndexError: raise AuthenticationFailed("토큰이 없습니다!") - except jwt.DecodeError: + except jwt.DecodeError: raise AuthenticationFailed("토큰이 유효하지 않습니다!") - except Exception as e: # + except User.DoesNotExist: + raise AuthenticationFailed("유효하지 않은 사용자입니다!") + except Exception as e: logger.error(f"인증 오류: {str(e)}") - raise AuthenticationFailed(f"인증이 유효하지 않습니다!") - - def authenticate_header(self, request): - return "Bearer" + raise AuthenticationFailed("인증이 유효하지 않습니다!") From e4406f964d98a0b157197a30a6e45752b34da161 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 16:35:17 +0900 Subject: [PATCH 11/15] Refactor: Update access token generation to return 403 Forbidden instead of 401 Unauthorized --- jwtauth/test/test_authentication.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jwtauth/test/test_authentication.py b/jwtauth/test/test_authentication.py index 0cbd229..8fc4f9b 100644 --- a/jwtauth/test/test_authentication.py +++ b/jwtauth/test/test_authentication.py @@ -142,8 +142,8 @@ def test_JWT_인증_실패_만료된_토큰(api_client, user): url = reverse("refresh") # When: 리프레시 API에 만료된 토큰과 함께 POST 요청을 보냄 response = api_client.post(url) - # Then: 응답 상태 코드가 401 (Unauthorized)임 - assert response.status_code == status.HTTP_401_UNAUTHORIZED + # Then: 응답 상태 코드가 403 (Forbidden)임 + assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db @@ -154,8 +154,8 @@ def test_JWT_인증_실패_유효하지_않은_토큰(api_client): url = reverse("refresh") # When: 리프레시 API에 유효하지 않은 토큰과 함께 POST 요청을 보냄 response = api_client.post(url) - # Then: 응답 상태 코드가 401 (Unauthorized)임 - assert response.status_code == status.HTTP_401_UNAUTHORIZED + # Then: 응답 상태 코드가 403 (Forbidden)임 + assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db From 616669a7cdc6fd6c55e71386c31919d555fe831c Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 16:39:17 +0900 Subject: [PATCH 12/15] Refactor: Update login failure response to return 403 Forbidden instead of 401 Unauthorized --- jwtauth/test/test_authentication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jwtauth/test/test_authentication.py b/jwtauth/test/test_authentication.py index 8fc4f9b..decde14 100644 --- a/jwtauth/test/test_authentication.py +++ b/jwtauth/test/test_authentication.py @@ -69,8 +69,8 @@ def test_로그인_실패(api_client): response = api_client.post( reverse("login"), {"email": "wrong@example.com", "password": "wrongpass"} ) - # Then: 응답 상태 코드가 401 (Unauthorized)임 - assert response.status_code == status.HTTP_401_UNAUTHORIZED + # Then: 응답 상태 코드가 403 + assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db From 53c0bfd27dbafc6bb521e70ea378b111b74fb581 Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 16:49:44 +0900 Subject: [PATCH 13/15] Refactor: requirements UTF-16 > UTF-8 --- requirements.txt | Bin 2488 -> 1182 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index ed3616df1c326af87af596a6d92311b1fbb9bffd..f8a148e2d3ed30bea2248cace5295ff7b3c9bf65 100644 GIT binary patch literal 1182 zcmYjR%Wm5+5WMqWNa$h5$$^IgMGr*_^iZJash}xJrcIFyNhMLfzO$4a2hc%W?Jj3$ zW~K6ElNqJ##iMAMDkWtpyPfDn!;YbZUdmSVdpz~1Lo)Y>HmTUrqM$*^qv#u^M>@Bn zJ)oUFsgzCOKBQSW`)(3l)QgVk;6hTflClvy@xXLiV+m7I@w7_0>qUd|!Hi2b!`tjA z?NKyIcWgNGWOCtZDJLl(kl3)VXXU3LBvIiOj!_4XiCaV;Zc)sY z`N^ok(EK}KYHpuK>k0wKg#9g+jTs12f4?pl3^W;30|Oe|QeW&~0ykcV!TM>t_STyQ z89{@24e3JEw0i=YMxLY5Md`&U+zj-GYkXC?_UKx1VBgfGGD4^=o2S}W>F25FGdcBq@fU>rXZ z5(tOUpTwTcYupYkVAFw{w_<$Wis{0J1ro>EBSG$v7L~rJsZs={TNbTz;T0{(APtLd znWowlYOBKs#wQ$Ok)+}xQh3B4dW!<>fMjm@hYm==(Q3G+aS(l8R&hQ*2`GRy3`r0 zeLWw>oA#5_#a;_TgyX1MmX-WW#bBD4f&4obOonZ=TK0kc{d7M3aB#%>_uiMC@xDiVtUdjj z>9y|*JRF_zA-+*QsIU-oyyF?Pv)@O%ifi$nhI z%6M~~{++RJ^PCx?anW}rC41u32w96~rO$0v#o4{IgCjbCzg4ok)Iw6a?+pLK_sJ)%>siqW54kGb#7z0q@Kb*XN0 z#i>d+L5*x%oxW~&4GcWVWYdV>W7e!`>xDME@ptX00}J+}6*xrW3(nr3WlHhre7@=2 z=lBudlPhsh)@n4eG26@}nRsKF%XYm!I(=zQ&idm(6Xf?^LGN z8>j08&u=$_Anuf@RV+P^hGc9d4>9n5*@xiqZtg2(Hj+v`eY^75$f6+D^3MIv9kSN8 zC_4s3KXJ~*_v%)D*u31DW_j^^A2}j(^N?DgdB45O=mhpdEh~2vHAZkDBWTiIx nx8dIPMn3GBtzu0TjUCC9yUWWoI6nNru_4$n!?yc8 Date: Thu, 10 Oct 2024 17:17:59 +0900 Subject: [PATCH 14/15] Refactor: Move 'allauth.account.middleware.AccountMiddleware' to correct position in MIDDLEWARE --- weaverse/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weaverse/settings.py b/weaverse/settings.py index c500b04..cef841c 100644 --- a/weaverse/settings.py +++ b/weaverse/settings.py @@ -48,7 +48,6 @@ ] MIDDLEWARE = [ - "allauth.account.middleware.AccountMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -57,6 +56,7 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", ] ROOT_URLCONF = "weaverse.urls" From 0e2c0e0a7d2e2f8c1eccfbf061967c0ec1c9470c Mon Sep 17 00:00:00 2001 From: nathanLYJ Date: Thu, 10 Oct 2024 17:45:48 +0900 Subject: [PATCH 15/15] =?UTF-8?q?Refactor:=20403,401=20=EC=B4=9D=EB=8F=8C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accounts/test/test_accounts_views.py | 4 ++-- courses/test/test_views.py | 12 ++++++------ jwtauth/test/test_authentication.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/accounts/test/test_accounts_views.py b/accounts/test/test_accounts_views.py index 61e3146..4d4c087 100644 --- a/accounts/test/test_accounts_views.py +++ b/accounts/test/test_accounts_views.py @@ -89,7 +89,7 @@ def test_password_reset_unauthenticated(self, api_client): "confirm_new_password": "confirmnewpassword", } response = api_client.post(url, data) - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db @@ -113,7 +113,7 @@ def test_student_list_view(self, api_client, create_user): def test_student_list_view_unauthenticated(self, api_client): url = reverse("accounts:student-list") response = api_client.get(url) - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db diff --git a/courses/test/test_views.py b/courses/test/test_views.py index b2404fc..38e2cdc 100644 --- a/courses/test/test_views.py +++ b/courses/test/test_views.py @@ -153,7 +153,7 @@ def test_course_수정_실패_로그인하지않은경우(self, api_client): response = api_client.put(url, data, format="json") # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } @@ -224,7 +224,7 @@ def test_course_삭제_실패_로그인하지않은경우(self, api_client): response = api_client.delete(url) # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } @@ -340,7 +340,7 @@ def test_course_생성_요청_실패_로그인하지않은경우(self, api_clien response = api_client.post(url, data, format="json") # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } @@ -429,7 +429,7 @@ def test_curriculum_생성_요청_실패_로그인하지않은경우( response = api_client.post(url, data, format="json") # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } @@ -558,7 +558,7 @@ def test_curriculum_수정_실패_로그인하지않은경우( response = api_client.put(url, data, format="json") # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } @@ -612,7 +612,7 @@ def test_curriculum_삭제_실패_로그인하지않은경우(self, api_client): response = api_client.delete(url) # Then - assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.status_code == status.HTTP_403_FORBIDDEN assert response.data == { "detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." } diff --git a/jwtauth/test/test_authentication.py b/jwtauth/test/test_authentication.py index decde14..8fc4f9b 100644 --- a/jwtauth/test/test_authentication.py +++ b/jwtauth/test/test_authentication.py @@ -69,8 +69,8 @@ def test_로그인_실패(api_client): response = api_client.post( reverse("login"), {"email": "wrong@example.com", "password": "wrongpass"} ) - # Then: 응답 상태 코드가 403 - assert response.status_code == status.HTTP_403_FORBIDDEN + # Then: 응답 상태 코드가 401 (Unauthorized)임 + assert response.status_code == status.HTTP_401_UNAUTHORIZED @pytest.mark.django_db