2

I'm having issues with DRF's token based authentication. Following is my landing page code (after login):

@api_view(['GET','POST'],)
def landing(request):
    this_tenant=request.user.tenant
    end=date_first.date.today()
    start=end-date_first.timedelta(days=30)
    sales_daily=sales_day_wise(start, end, this_tenant)
    invoice_value=sales_raised_value(start, end, this_tenant)
    payment_value=sales_collected_value(start, end, this_tenant)
    return render(request,'landing.html', {'sales_daily':json.dumps(sales_daily, cls=DjangoJSONEncoder),\
        'invoice_value':json.dumps(invoice_value, cls=DjangoJSONEncoder), \
        'payment_value':json.dumps(payment_value, cls=DjangoJSONEncoder)})

I was using Django's built-in login view to authenticate and log in a user, then I revised to consider putting token in the header. But that is also not working

This is my login code:

#Redirect authenticated users to landing page
def custom_login(request):
    if request.user.is_authenticated():
        token, created = Token.objects.get_or_create(user=request.user)
        request.session['AUTHORIZATION'] = "TOKEN "+token.key
        return redirect(landing)
    else:
        return login(request)

Following is my DRF settings:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        # 'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
}

Issue is, when I'm logging in and going to the landing page via browser, DRF is not working and I'm getting the following error:

{"detail":"Authentication credentials were not provided."}

The reason is the custom DRF header (AUTHENTICATION = TOEKN XXXXXXXXXX) is not present in the request.

However, if I use Postman and put in the custom header (AUTHENTICATION = TOKEN XXXXXXXXXXXX), then it works.

How do I solve it?

Would this mean I would need a custom header for every view?

And on using DRF Token, does it open up CSRF vulnerability (this question: Django DRF - How to do CSRF validation with token authentication )?

Thanks a lot!!

Sayantan
  • 315
  • 5
  • 20

4 Answers4

1
  • How do I solve it? Would this mean I would need a custom header for every view? TokenAuthentication is used for Single Page App and the token in Request Header needs to be provided by API client(Postman, Javascript or any other client) on each request. In your case, if you want to use Django views you should activate SessionAuthentication. TokenAuthentication and SessionAuthentication can co-exist. One way is to save the token in the cookie in your in custom login view and read it by javascript client.

  • On using DRF Token, does it open up CSRF vulnerability (this question: Django DRF - How to do CSRF validation with token authentication )? Yes. But there are ways to secure the requests. According to DRF documentation "If you use token authentication in production you must ensure that your API is only available over https". Also, make sure set ALLOWED_HOSTS in settings to the domains that you want Django to respond to so the server does not respond to requests with other origins. There other more secure Auths can be used beside TokenAuthentication like JWT that has been mentioned above.

Bradia
  • 827
  • 5
  • 8
0

You need to learn the basics first. What is HTTP, what is HTTP headers, what is Django session (it's not an HTTP header and contents of the session doesn't affect the headers), read Django REST Framework documentation on token authentication.

If you want to test your views in browsers, then explicitly allow Django Session authentication in the DRF DEFAULT_AUTHENTICATION_CLASSES configuration variable. It can coexist with token authentication.

You can't make plain web browser append token to the HTTP request unless you're using some plugin like RESTClient, or DHC or REST Easy.

You're adding token to the Django session but you have disabled session authentication in DRF and even if you'd enable it, DRF doesn't read token from Django session because API client has no way to add Token to the Django session. Even if DRF would read Token from Django sessions it would be totally pointless as the client has no control over contents of the session. Session variables are set on the server, not on the client.

Eugene Morozov
  • 15,081
  • 3
  • 25
  • 32
  • Regret if my wording was a little incorrect I dont want my browser to append anything. What I want is to log-in and I dont see my username/password token present during login. Django authentication is working and as a fallback it's doing great. But, my token based usage is not working somehow. To reiterate: it's all working without DRF (vanilla Django), but token authentication aint working. – Sayantan May 29 '17 at 07:33
  • @SGangs I've started my answer with a suggestion to read the documentation first. You're not using Token authentication properly, because you don't understand what is a Token authentication and how to use it. To use token authentication, you must: 1) create a token 2) pass the token in the request to the API method in the `Authorization` http header. – Eugene Morozov May 29 '17 at 07:39
  • DRF contains an exact command line to make requests using Token authentication on this page: http://www.django-rest-framework.org/api-guide/authentication/: `curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'` – Eugene Morozov May 29 '17 at 07:41
  • thanks again. But my point is, when I'm using curl/httpie/postman they are working. I'm not getting what would happen when a user fills a log-in form and clicks on log-in. Will that result in a call to the "gettoken" url, where it'll get a token and then will it redirect to the "landing" view? I'm not getting this part. Hope you ould help. Thanks again btw!! I'm trying to re-read a few things as well!!! – Sayantan May 29 '17 at 07:54
  • 1
    Users are not supposed to interact with the REST API directly. If you need that, DRF supports session authentication which you disabled in your settings. Just enable it and use DRF browsable API in your browser. Users that visit your site shouldn't interact with the API directly. If you're writing a single page application, your application should obtain token from the respective DRF API endpoint and pass the token along with the requests. – Eugene Morozov May 29 '17 at 08:29
  • thanks a lot!!! This makes a lot of sense now. We're working on the API for the android app and it makes sense that the webapp has session authentication. But, if otherwise, can't we use token based authorization for webapp? (Like why have both session and token authentication, if we can have just token authentication)? Either ways, THANK YOU!! – Sayantan May 29 '17 at 08:44
  • You can have token authentication in your application. You just need to obtain token from the DRF auth view using `XMLHttpRequest` and append the obtained token to every subsequent `XMLHttpRequest`. Or just use Django session authentication. Why would you want token auth for a plain web application? It won't work with normal pages and forms. – Eugene Morozov May 29 '17 at 09:22
  • as u asked: Reason for using token authentication: for every request, one need not do a db lookup if using token. So, if some part of the app uses token, I was thinking if making token applicable everywhere. – Sayantan May 29 '17 at 09:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/145357/discussion-between-eugene-morozov-and-s-gangs). – Eugene Morozov May 29 '17 at 09:48
0
REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',  # enables simple command line authentication
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
        )
    }

Adding 'rest_framework.authentication.SessionAuthentication' has solved this issue most of the time.

Alternative

You can use:

'DEFAULT_AUTHENTICATION_CLASSES': (
       'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
   ),

But now, in order to access protected api urls you must include the Authorization: JWT header.

Federico Grandi
  • 6,785
  • 5
  • 30
  • 50
Syed Faizan
  • 958
  • 9
  • 19
0

If it works with curl or postman, this indicates it is not an issue with the backend. This is certainly an issue with the client side code. Have you had a look at your request to your REST api? I would recommend that and ensure the token is being passed in the headers and is formatted correctly.

Dap
  • 2,309
  • 5
  • 32
  • 44