Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat-7] Token Location Cookie Parsing 지원 #15

Merged
merged 3 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions django_jwt_extended/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def ready(self):
self.access_token_expires = data.access_token_expires
self.refresh_token_expires = data.refresh_token_expires
self.token_header_name = 'Authorization'
self.token_cookie_name = data.token_cookie_name
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

액세스 쿠키 이름, 리프레시 쿠키 이름을 나눠야 하지 않을까 싶습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 secure 옵션으로 인해 쿠키조작이 불가능한거였구나


self.jwt_not_found_msg = data.errors['JWT_NOT_FOUND_MSG']
self.bearer_error_msg = data.errors['BEARER_ERROR_MSG']
Expand Down
10 changes: 8 additions & 2 deletions django_jwt_extended/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,25 @@
)

allowed_algorithm = ('HS256',)
allowed_location = ('headers',)
allowed_location = ('headers', 'cookies',)

class ConfigParser:

def __init__(self, config: dict):
if not isinstance(config, dict):
raise ConfigIsNotDict()
self.token_cookie_name = self.validate_token_cookie_name(config)
self.jwt_algorithm = self.validate_jwt_algorithm(config)
self.token_location = self.validate_token_location(config)
self.access_token_expires = self.validate_access_token_expires(config)
self.refresh_token_expires = self.validate_refresh_token_expires(config)
self.errors = self.customize_error(config)

@staticmethod
def validate_token_cookie_name(config: dict):
token_cookie_name = config.get('TOKEN_COOKIE_NAME', 'token')
return token_cookie_name

@staticmethod
def validate_jwt_algorithm(config: dict):
jwt_algorithm = config.get('ALGORITHM', 'HS256')
Expand All @@ -33,7 +39,7 @@ def validate_jwt_algorithm(config: dict):

@staticmethod
def validate_token_location(config: dict):
token_location = config.get('LOCATION', ['headers'])
token_location = config.get('LOCATION', ['headers', 'cookies'])
if not (
isinstance(token_location, list)
and not (set(token_location) - set(allowed_location))
Expand Down
12 changes: 9 additions & 3 deletions django_jwt_extended/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,27 @@ def _create_payload(identity, type: str, config: DjangoJwtExtConfig):

def _find_jwt_token(request, config: DjangoJwtExtConfig):
for location in config.token_location:
# Header에서 탐지
if (
location == 'headers'
and config.token_header_name in request.headers
):
return request.headers[config.token_header_name], location

elif (location == 'cookies'
and config.token_cookie_name in request.COOKIES
):
return request.COOKIES[config.token_cookie_name], location
return None, None


def _parse_jwt_token(jwt_token: str, location: str):
if location == 'headers':
if jwt_token.startswith('Bearer '):
return jwt_token[7:]
else:
return None
elif location == 'cookies':
return jwt_token
else:
return None


def _validate_payload(payload: dict, type: str):
Expand Down
5 changes: 3 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ def setup_django():
),
JWT_CONFIG = {
'ALGORITHM': 'HS256',
'LOCATION': ['headers'],
'LOCATION': ['headers', 'cookies'],
'ACCESS_TOKEN_EXPIRES': timedelta(days=2),
'REFRESH_TOKEN_EXPIRES': timedelta(days=30),
'JWT_NOT_FOUND_MSG': {'msg': "can't find JWT token."}
'JWT_NOT_FOUND_MSG': {'msg': "can't find JWT token."},
'TOKEN_COOKIE_NAME': 'token'
},
TIME_ZONE='Asia/Seoul',
USE_TZ=False,
Expand Down
98 changes: 98 additions & 0 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from http.cookiejar import Cookie
import unittest, json, jwt
from django.conf import settings
from django.test import RequestFactory
Expand Down Expand Up @@ -158,6 +159,103 @@ def test_expired_token_auth(self):
response = user(request)
self.assertEqual(response.status_code, 401)

#cookie test
def test_auth_basic_cookie(self):
"""Run Authentication basic"""
request = self.factory.get('/user')
request.COOKIES = {'token': self.access_token}
response = user(request)
self.assertEqual(response.status_code, 200)

def test_auth_decorator_cookie(self):
"""Run Authentication basic with Decorator"""
request = self.factory.get('/user')
request.COOKIES = {'token': self.access_token}
response = decorator_user(request)
self.assertEqual(response.status_code, 200)

def test_refresh_cookie(self):
"""Run Token Refresh & reauth"""
request = self.factory.get('/refresh')
request.COOKIES = {'token': self.refresh_token}
response = refresh(request)
self.assertEqual(response.status_code, 200)
tokens = json.loads(response.content)
access_token = tokens['access_token']

request = self.factory.get('/user')
request.COOKIES = {'token': access_token}
response = user(request)
self.assertEqual(response.status_code, 200)

def test_auth_optional_cookie(self):
"""Run Auth Optional"""
auth_request = self.factory.get('/user_optional')
auth_request.COOKIES = {'token': self.access_token}
no_auth_request = self.factory.get('/user_optional')
no_auth_request.COOKIES = {'token': self.access_token}

response = user_optional(auth_request)
self.assertEqual(response.status_code, 200)
response = user_optional(no_auth_request)
self.assertEqual(response.status_code, 200)

def test_rest_api_class_cookie(self):
"""Run REST API View Class Auth"""
view = RestAPIView.as_view()

path = "/rest-api-class"
token = {'token': self.access_token}
for method in self.methods:
request = method(path)
request.COOKIES = token
response = view(request)
self.assertEqual(response.status_code, 200)

def test_rest_api_func_cookie(self):
"""Run REST API View Func Auth"""
path = "/rest-api-func"
token = {'token': self.access_token}
for method in self.methods:
request = method(path)
request.COOKIES = token
response = rest_user(request, "hello")
self.assertEqual(response.status_code, 200)

def test_invalid_token_auth_cookie(self):
"""Run Invalid Token Auth"""
payload = {'sub': 'invalid'}
algorithm = 'HS256'
secret_key = "invalid"
invalid_token = jwt.encode(payload, secret_key, algorithm)

request = self.factory.get('/user')
request.COOKIES = {'token': invalid_token}
response = user(request)
self.assertEqual(response.status_code, 401)

def test_token_not_found_cookie(self):
"""Test No Input Token Auth"""
request = self.factory.get('/user')
response = user(request)
self.assertEqual(response.status_code, 401)
self.assertEqual(
response.content.decode(encoding='utf8'),
json.dumps({'msg': "can't find JWT token."})
)

def test_expired_token_auth_cookie(self):
"""Test Expired Token Auth"""
payload = {'sub': 'iml', 'exp': 1}
algorithm = 'HS256'
secret_key = settings.SECRET_KEY
exp_token = jwt.encode(payload, secret_key, algorithm)

request = self.factory.get('/user')
request.COOKIE = {'token': exp_token}
response = user(request)
self.assertEqual(response.status_code, 401)

def _get_tokens(self):
request = self.factory.get('/login')
response = login(request)
Expand Down