Authentication error 401 for jwt token in DRF tests - django

I have a problem getting jwt authorization for get method in test. my tests:
class ImageViewsTests(APITestCase):
def setUp(self):
self.test_user = User.objects.create_user(**TEST_USER)
TEST_IMAGE['created_by'] = self.test_user
self.temp_image = Image.objects.create(**TEST_IMAGE)
self.token = RefreshToken.for_user(self.test_user)
def test_images_list(self):
url = reverse('jwt-create')
client = APIClient()
client.credentials(HTTP_AUTHORIZATION=f'JWT {self.token.access_token}')
response = self.client.get('/image/', data={'format': 'json'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, TEST_IMAGE)
Tests return AssertionError: 401 != 200.
Token is returned correctly but does not work in header. For example everything is fine in Postman. postman
I did something else like this and I also get no authorization
def test_images_list(self):
url = reverse('jwt-create')
client = APIClient()
resp = self.client.post(url, {'email':TEST_USER['email'], 'password':TEST_USER['password']}, format='json')
self.assertEqual(resp.status_code, status.HTTP_200_OK)
client.credentials(AUTHORIZATION=f'JWT {resp.data["access"]}')
response = self.client.get('/image/', data={'format': 'json'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, TEST_IMAGE)
Im using Djoser, jwt, DRF. Where am I making a mistake?

Try this for setting the credentials:
self.token = RefreshToken.for_user(self.test_user)
client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token.access_token}')

Related

How to access a jwt token from a django rest framework to angular frontend

I have created a DRF api authenticated with jwt,the token is stored in a cookie.I can successfully access all the viewsets using the token with postman.It only becomes a problem when l want to pass the token to angular frontend for the same operations.I am using django rest framework backend and Angular 9 frontend.Also note that l am storing the token in a cookie.
My views.py
class LoginView(APIView):
def post(self,request):
#getting the inputs from frontend/postman
email =request.data['email']
password =request.data['password']
user=User.objects.filter(email=email).first()
#Authentication
if user is None:
raise AuthenticationFailed('User not found!')
if user.password!=password :
raise AuthenticationFailed("incorrect password")
payload = {
'id':user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=10),
'iat': datetime.datetime.utcnow()
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
response = Response()
#storing the token in a cookie
response.set_cookie(key='jwt',value=token ,httponly=True)
response.data = {
'jwt':token
}
return response
class UserView(APIView):
def get(self,request):
token=request.COOKIES.get('jwt')
if not token:
raise AuthenticationFailed("unauthorised")
try:
payload =jwt.decode(token, 'secret', algorithms=['HS256'])
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("session expired")
user=User.objects.get(id=payload['id'])
serializer=UserSerializer(user)
return Response(serializer.data)
class Update(APIView):
def get_object(self,request):
try:
token=request.COOKIES.get('jwt')
if not token:
raise AuthenticationFailed("unauthorised")
try:
payload =jwt.decode(token, 'secret', algorithms=['HS256'])
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("session expired")
user=User.objects.get(id=payload['id'])
return user
except User.DoesNotExist:
return Response("wakadhakwa",status=status.HTTP_204_NO_CONTENT)
def get(self,request):
obj=self.get_object(request)
serializer=UserSerializer(obj)
return Response(serializer.data)
def put(self,request):
obj=self.get_object(request)
serializer=UserSerializer(obj,data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response("corrupted data",status=status.HTTP_204_NO_CONTENT)
def delete(self,request):
all=self.get_object(request)
all.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Did you check that the cookie gets properly saved in browser when receiving response from login?
Are you calling the UserView endpoints from your Angular app with an AJAX call or are you reloading the page? If it is a call from the app make sure that the request sends cookies. It depends on how exactly you request the data, e.g. if you're using fetch, then make sure you have the option credentials: 'include' set. If you're requesting the data in some other way try to find in the documentation which option is used to enable sending credentials (cookies).

How can I set a persistent JWT token header in flask?

I can't seem to find a way to set the JWT Token as a header in each HTTP Request without the help of Javascript.
Currently I have my application setup to use the methods 'set_access_cookies' and 'unset_access_cookies' to keep track of the session. However, just unsetting the cookie does not invalidate the cookie. Therefore, I would like to use JWT in the header so that I can use the blacklist.add() method as blacklist.add() appears to not be able to look at cookies from my testing.
My Login function:
#app.route('/v1/login', methods=['POST', 'GET'])
def auth_user():
''' auth endpoint '''
if request.method == 'POST':
data = validate_user(request.get_json())
if data['ok']:
data = data['data']
user = mongo.db.users.find_one({'email': data['email']}, {"_id": 0})
if user and flask_bcrypt.check_password_hash(user['password'], data['password']):
access_token = create_access_token(identity=data)
refresh_token = create_refresh_token(identity=data)
resp = make_response(render_template('index.html'), 302)
set_access_cookies(resp, access_token)
return resp
else:
return jsonify({'ok': False, 'message': 'invalid username or password'}), 200
else:
return jsonify({'ok': False, 'message': 'invalid username or password'}), 200
elif request.method == 'GET':
return render_template('/api/v1/login.html')
My Logout function:
#app.route('/v1/logout', methods=['POST'])
def logout():
''' logout user endpoint '''
resp = jsonify({'logout': True})
unset_jwt_cookies(resp)
return resp, 200
This works fine, but is there an easy way to place the JWT as a persistent header instead?
I forgot to set 'JWT_COOKIE_CSRF_PROTECT' in my config so my POST request to logout was returning Unauthorized before it could be blacklisted.

Unit Test Using Forcing Authentication Django Rest Framework

I'm building RESTful API services using django rest framework, I've reached the point where i have to create an automated test for my RESTful API services.
The sessionList api require token authentication, in case the user doesn't have the token he won't be able to access the session collection.
The api worked fine when I've tested it using POSTMAN and the real browser.
SessionList:
class SessionList(generics.ListCreateAPIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [IsAuthenticated, ]
throttle_scope = 'session'
throttle_classes = (ScopedRateThrottle,)
name = 'session-list'
filter_class = SessionFilter
serializer_class = SessionSerializer
ordering_fields = (
'distance_in_miles',
'speed'
)
def get_queryset(self):
return Session.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Then i've created an automated test using DRF test
RunningSessionTest:
class RunningSessionTest(APITestCase):
def test_get_sessions(self):
factory = APIRequestFactory()
view = views.SessionList.as_view()
user = User.objects.create_user(
'user01', 'user01#example.com', 'user01P4ssw0rD')
request = factory.get('http://localhost:8000/sessions/')
force_authenticate(request, user=user)
response = view(request)
assert Response.status_code == status.HTTP_200_OK
def test_get_sessions_not_authenticated_user(self):
factory = APIRequestFactory()
view = views.SessionList.as_view()
user = User.objects.create_user(
'user01', 'user01#example.com', 'user01P4ssw0rD')
request = factory.get('http://localhost:8000/sessions/')
response = view(request)
assert Response.status_code == status.HTTP_401_UNAUTHORIZED
The issue: in both cases, if the user has the token or not the response value is HTTP_200_OK
I've tried to solve the problem by trying different methods to implement the test. I've used APIRequestFactory, Also i've used the APIClient but i got the same result. To be honest after reading the document many times i couldn't understand the differences between the APIClient and the APIRequestFactory.
The result of the test :
Traceback (most recent call last):
File "C:\python_work\DjnagoREST\01\restful01\RunKeeper\tests.py", line 67, in test_get_sessions_not_authenticated_user
assert Response.status_code == status.HTTP_401_UNAUTHORIZED
AssertionError
I will be grateful to your help.
I guess you will need to change Response.status_code to response.status_code.
As it turned out Response.status_code (as from rest_framework.response.Response)
is equal to 200 :D

Django Rest Framework testing function that uses requests library

How can I test the following function in a django project?
#api_view(['GET'])
def get_films(request):
if request.method == "GET":
r = requests.get('https://swapi.co/api/films')
if r.status_code == 200:
data = r.json()
return Response(data, status=status.HTTP_200_OK)
else:
return Response({"error": "Request failed"}, status=r.status_code)
else:
return Response({"error": "Method not allowed"}, status=status.HTTP_400_BAD_REQUEST)
You need to mock requests.
from unittest.mock import Mock, patch
from rest_framework.test import APITestCase
class YourTests(APITestCase):
def test_get_films_success(self):
with patch('*location of your get_films_file*.requests') as mock_requests:
mock_requests.post.return_value = mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'message': "Your expected response"}
response = self.client.get(f'{your_url_for_get_films_view}')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {'message': f'{expected_response}'})
With the similar approach you can test all your conditions for wrong Method or 404 response.

Authentication on Post DRF

I got an error on my DRF. when I try to authenticate using a post method.
the token is the correct for the admin user. when I use a safe method it is sucessfull, but with the post method no, it doesn't authenticate
my view
class SpecialistListView(ListCreateAPIView):
authentication_classes = (OAuth2Authentication,)
permission_classes = (permissions.IsAdminUser,)
queryset = Specialist.objects.all()
serializer_class = SpecialistSerializer
I don't understand why the status of the code returned is HTTP 401 Unauthorized.
It does'nt work on testing. I check that in postman is succesfull.
I'm using APIClient from rest_framework.test import APIClient
my test_client
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Bearer EGsnU4Cz3Mx5bUCuLrc2hmup51sSGz')
class CreateSpecialist(APITestCase):
fixtures = ['data','data2']
def setUp(self):
self.valid_payload = {
'username': 'julia',
'nick': 'julia',
'password': 'intel12345',
"first_name": "juliana",
"last_name": "garzon"
}
def test_create_specialist(self):
response = self.client.post(
reverse('specialists'),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
I just add the credentials before the post and it works! :D
def test_create_specialist(self):
self.client.credentials(HTTP_AUTHORIZATION='Bearer EGsnU4Cz3Mx5bUCuLrc2hmup51sSGz')
response = self.client.post(
reverse('specialists'),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)