-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#162163262 implement jwt authentication feature (#10)
- Loading branch information
1 parent
7c2dd54
commit eccdc5c
Showing
11 changed files
with
274 additions
and
104 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,58 @@ | ||
# import jwt | ||
# | ||
# from django.conf import settings | ||
# | ||
# from rest_framework import authentication, exceptions | ||
# | ||
# from .models import User | ||
import jwt | ||
from django.conf import settings | ||
from rest_framework import authentication, exceptions | ||
from .models import User | ||
|
||
"""Configure JWT Here""" | ||
|
||
|
||
class JWTAuthentication: | ||
""" | ||
This class implements a custom authentication by overriding | ||
the .authenticate(self, request) method. The method returns | ||
a two-tuple of (user, token) on successfull authentication | ||
and None otherwise. | ||
""" | ||
|
||
def authenticate_header(self, request): | ||
""" | ||
This method returns 'None' if authentication is not attempted | ||
Otherwise it returns the token. | ||
""" | ||
header = authentication.get_authorization_header(request) | ||
token = None | ||
try: | ||
token = header.split()[1].decode('utf-8') | ||
except: | ||
raise exceptions.AuthenticationFailed( | ||
'Token not found in the header') | ||
finally: | ||
return token | ||
|
||
def authenticate(self, request): | ||
pass | ||
""" | ||
This method gets the token from the authenticate_header method | ||
and perform permission checks on the token. When the checks | ||
fails, a AuthenticationFailed exception is raised otherwise | ||
a user object and token are returned. | ||
""" | ||
user_token = self.authenticate_header(request) | ||
if not user_token: | ||
return None | ||
try: | ||
payload_id = self.decode_token(user_token) | ||
user = User.objects.get(id=payload_id) | ||
except (User.DoesNotExist): | ||
raise exceptions.AuthenticationFailed('Invalid user credentials') | ||
return (user, user_token) | ||
|
||
def decode_token(self, user_token): | ||
try: | ||
payload = jwt.decode(user_token, settings.SECRET_KEY) | ||
return payload['id'] | ||
except jwt.InvalidTokenError: | ||
raise exceptions.AuthenticationFailed( | ||
'Invalid token. please login again') | ||
except jwt.ExpiredSignatureError: | ||
raise exceptions.AuthenticationFailed( | ||
'Token expired. Please log in again.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
125 changes: 125 additions & 0 deletions
125
authors/apps/authentication/test/test_authentication.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import json | ||
from django.urls import reverse | ||
from rest_framework.views import status | ||
from rest_framework.test import APITestCase, APIClient | ||
from ..models import UserManager, User | ||
|
||
|
||
class TestUsers(APITestCase): | ||
|
||
def setUp(self): | ||
self.client = APIClient() | ||
|
||
def generate_user(self, username='', email='', password=''): | ||
user = { | ||
'user': { | ||
'email': email, | ||
'username': username, | ||
'password': password | ||
} | ||
} | ||
return user | ||
|
||
def create_user(self, username='', email='', password=''): | ||
user = self.generate_user(username, email, password) | ||
self.client.post('/api/users/', user, format='json') | ||
return user | ||
|
||
def test_user_registration(self): | ||
user = self.generate_user( | ||
'athena', 'athena@gmail.com', 'password@user') | ||
response = self.client.post('/api/users/', user, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||
self.assertEqual( | ||
json.loads(response.content), | ||
{"user": {"email": "athena@gmail.com", "username": "athena"}} | ||
) | ||
|
||
def test_user_registration_empty_details(self): | ||
user = self.generate_user('', '', '') | ||
response = self.client.post('/api/users/', user, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
def test_user_registration_wrong_email_format(self): | ||
user = self.generate_user('athena', 'athenmail', 'password@user') | ||
response = self.client.post('/api/users/', user, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
def test_user_login(self): | ||
self.create_user('athena', 'athena@gmail.com', 'password@user') | ||
login_details = self.generate_user( | ||
'', 'athena@gmail.com', 'password@user') | ||
response = self.client.post( | ||
'/api/users/login/', login_details, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
self.assertEqual( | ||
json.loads(response.content), | ||
{"user": { | ||
"email": "athena@gmail.com", | ||
"username": "athena", | ||
'token': response.data['token'] | ||
} | ||
} | ||
) | ||
|
||
def test_unauthorized_access_to_authenticated_endpoint(self): | ||
self.create_user('kasule', 'athena@gmail.com', 'Password@user1') | ||
login_details = self.generate_user( | ||
'', 'athena@gmail.com', 'Password@user1') | ||
response = self.client.post( | ||
'/api/user/', login_details, format='json') | ||
self.assertTrue(response.status_code == 403) | ||
self.assertEqual( | ||
json.loads(response.content), | ||
{"user": { | ||
"detail": "Authentication credentials were not provided." | ||
} | ||
} | ||
) | ||
|
||
def test_user_with_valid_token_access_protected_endpoints(self): | ||
self.create_user('soko', 'athena@gmail.com', 'Password@user1') | ||
login_details = self.generate_user( | ||
'', 'athena@gmail.com', 'Password@user1') | ||
response = self.client.post( | ||
'/api/users/login/', login_details, format='json') | ||
token = response.data['token'] | ||
self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) | ||
res = self.client.get( | ||
'/api/user/', login_details, format='json') | ||
self.assertEqual(res.status_code, status.HTTP_200_OK) | ||
self.assertEqual( | ||
json.loads(res.content), | ||
{"user": { | ||
"email": "athena@gmail.com", | ||
"username": "soko", | ||
'token': res.data['token'] | ||
} | ||
} | ||
) | ||
|
||
def test_invalid_token(self): | ||
self.create_user('josh', 'athena@gmail.com', 'Password@user1') | ||
login_details = self.generate_user( | ||
'', 'athena@gmail.com', 'Password@user1') | ||
self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + '123hjhj12') | ||
res = self.client.get( | ||
'/api/user/', login_details, format='json') | ||
self.assertTrue(res.status_code == 401) | ||
self.assertEqual( | ||
'Invalid token. please login again', res.data['detail']) | ||
|
||
def test_login_jwt_with_bad_credentials(self): | ||
self.create_user('kica', 'athena@gmail.com', 'Password@user11') | ||
login_details = self.generate_user( | ||
'', 'kica@gmail.com', 'Password@user11') | ||
response = self.client.post( | ||
'/api/users/login/', login_details, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
self.assertEqual( | ||
{"errors": { | ||
"error": [ | ||
"A user with this email and password was not found."] | ||
} | ||
}, | ||
json.loads(response.content)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import json | ||
from django.urls import reverse | ||
from rest_framework.views import status | ||
from rest_framework.test import APITestCase, APIClient | ||
from ..models import UserManager, User | ||
|
||
|
||
class TestUsers(APITestCase): | ||
|
||
def setUp(self): | ||
self.client = APIClient() | ||
self.user = User.objects.create_user( | ||
username='', email='', password='') | ||
self.supper = User.objects.create_superuser( | ||
username='henry', email='antena@andela.com', password='longpass') | ||
|
||
def test_users_is_instance_of_User(self): | ||
self.assertIsInstance(self.user, User) | ||
self.assertIsInstance(self.supper, User) | ||
|
||
def test_raise_type_error_no_username_details(self): | ||
try: | ||
response = User.objects.create_user( | ||
username=None, email='anthena@andela.com' | ||
) | ||
except TypeError as error: | ||
self.assertTrue(str(error), 'Users must have a username.') | ||
|
||
def test_user_missing_password(self): | ||
try: | ||
res = User.objects.create_user( | ||
username='soko', email=None | ||
) | ||
except TypeError as error: | ||
self.assertTrue(str(error), 'Users must have an email address.') | ||
|
||
def test_create_a_user_model(self): | ||
self.assertTrue(self.supper.username, 'henry') | ||
self.assertNotEqual(self.supper.username, 'kasule') | ||
self.assertTrue(self.supper.email, 'anthena@andela.com') | ||
self.assertTrue(self.user) | ||
|
||
def test_get_short_name_and_full_name(self): | ||
self.assertTrue(self.supper.get_full_name, 'henry') | ||
self.assertTrue(self.supper.get_short_name, 'henry') | ||
|
||
def test_token_created_successfully(self): | ||
self.assertGreater(len(User.token(self.supper)), 12) |
Oops, something went wrong.