Passing Token Using Fetch to a Django Rest API in React Native - django

I have my rest-api set up in Django and am using React Native to connect with it. I have registered users and am able to generate tokens however I am unable to pass the token in the header of the GET request. My code is as follows:
try{
let response = await fetch("http://127.0.0.1:8000/fishes/auth/",
{
method: 'GET',
headers: {
// 'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': ' Token '+accessToken,
}});
let res = await response.text();
}}
I have been following this link http://cheng.logdown.com/posts/2015/10/27/how-to-use-django-rest-frameworks-token-based-authentication and have already verified that the response from the rest api is correct.
However on the phone with native react I get the following error in the console:
TypeError: Network request failed
at XMLHttpRequest.xhr.onerror (fetch.js:441)
at XMLHttpRequest.dispatchEvent (event-target.js:172)
at XMLHttpRequest.setReadyState (XMLHttpRequest.js:542)
What am I doing wrong in the GET code?

Alright 401 status code which means UnAuthorized.
For Django Rest Framework you must pass in the access Token as part of header for all your API requests.
Header Format will be
Key : Authorization
Value: Token <token>
You can see more here
http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication

I think that you need change
'Content-Type' to 'content-type'
On lowercase

See This answer.
The same-origin policy restricts the kinds of requests that a Web page can send to resources from another origin.
In the no-cors mode, the browser is limited to sending “simple” requests — those with safelisted methods and safelisted headers only.
To send a cross-origin request with headers like Authorization and X-My-Custom-Header, you have to drop the no-cors mode and support preflight requests (OPTIONS).
The distinction between “simple” and “non-simple” requests is for historical reasons. Web pages could always perform some cross-origin requests through various means (such as creating and submitting a form), so when Web browsers introduced a principled means of sending cross-origin requests (cross-origin resource sharing, or CORS), it was decided that such “simple” requests could be exempt from the preflight OPTIONS check.
I got around this issue by handling preflight request, as stated by the OP.
Previously, in my middleware, I filtered out requests that did not include an auth token and return 403 if they were trying to access private data. Now, I check for preflight and send a response allowing these types of headers. This way, when the following request comes (get, post, etc), it will have the desired headers and I can use my middleware as originally intended.
Here is my middleware:
class ValidateInflight(MiddlewareMixin):
def process_view(self, request, view_func, view_args, view_kwargs):
assert hasattr(request, 'user')
path = request.path.lstrip('/')
if path not in EXEMPT_URLS:
logger.info(path)
header_token = request.META.get('HTTP_AUTHORIZATION', None)
if header_token is not None:
try:
token = header_token
token_obj = Token.objects.get(token=token)
request.user = token_obj.user
except Token.DoesNotExist:
return HttpResponse(status=403)
elif request.method == 'OPTIONS':
pass
else:
return HttpResponse(status=403)
Here is my Options handling
class BaseView(View):
def options(self, request, *args, **kwargs):
res = super().options(request, *args, **kwargs)
res['Access-Control-Allow-Origin'] = '*'
res['Access-Control-Allow-Headers'] = '*'
return res

Related

Getting django rest framework jwt authentication to work [django rest, angular 5]

