CSRF with Django, React+Redux using Axios - django

This is an educational project, not for production. I wasn't intending to have user logins as part of this.
Can I make POST calls to Django with a CSRF token without having user logins? Can I do this without using jQuery? I'm out of my depth here, and surely conflating some concepts.
For the JavaScript side, I found this redux-csrf package. I'm not sure how to combine it with my POST action using Axios:
export const addJob = (title, hourly, tax) => {
console.log("Trying to addJob: ", title, hourly, tax)
return (dispatch) => {
dispatch(requestData("addJob"));
return axios({
method: 'post',
url: "/api/jobs",
data: {
"title": title,
"hourly_rate": hourly,
"tax_rate": tax
},
responseType: 'json'
})
.then((response) => {
dispatch(receiveData(response.data, "addJob"));
})
.catch((response) => {
dispatch(receiveError(response.data, "addJob"));
})
}
};
On the Django side, I've read this documentation on CSRF, and this on generally working with class based views.
Here is my view so far:
class JobsHandler(View):
def get(self, request):
with open('./data/jobs.json', 'r') as f:
jobs = json.loads(f.read())
return HttpResponse(json.dumps(jobs))
def post(self, request):
with open('./data/jobs.json', 'r') as f:
jobs = json.loads(f.read())
new_job = request.to_dict()
id = new_job['title']
jobs[id] = new_job
with open('./data/jobs.json', 'w') as f:
f.write(json.dumps(jobs, indent=4, separators=(',', ': ')))
return HttpResponse(json.dumps(jobs[id]))
I tried using the csrf_exempt decorator just to not have to worry about this for now, but that doesn't seem to be how that works.
I've added {% csrf_token %} to my template.
This is my getCookie method (stolen from Django docs):
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;
}
I've read that I need to change the Axios CSRF info:
var axios = require("axios");
var axiosDefaults = require("axios/lib/defaults");
axiosDefaults.xsrfCookieName = "csrftoken"
axiosDefaults.xsrfHeaderName = "X-CSRFToken"
Where do I stick the actual token, the value I get from calling getCookie('csrftoken')?

This Q&A is from 2016, and unsurprisingly I believe things have changed. The answer continues to receive upvotes, so I'm going to add in new information from other answers but leave the original answers as well.
Let me know in the comments which solution works for you.
Option 1. Set the default headers
In the file where you're importing Axios, set the default headers:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
Option 2. Add it manually to the Axios call
Let's say you've got the value of the token stored in a variable called csrfToken. Set the headers in your axios call:
// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...
Option 3. Setting xsrfHeaderName in the call:
Add this:
// ...
method: 'post',
url: '/api/data',
data: {...},
xsrfHeaderName: "X-CSRFToken",
// ...
Then in your settings.py file, add this line:
CSRF_COOKIE_NAME = "XSRF-TOKEN"
Edit (June 10, 2017): User #yestema says that it works slightly different with Safari[2]
Edit (April 17, 2019): User #GregHolst says that the Safari solution above does not work for him. Instead, he used the above Solution #3 for Safari 12.1 on MacOS Mojave. (from comments)
Edit (February 17, 2019): You might also need to set[3]:
axios.defaults.withCredentials = true
Things I tried that didn't work: 1

I've found out, that axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
and CSRF_COOKIE_NAME = "XCSRF-TOKEN"
DOESN'T WORK IN APPLE Safari on Mac OS
The solution for MAC Safari is easy, just change XCSRF-TOKEN to csrftoken
So, in js-code should be:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
In settings.py:
CSRF_COOKIE_NAME = "csrftoken"

This configuration works for me without problems Config axios CSRF django
import axios from 'axios'
/**
* Config global for axios/django
*/
axios.defaults.xsrfHeaderName = "X-CSRFToken"
axios.defaults.xsrfCookieName = 'csrftoken'
export default axios

