Django, CORS, CSRF - am I doing it right?

Viewed 423

My setup (local) is the following:

  • Vue.js running on localhost:8080 (npm run serve)
  • REST API built with Django running on localhost:8000 (./manage-py runserver)

In order to enable this to work I've made the following additions:

ALLOWED_HOSTS = [
...
'localhost',
'localhost:8000',
'localhost:8080',
]

INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders',
]

MIDDLEWARE = [
    ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

CORS_ORIGIN_WHITELIST = (
    'localhost:8080',
    'localhost:8000',
)

CORS_ALLOW_CREDENTIALS = True

from corsheaders.defaults import default_headers

CORS_ALLOW_HEADERS = default_headers + (
    'credentials',
)

One of my API functions:

@ensure_csrf_cookie
def try_login(request):
  # this is just to get the initial CSRF token:
  if request.method == "GET" or request.method == "OPTIONS":
    return JsonResponse({'status': 'ok'}) 
  # else, an actual login request:
  else:
    data = JSONParser().parse(request)
    user = authenticate(request, username=data['user'] , password=data['pass'])
    if user is not None:
        login(request, user)
        return JsonResponse({'login_succ': 'ok'});
    else:
        return JsonResponse({'login_succ': 'fail'});

Finally, in Vue:

api: function(endpoint, method, data) {
    var headers = new Headers();
    headers.append('content-type', 'application/json');
    if (... this is not the first request ever ...) 
    {
      csrftoken = document.cookie.replace(/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/, "$1");
      headers.append('X-CSRFToken', csrftoken);
    }
    method = method || 'GET';
    var config = {
      method: method,
      body: data !== undefined ? JSON.stringify(data) : null,
      headers: headers,
    };

    config['credentials'] = 'include';

    return fetch(endpoint, config)
      .then(response => response.json())
      .catch((error) => { console.log(...); });
  },

trylogin: function() {
    // initial request: just to get the CSRF token
    this.api(".../login/", "GET").then(
      response => {
          this.api(".../login/", "POST", {'username': ..., 'password': ...}  ).then(
          response => {
            if ("login_succ" in response && res["login_succ"] == "ok")
            {}  // user is logged in
          }
        );
      }
    );

   }

What happens now, afaiu, is that my initial API request (which does not have to be pointed to the endpoint equal to the subsequent POST request, right?) gets the CSRF token as a cookie. Every subsequent request reads this cookie and sets the X-CSRFToken header. The cookie itself is also being sent in the subsequent requests. I do not understand why is the token needed in both places.

Is this approach correct? Is everything I've done necessary? (Are there redundant parts?) I'm especially interested in the way that I should acquire the token in the first place, and in general with the token's lifecycle.

Thank you.

0 Answers
Related