so I have installed djanogo rest framework JWT and set the settings and authetication classes. According to this guide. I will leave out the settings as they are correct, and that is not where the problem lies. It is also to not post too much code
https://jpadilla.github.io/django-rest-framework-jwt/
I then make a call to a authorization view on my server from the front end
let token = "hardcoded token just to get the service working";
if(token != null){
this.authservice.authorizetoken(token)
.subscribe(
(req: any)=>{
console.log(req);
}
);
// grab the permissions a user has and who they are by token
authorizetoken(token){
return this.http.get(userauthorization, {
headers: new HttpHeaders().set('Authorization', 'JWT' + token )
});
}
then in my django here is the view code:
class UserAuthorization(APIView):
authentication_classes = (JSONWebTokenAuthentication,)
def get(self, request, *args, **kwargs):
print(request.user)
return Response({})
but I keep getting anonymousUser returned. Shouldn't it be a user object since I am passing a token in the header?
I dont know what I am doing wrong.
According to documentation the headers should be in format Authorization: JWT <your_token>. When you are setting the token in headers inside your authorizetoken function, you are missing a white-space between 'JWT'+ token. This might be the problem for not authenticating the user. Have you tried the endpoint with Postman?

How to properly set up Flask with CORS and Authorization Header

I want to set up CORS within my flask application and adding flask-cors helped up to a certain point. While "CORS(app)" helped with all routes that don't require Authorization, I can't access routes that do.
One route in question looks like this.
#users_blueprint.route('/users/<user_id>', methods=['GET'])
#authenticate
def get_single_user(resp, user_id):
response_object = {
'status': 'failure',
'message': 'Invalid Payload'
}
if resp != user_id:
response_object['message'] = 'You are not authorized to view this user'
return jsonify(response_object), 400
try:
user = User.query.filter_by(user_id=user_id).first()
if user:
response_object['status'] = 'success'
response_object['data'] = user.to_json()
return jsonify(response_object), 200
else:
response_object['message'] = 'The user does not exist'
return jsonify(response_object), 400
except (exc.IntegrityError, ValueError, TypeError) as e:
db.session().rollback()
return jsonify(response_object), 400
I played around with the CORS settings and so far got this:
#enable CORS
CORS(app, allow_headers=["Content-Type", "Authorization", \
"Access-Control-Allow-Credentials"], supports_credentials=True)
But when I try to access the route in question from within my React application via fetch I get this error:
Failed to load
MYROUTE: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access. The response had HTTP status code 502. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the
resource with CORS disabled.
What else could I do?
EDIT
When I send a OPTIONS request via CURL I can see that my route in question will respond with "access-control-allow"-headers. So I'm really clueless as to what is going on. Here's also my fetch request:
const url = `myurlendpoint/myuserid`
const token = my_id_token
const options = {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
};
fetch(url, options)
.then(response => console.log(response.json()))
.catch(error => console.log(error))
ALSO: When I call my route without the header 'Authorization' present I get the correct response saying "No auth provided" and NOT the cross-origin problem. If this route really had not cross-origin-allow set then it should state that in my request without Authorization, right? So it has something to do with the Authorization header...

DRF - request.user returns AnonymousUser in APIView while logged in during api call from fetch

I've constructed a APIView as such:
class CustomAPIView(APIView):
def get(self, request, *args, **kwargs):
if not request.user or not request.user.is_authenticated():
return Response("User not logged in", status=status.HTTP_403_FORBIDDEN)
# Other stuff
And in my html template I'm making a call to it using fetchAPI:
fetch('/api/request/url/', {method: 'get'})
.then(
// Process info );
I'm logged in through all this, but I'm always being greeted with a 403 response with the request.user variable in the APIView returning AnonymousUser. However, if I try and visit the api url manually everything works out right.
Can someone point out what I'm missing?
Thanks in advance.
The issue with fetch api is that by defualt it will not send cookies to the server.
By default, fetch won't send or receive any cookies from the server,
resulting in unauthenticated requests if the site relies on
maintaining a user session (to send cookies, the credentials init
option must be set).
So You have to set credentials: 'same-origin' in your fetch request,
fetch('/api/request/url/', {method: "GET", credentials: 'same-origin'})
.then(
// Process info );
for cross-origin requests, use credentials: 'include'

Django - How to implement authentication service in microservices architecture

Basically, I have several independent services. I want to build a service for authentication. When client get a token from authentication service. Client use it for further request to others services. Client need to attach that token in header of request. The services receiving token need to verify the token by sending it to authentication server. So all requests that clients make to protected routes need to be verified by authentication service. The thing is I do not know the best place to put the code that automatically sends token to authentication service and receive the result.
Here is what i tried so far:
I implemented a middleware like that:
class VerifyTokenMiddleware(object):
def process_request(self, request):
if not request.META.get('HTTP_AUTHORIZATION'):
return HttpResponse(status=404)
auth_header = request.META.get('HTTP_AUTHORIZATION')
token = auth_header[4:]
response = requests.post(AUTH_URL, {"token": token})
if response.status_code == 400:
return HttpResponse(status=403)
return None
However, the problem of my solution is every requests to services(not auth service) have to pass through that middleware. Therefore, client cannot access unprotected routes like before.
Any help is extremely appreciated. :D
I used django restframework jwt https://github.com/GetBlimp/django-rest-framework-jwt.
It have many way to desiged microservice, you can write file restfull method like
class RestFulClient:
#classmethod
def get(cls, url, loggers, headers):
return is_success, status_code, data
#classmethod
def post(cls, url, headers, loggers, params={}):
return is_success, status_code, status_message, data
#classmethod
def put(cls, url, headers, loggers, params={}):
return is_success, status_code, status_message, data
#classmethod
def delete(cls, url, headers, loggers, params={}):
return is_success, status_code, status_message
Any question?

django-rest-framework returning 403 response on POST, PUT, DELETE despite AllowAny permissions

I'm using a django-oneall to allow social login session authentication on my site. While it isn't one of the suggested auth providers for django-rest-framework, rest_framework.authentication.SessionAuthentication uses django's default session authentication. so I thought it should be fairly simple to integrate.
On the permissions side, ultimately I'll use IsAdmin, but for development purposes, I just had it set to IsAuthenticated. When that returning 403s, I relaxed the permissions to AllowAny, but still no dice. Here's my rest framework config:
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
# 'rest_framework.permissions.IsAuthenticated',
# 'rest_framework.permissions.IsAdminUser',
),
'PAGE_SIZE': 100,
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.DjangoFilterBackend',
),
}
EDIT:
I got this working based on the answer below. It turns out that rest_framework expects both the csrftoken cookie and a a X-CSRFToken Header of the same value, I setup my front-end code to send that header for all ajax requests and everything worked fine.
Django REST Framework returns status code 403 under a couple of relevant circumstances:
When you don't have the required permission level (e.g. making an API request as an unauthenticated user when DEFAULT_PERMISSION_CLASSES is ('rest_framework.permissions.IsAuthenticated',).
When you doing an unsafe request type (POST, PUT, PATCH or DELETE - a request that should have side effects), you are using rest_framework.authentication.SessionAuthentication and you've not included your CSRFToken in the requeset.
When you are doing an unsafe request type and the CSRFToken you've included is no longer valid.
I'm going to make a few demo requests against a test API to give an example of each to help you diagnose which issue you are having and show how to resolve it. I'll be using the requests library.
The test API
I set up a very simple DRF API with a single model, Life, that contains a single field (answer, with a default value of 42). Everything from here on out is pretty straight forward; I set up a ModelSerializer - LifeSerializer, a ModelViewSet - LifeViewSet, and a DefaultRouter on the /life URL route. I've configured DRF to require user's be authenticated to use the API and to use SessionAuthentication.
Hitting the API
import json
import requests
response = requests.get('http://localhost:8000/life/1/')
# prints (403, '{"detail":"Authentication credentials were not provided."}')
print response.status_code, response.content
my_session_id = 'mph3eugf0gh5hyzc8glvrt79r2sd6xu6'
cookies = {}
cookies['sessionid'] = my_session_id
response = requests.get('http://localhost:8000/life/1/',
cookies=cookies)
# prints (200, '{"id":1,"answer":42}')
print response.status_code, response.content
data = json.dumps({'answer': 24})
headers = {'content-type': 'application/json'}
response = requests.put('http://localhost:8000/life/1/',
data=data, headers=headers,
cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF cookie not set."}')
print response.status_code, response.content
# Let's grab a valid csrftoken
html_response = requests.get('http://localhost:8000/life/1/',
headers={'accept': 'text/html'},
cookies=cookies)
cookies['csrftoken'] = html_response.cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
data=data, headers=headers,
cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF token missing or incorrect."}')
print response.status_code, response.content
headers['X-CSRFToken'] = cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
data=data, headers=headers,
cookies=cookies)
# prints (200, '{"id":1,"answer":24}')
print response.status_code, response.content
Just for anyone that might find the same problem.
If you are using viewsets without routers like:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
Django Rest framework will return 403 unless you define permission_classes at a class level:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
permission_classes= YourPermisionClass
Hope it helps!
For completeness sake, there is one more circumstance under which DRF returns code 403: if you forget to add as_view() to the view declaration in your urls.py file. Just happened to me, and I spent hours until I found where the issue was, so maybe this addition can save some time for someone.
For those that aren't able to even access their csrftoken from Javascript:
In my case I wasn't able to get the csrftoken from my Javascript code to be able to set it in my ajax POST. It always printed null. I finally discovered that the django CSRF_COOKIE_HTTPONLY environment variable was set to True.
From the Django Documentation
CSRF_COOKIE_HTTPONLY: If this is set to True, client-side JavaScript will not be able to access the CSRF cookie."
Changing CSRF_COOKIE_HTTPONLY to False allowed me to finally get the csrftoken.
https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-CSRF_COOKIE_HTTPONLY
One more situation that someone may find is that you get a 403 error on an AllowAny route when you pass an token as null in the "Authorization" header in your request. For example, you may want to allow anyone to use the route but also want to know if the person that used the route is an authenticated user.
E.g.
if (token) {
headers = {
"Content-Type": "application/json",
"Authorization": "Token " + token
}
} else {
headers = {
"Content-Type": "application/json"
}
}