After spending too many hours researching, and implementing the above answer, I found my error for this problem! I have added this answer to be supplemental of the accepted answer. I had set up everything as mentioned, but the gotcha for me was actually in the browser itself!
If testing locally, make sure you are accessing react through 127.0.0.1 instead of localhost! localhost handles request headers differently and doesn't show the CSRF tokens in the header response, where as 127.0.0.1 will! So instead of localhost:3000 try 127.0.0.1:3000!
Hope this helps.

The "easy way" almost worked for me. This seems to work:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
And in the settings.py file:
CSRF_COOKIE_NAME = "XCSRF-TOKEN"

You could add the Django-provided CSRF token manually into all of your post requests, but that's annoying.
From the Django docs:
While the above method (manually setting CSRF token) can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many JavaScript frameworks provide hooks that allow headers to be set on every request.
The docs have code you can use to pull the CSRF token from the CSRF token cookie and then add it to the header of your AJAX request.

There is actually a really easy way to do this.
Add axios.defaults.xsrfHeaderName = "X-CSRFToken"; to your app config and then set CSRF_COOKIE_NAME = "XSRF-TOKEN" in your settings.py file. Works like a charm.

For me, django wasn't listening to the headers that I was sending. I could curl into the api but couldn't access it with axios. Check out the cors-headers package... it might be your new best friend.
I fixed it by installing django-cors-headers
pip install django-cors-headers
And then adding
INSTALLED_APPS = (
...
'corsheaders',
...
)
and
MIDDLEWARE = [ # Or MIDDLEWARE_CLASSES on Django < 1.10
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
into my settings.py
I also had
ALLOWED_HOSTS = ['*']
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_EXPOSE_HEADERS = (
'Access-Control-Allow-Origin: *',
)
in my settings.py although that is probably overkill

In addition to what yestema said (and echoed by krescruz, cran_man, Dave Merwin et. al), You also need:
axios.defaults.withCredentials = true

Related

Difference between fetch and postman's results

I have a simple login backend which has two routes login and get_user. After a user is logged in, a cookie is set such that it enables other routes like get_user. I tested this backend with Postman and after correct login, the cookie is set and get_user responds with user's data.
However, when I try to use fetch or axios in React and JS, I get problems. After I fetch login, I can see the cookie is sent, however, fetch get_user acts like cookie is not set at all.
I provided a minimal example to show that server side session somehow doesn't work with fetch:
Frontend:
<!DOCTYPE html>
<html>
<body>
<h1> Set value: </h1> <h2 id="set_value"></h2>
<h1> Get value: </h1> <h2 id="get_value"></h2>
</body>
<script>
// ..\..\Flask_general_framework\backend\venv\Scripts\Activate.ps1
async function Set_user_fetch()
{
// Get user
let user = await fetch('http://127.0.0.1:5000/set/gggg', {
'method': 'GET',
//credentials: 'include',
mode: 'cors',
credentials: "same-origin",
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch()
{
let user = await fetch('http://127.0.0.1:5000/get', {
'method': 'GET',
//credentials: 'include',
credentials: "same-origin",
mode: 'cors',
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
Set_user_fetch().then( () => {
Get_user_fetch();
});
</script>
</html>
Backend:
from re import I
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
import redis
import datetime as dt
app = Flask(__name__)
CORS(app, supports_credentials=True)
app.config['SECRET_KEY'] = 'super secret key'
#app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT'] = True
#app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:9876')
app.config['PERMANENT_SESSION_LIFETIME'] = dt.timedelta(days=7).total_seconds()
server_session = Session()
server_session.init_app(app)
#app.route('/set/<value>', methods=['GET', 'POST'])
def set_value(value):
session['value'] = value
return {"value": value}
#app.route('/get', methods=['GET', 'POST'])
def get_value():
return {"value": session.get('value', 'None')}
app.run(host='127.0.0.1', port=5000, debug=True)
Server Side
In order to support cross-site cookies in modern browsers, you need to configure your server to use the Set-Cookie attribute SameSite=None (see Flask-specific example here). Unfortunately, this also requires the Secure attribute and an HTTPS enabled server.
For local development, you can get around this by serving your client and server on the same hostname, eg localhost with SameSite=Lax (or omitting SameSite which defaults to "Lax").
By "same hostname" I mean that if your frontend code makes requests to localhost:5000, you should open it in your browser at http://localhost:<frontend-port>. Similarly, if you make requests to 127.0.0.1:5000, you should open it at http://127.0.0.1:<frontend-port>.
Lax same-site restrictions don't come into play if only the ports differ.
Client Side
You have a few problems here...
You're sending headers in your request that do not belong there. Access-Control-Allow-* are response headers that must come from the server.
You set credentials to same-origin but are sending a request to a different host. To use cookies, you need to set credentials to "include". See Request.credentials.
You have no error handling for non-successful requests.
You're also setting a lot of redundant properties and headers and can trim down your code significantly.
async function Set_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/set/gggg", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/get", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
If you were using Axios, you would set the withCredentials config to true
axios.get("http://127.0.0.1:5000/set/gggg", {
withCredentials: true
});

Can I only return data only if front-end webapp calls django app?

I have a Django app that "wraps" my Flickr API. The point of the wrapper is to hide the API key.
views.py:
...
def flickrApiGetPhotoSet(request):
flickr_user_id = get_secret('flickr_user_id')
flickr_photoset_id = get_secret('flickr_photoset_id')
flickr_api_key = get_secret('flickr_api_key')
response = requests.get('https://api.flickr.com/services/rest/?',
params = {
'method': 'flickr.photosets.getPhotos',
'user_id': flickr_user_id,
'photoset_id': flickr_photoset_id,
'api_key': flickr_api_key,
'format': 'json',
'nojsoncallback': 1,
'extras': 'date_upload'
}
)
return HttpResponse(response, content_type='application/json')
While is Api-key is not exposed, the response is freely viewable in a browser.
e.g. mywebsite_domain.com/wrapper/flickrApiGetPhotoSet/
The 2 of the 3 pieces of data I was trying to hide (flickr_user_id, flickr_photoset_id) are exposed.
I have these values in my settings.py:
ALLOWED_HOSTS = ['localhost', '127.0.0.1','.mywebsite_domain.com','192.168.1.82']
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'http://localhost',
'https://www.mywebsite_domain.com',
)
On the front-end I'm using Axios to call the API: mywebsite_domain.com/wrapper/flickrApiGetPhotoSet/
Ideally, I'd like to only allow Axios to get the 'wrapped' response and get an error in the browser.
I'm not sure if there's something I need to update in (1) the Django API or (2) my 'wrapper' app or (3) something else.
Thanks in advance!
Larry

How to add cookies to vue-apollo request?

I use vue-cli-plugin-apollo and I want to send language chosen by user from frontend to backend via cookie.
As a vue-apollo.js I use the next template
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
// Install the vue plugin
Vue.use(VueApollo)
// Name of the localStorage item
const AUTH_TOKEN = 'apollo-token'
// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:4000/graphql'
// Files URL root
export const filesRoot = process.env.VUE_APP_FILES_ROOT || httpEndpoint.substr(0, httpEndpoint.indexOf('/graphql'))
Vue.prototype.$filesRoot = filesRoot
// Config
const defaultOptions = {
// You can use `https` for secure connection (recommended in production)
httpEndpoint,
// You can use `wss` for secure connection (recommended in production)
// Use `null` to disable subscriptions
wsEndpoint: process.env.VUE_APP_GRAPHQL_WS || 'ws://localhost:4000/graphql',
// LocalStorage token
tokenName: AUTH_TOKEN,
// Enable Automatic Query persisting with Apollo Engine
persisting: false,
// Use websockets for everything (no HTTP)
// You need to pass a `wsEndpoint` for this to work
websocketsOnly: false,
// Is being rendered on the server?
ssr: false,
// Override default apollo link
// note: don't override httpLink here, specify httpLink options in the
// httpLinkOptions property of defaultOptions.
// link: myLink
// Override default cache
// cache: myCache
// Override the way the Authorization header is set
// getAuth: (tokenName) => ...
// Additional ApolloClient options
// apollo: { ... }
// Client local data (see apollo-link-state)
// clientState: { resolvers: { ... }, defaults: { ... } }
}
// Call this in the Vue app file
export function createProvider (options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options,
})
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
// fetchPolicy: 'cache-and-network',
},
},
errorHandler (error) {
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
},
})
return apolloProvider
}
taken from here. All options are shown here.
I have seen in different github discussions that cookies must be placed inside headers, for example here. Then I found, that apollo-link-http has headers option, so at the end I tried different variations of ...:
httpLinkOptions: {
headers: {
// Tried something like:
cookie[s]: 'language=en; path=/;'
// and something like:
cookie[s]: {
language: 'en'
}
}
}
but no luck.
In case of cookieS I receive Error: Network error: Failed to fetch.
In case of cookie, request is sent without issues, but backend does not see language cookie.
I double-checked backend using Postman and in this case backend receives request with manually added language cookie.
Could anyone help me?
Found solution.
FRONT END SETTINGS
Create cookie:
export function languageCookieSet (lang) {
document.cookie = `language=${lang}; path=/;`
}
Add httpLinkOptions to defaultOptions of vue-apollo.js.
const defaultOptions = {
...
httpLinkOptions: {
credentials: 'include'
},
...
BACKEND SETTINGS
As a backend I use Django (currently v2.2.7).
For development we need to use django-cors-headers
My development.py now looks like:
from .production import *
CORS_ORIGIN_WHITELIST = (
'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True
INSTALLED_APPS += ['corsheaders']
MIDDLEWARE.insert(0, 'corsheaders.middleware.CorsMiddleware')
Add to production.py:
LANGUAGE_COOKIE_NAME = 'language'
The default value of LANGUAGE_COOKIE_NAME is django_language, so if it is suitable for you, change
document.cookie = `language=${lang}; path=/;`
to
document.cookie = `django_language=${lang}; path=/;`
Now in backend we can get frontend language:
import graphene
from django.contrib.auth import get_user_model
from django.utils.translation import gettext as _
from .views import user_activation__create_email_confirmation
User = get_user_model()
class UserRegister(graphene.Mutation):
"""
mutation {
userRegister(email: "test#domain.com", password: "TestPass") {
msg
}
}
"""
msg = graphene.String()
class Arguments:
email = graphene.String(required=True)
password = graphene.String(required=True)
def mutate(self, info, email, password):
request = info.context
# Here we get either language from our cookie or from
# header's "Accept-Language" added by Browser (taken
# from its settings)
lang = request.LANGUAGE_CODE
print('lang:', lang)
if User.objects.filter(email=email).exists():
# In our case Django translates this string based
# on the cookie's value (the same as "request.LANGUAGE_CODE")
# Details: https://docs.djangoproject.com/en/2.2/topics/i18n/translation/
msg = _('Email is already taken')
else:
msg = _('Activation link has been sent to your email.')
user = User(email=email)
user.set_password(password)
user.save()
user_activation__create_email_confirmation(info.context, user)
return UserRegister(msg=msg)
Note: I have not tested yet these changes in production, but in production I use just one server where frontend and backend live behind nGinx and this is the reason why CORS settings live in development.py instead of production.py. Also in production credentials: 'include' possibly could be changed to credentials: 'same-origin' (ie more strict).

request.user.is_authenticated() consistently returns false (Django)

The problem I'm having
I'm currently using Django v1.9 as a back-end for my Angular2 app (I'm not using the Django REST Framework yet, just using Django's authentication system and dumping JSON)
I'm trying to authenticate the user, log them in, and then allow them to edit their profile.
The first two steps seem to work. However, I'm having some trouble with request.user.is_authenticated() - it consistently returns false, even though I have called the login() function on the user previously.
The part that seems to work
#csrf_exempt
def userlogin(request):
body_unicode = request.body.decode('utf-8')
body = json.loads(body_unicode)
input_u = body['uname']
input_p = body['pword']
worked = False
user = authenticate(username=input_u, password=input_p)
if user is not None:
login(request, user)
context = { "login_data" : { "logged_in" : True, "user_id" : user.id } }
else:
context = { "login_data" : { "logged_in" : False, "user_id" : 0 } }
return HttpResponse(json.dumps(context), content_type="application/json")
The part I'm struggling with
#ensure_csrf_cookie
def user(request):
is_auth = False
if request.user.is_authenticated():
is_auth = True
context = { "is_auth" : is_auth }
return HttpResponse(json.dumps(context), content_type="application/json")
Note: I'm using is_authenticated() (function) and not is_authenticated (property) as I'm on Django v1.9 and not v.1.10 (source). I was previously making the mistake of checking for the property and it always returned true, but when I'd try to return the ID of the user from the request object it would always be null.
I keep getting false here. This is the first time I've tried auth with Django, so I just wanted to ask some questions here:
Am I doing something terribly wrong? I think I have all of the stuff I need in my settings:
INSTALLED_APPS = [
'search.apps.SearchConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders'
]
I also have 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware' in my middleware.
How exactly does Django know that the user is authenticated? I assume that since I have sessions activated, it checks for the session cookie. However, I suspect this could be the issue. On inspection, I had a cookie placed this afternoon for localhost. However, since then I've signed in and not been able to update it. I even tried Django's in-built test cookie function (source) but it wouldn't work when I tested it. My settings should be okay, I have the following:
INSTALLED_APPS = ['django.contrib.sessions']
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
MIDDLEWARE_CLASSES = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
...
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
...]
I suspect I'm missing something obvious but I've been reading other threads for a while now with no luck.
Thanks, guys!
Nick
Found the solution to my own problem from another problem I was having.
The issue is that Angular wasn't sending cookies to the Django server. As Angular is using CORS by default, I had to add { withCredentials : true } to my RequestOptions object.
Example:
editUser(userdata) {
console.log("UserService: createUser function called");
console.log(JSON.stringify(userdata));
if(this.validateData(userdata)) {
let headers = new Headers({
'Content-Type': 'application/json',
'X-CSRFToken': this.getCookie('csrftoken')
});
let options = new RequestOptions({ headers: headers, withCredentials: true });
return this._http
.post(
this._editUserUri,
JSON.stringify(userdata),
options)
.map(res => {
console.log(res.json());
return res.json();
})
}
}
Explained thoroughly here: Angular2 and Django: CSRF Token Headache
You don't have the contrib.auth app in INSTALLED_APPS.

Getting 403 on POST

I am learning to work with Django Rest Framework and following the tutorial. I have create a simple index based on the tutorial, that works for GET, but not for POST:
#api_view(['GET','POST'])
def game_list(request):
if request.method == 'GET':
games = Game.objects.all()
serializer = GameSerializer(games, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = GameSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I set the default settings to AllowAny:
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny'
]
}
but I still get a HTTP 403 when I try to POST anything, using the Firefox RESTClient. I read that I have to add a X-CSRFToken header and cookie for this to work, but I do not have those.
From documentation:
By default, a ‘403 Forbidden’ response is sent to the user if an incoming request fails the checks performed by CsrfViewMiddleware. This should usually only be seen when there is a genuine Cross Site Request Forgery, or when, due to a programming error, the CSRF token has not been included with a POST form.
Also, as stated if the official documentation, CSRF is enabled by default, and you need to add a X-CSRFToken in your AJAX requests.
Here is the code from the documentation:
// using jQuery
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 = jQuery.trim(cookies[i]);
// 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;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
Take in mind that the documentation suggest to use ajaxSetup method from jquery, which is not a recommended way to do it because it can alter the way that others scripts uses the ajax function, so it's better to add the specific CSRF's code in your custom JS code like this:
$.ajax({
method: 'POST',
url: 'your.url.com/',
beforeSend: function(xhr, settings) {
if (!WU._csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
success: function(msg)
{}
});
Reference: https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax