10

I have an Android client app that tries to authenticate with a Django + DRF backend. However, when I try to login, I get the following response:

403: CSRF Failed: CSRF token missing or incorrect.

The request is sent to http://localhost/rest-auth/google/ with the following body:

access_token: <the OAuth token from Google>

What could cause this? The client doesn't have a CSRF token since the POST to authenticate is the first thing to happen between the client and the server. I checked out a lot of the past questions with the same problem, but I couldn't find any solutions.

The relevant settings on the Django side are like this:

AUTHENTICATION_BACKENDS = (
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend"
)

TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.request",
    "django.contrib.auth.context_processors.auth",
    "allauth.account.context_processors.account",
    "allauth.socialaccount.context_processors.socialaccount"
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
    'allauth.socialaccount.providers.google',

    'django.contrib.admin',

    # REST framework
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    'rest_auth.registration',
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ( 
        'rest_framework.permissions.IsAuthenticated'
    ),
}
manabreak
  • 5,415
  • 7
  • 39
  • 96

4 Answers4

12

Why are you getting this error?

As you have not defined the AUTHENTICATION_CLASSES in your settings, DRF uses the following default authentication classes.

'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication'
)

Now, SessionAuthentication enforces the use of CSRF Token. If you don't pass a valid CSRF token, then 403 error is raised.

If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such asPUT, PATCH, POST or DELETE requests.

What you need to do then?

Since you are using TokenAuthentication, you need to explicitly define it in the AUTHENTICATION_CLASSES in your DRF settings. This should resolve your CSRF token issue.

Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • You say we have to pass a valid CSRF token. Where do I find this token? In my case the problem come when I try to login with `rest-auth/login/` endpoint. But it's the first contact between the client and the server, so the client desn't know the token. – Ben Jan 04 '17 at 13:37
  • 1
    Check this Django docs [link](https://docs.djangoproject.com/en/1.10/ref/csrf/#ajax) to get CSRF Token for Ajax requests. – Rahul Gupta Jan 04 '17 at 14:34
  • Thank you @rahul-gupta but I'm not on a web environment. I'm on a c# environment (Xamarin). But in any case, in a rest<>client application, the client can not know the csrf if the server has not told it to him. – Ben Jan 04 '17 at 17:31
  • Then you should use a different authentication class as `SessionAuthentication` requires a CSRF token to be sent. You will need to explicitly define that particular authentication class in your settings or your particular view. – Rahul Gupta Jan 04 '17 at 17:59
9

Silly me, I missed the TokenAuthentication framework from the REST settings:

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    )
}

Now it works just as intended.

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
manabreak
  • 5,415
  • 7
  • 39
  • 96
1

You need to pass the csrf token while sending the request lets see the given code:

function getCookie(name) {
 var cookieValue = null;
 if (document.cookie && document.cookie !== '') {
     var cookies = document.cookie.split(';');
     for (var i = 0; i < cookies.length; i++) {
         var cookie = cookies[i].trim();
         // Does this cookie string begin with the name we want?
         if (cookie.substring(0, name.length + 1) === (name + '=')) {
             cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
             break;
         }
     }
 }
 return cookieValue;
}

const check = () => {

 const endPoint = '/api/check/'
 const csrftoken = getCookie('csrftoken'); // getting the cookie to pass as csrf token

 return fetch(baseUrl + endPoint,{
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': csrftoken
        },
        
    })
    .then(response => {
        if (response.ok) {
            return response
        }
        else { 
            if (response.status === 401 && response.statusText === 'Unauthorized'){ // if it is un authorized then dispatch the logout
                dispatch(logout());
            }
            var error = new Error('Error: '+ response.status + ': ' + response.statusText); 
            error.response = response;  
            throw error; 
        }

    },               
    error => {
        var errmess = new Error(error.message); 
        throw errmess;
    })
    .then( data => data.json())
    .then(data => console.log(data))
    .catch(error => console.log(error.message)); 
   }

Guys one thing remember no matter you are sending the GET PUT or POST request you need to send the csrf token this is for the security purpose.

I hope my answer help you.

sahil
  • 11
  • 1
0

This error can happen due to improper implementation of urlpatterns including static URL paths. To debug,

  1. Check whether you have set authentication classes correctly.

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            <your auth classes>
        )
    }
    
  2. Check whether the paths for the class-based views are properly defined.

    urlpatterns = [
        '<your URL path>', <your class-based view>.as_view(), name='<path name>')
    ]
    
  3. Check whether you have defined urlpatterns for static and media files and also have defined them correctly in setting.py.

    In settings.py:

    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    # STATIC_ROOT = '/assets'
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]
    
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    

    In urls.py:

    urlpatterns = [
        ...
    ]
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    
Avishka Dambawinna
  • 1,180
  • 1
  • 13
  • 29