Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework JWT

I'm using Django Rest Framework to build the API for my application and would like to implement DjangoRestFramework-JWT for token authentication. The steps seem simple enough but when I test the endpoint I get a 500 error. The terminal output is a ton of html saying a csrf_token wasn't provided. Code & errors are below. Your help is greatly appreciated.

curl -X POST -d "username=admin&password=123abc" http://127.0.0.1:8000/api/token/auth/

CSRF Error

<!DOCTYPE html>
<html lang="en">
    <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <meta name="robots" content="NONE,NOARCHIVE">
      <title>403 Forbidden</title>
    </head>
    <body>
        <div id="summary">
            <h1>Forbidden <span>(403)</span></h1>
            <p>CSRF verification failed. Request aborted.</p>
            <p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
            <p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for &#39;same-origin&#39; requests.</p>
        </div>

        <div id="info">
            <h2>Help</h2>
            <p>Reason given for failure:</p>
            <pre>CSRF cookie not set.</pre>
            <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when <a href="https://docs.djangoproject.com/en/1.8/ref/csrf/">Django's CSRF mechanism</a> has not been used correctly.  For POST forms, you need to ensure:</p>
            <ul>
                <li>Your browser is accepting cookies.</li>
                <li>The view function passes a <code>request</code> to the template's <a href="https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a> method.</li>
                <li>In the template, there is a <code>{% csrf_token %}</code> template tag inside each POST form that targets an internal URL.</li>
                <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use <code>csrf_protect</code> on any views that use the <code>csrf_token</code> template tag, as well as those that accept the POST data.</li>
            </ul>

            <p>You're seeing the help section of this page because you have <code>DEBUG = True</code> in your Django settings file. Change that to <code>False</code>, and only the initial error message will be displayed.  </p>
            <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
        </div>
    </body>
</html>

Settings.py

INSTALLED_APPS = (
    ...
    'rest_framework',
    ...
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'app.utils.jwt_response_payload_handler',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000),
}

Global Urls.py

urlpatterns = patterns(
    ...
    url(r'^api/auth/token/$', 'rest_framework_jwt.views.obtain_jwt_token'),
    url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^api/', include(router.urls)),
)
like image 555
Sklan10 Avatar asked Jul 29 '15 05:07

Sklan10


1 Answers

You are getting this error because of SessionAuthentication class defined in the DRF settings.

DRF's SessionAuthentication uses Django's session framework for authentication which requires CSRF to be checked. DRF enforces CSRF check for authenticated users. This means that only authenticated requests require CSRF tokens and anonymous requests may be sent without CSRF tokens.

Now you are making a request from an authenticated user and as per the order defined in DEFAULT_AUTHENTICATION_CLASSES in the settings.py file, SessionAuthentication is called first. It checks if the request is coming from an authenticated user and if it is, then it must have CSRF token set in the request headers. Since you are not sending CSRF Token in the request, so it returns an error.

You need to pass CSRF Token in the headers while making the request. You can do that via --header/-Hoption.

--header "X-CSRFToken: {token_value}" # using '--header'
-H "X-CSRFToken: {token_value}" # using '-H' 

You can change your request to something like:

curl --header "X-CSRFToken: {token_value}" -X POST -d "username=admin&password=123abc" http://127.0.0.1:8000/api/token/auth/

Another method:

There is another option where you can create a Custom Session Authentication class which does not enforces this CSRF check for authenticated users.

SessionCsrfExemptAuthentication class below does not enforces the CSRF token check for authenticated users.

from rest_framework.authentication import SessionAuthentication 

class SessionCsrfExemptAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

You can then define this authentication class in your settings like:

'DEFAULT_AUTHENTICATION_CLASSES': (
        # Replace DRF session authentication class with our custom authentication class
        'rest_framework.authentication.SessionCsrfExemptAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
like image 143
Rahul Gupta Avatar answered Sep 29 '22 16:09

Rahul Gupta