AngularJS single-user web application on subdomain A, talking to the Jang JSON (REST) ββAPI in subdomain B using CORS and CSRF protection
Since I'm currently working on a similar setup and struggled to get CORS to work properly in conjunction with CSRF protection, I wanted to share my own knowledge here.
Setup - SPA and API are in different subdomains of the same domain:
- AngularJS (1.2.14) One-page web application on the subdomain app.mydomain.com
- Django application (1.6.2) implements the JSON REST API on the api.mydomain.com subdomain
The AngularJS application is served through the Django application in the same project as the Django APP API, so it sets the CSRF Cookie. See Also, for example, How to run multiple websites from a single Django project.
Django API App - in order to work with CORS and CSRF, I needed to do the following on the API backend.
In settings.py for this application (extension of the Django settings.py project):
- Add the corsheaders app and middleware and CSRF middleware:
INSTALLED_APPS = ( ... 'corsheaders', ... ) MIDDLEWARE_CLASSES = ( ... 'django.middleware.csrf.CsrfViewMiddleware', ... 'corsheaders.middleware.CorsMiddleware', )
Also see Django CORS headers on GitHub
- Add a domain for SPA Webapp to CORS_ORIGIN_WHITELIST
CORS_ORIGIN_WHITELIST = [ ... 'app.mydomain.com', ... ]
- Set CORS_ALLOW_CREDENTIALS to True. This is important, if you do not, the CSRF cookie will not be sent with the request
CORS_ALLOW_CREDENTIALS = True
Add the csrf_cookie provide decorator to your views handling JSON API requests:
from django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def myResource(request): ...
Django app for AngularJS - An AngularJS app is served through a Django app in the same project. This Django app is configured to set the CSRF Cookie. Then, the CSRF token from the cookie is used to request the API (which is thus executed as part of the same Django project).
Note that almost all files associated with an AngularJS application are only static files from the point of view of Django. A Django application must serve index.html in order to set a cookie.
In settings.py for this application (again the extension of the Django settings.py project), set CSRF_COOKIE_DOMAIN so that subdomains can also use them:
CSRF_COOKIE_DOMAIN = ".mydomain.com"
In views.py, I only need to display the AngularJS index.html file again using the make_csrf_cookie decorator:
from django.shortcuts import render from django.views.decorators.csrf import ensure_csrf_cookie
Sending requests to the API using AngularJS. In the AngularJS application configuration, set the following $ httpProvider defaults:
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; $httpProvider.defaults.withCredentials = true;
Again, pay attention to withCredentials, this ensures that the CSRF Cookie will be used in the request.
Below I will show how you can send requests to api using the AngularJS $ http and jQuery service:
$http.post("http://api.mydomain.com/myresource", { field1 : ..., ... fieldN : ... }, { headers : { "x-csrftoken" : $cookies.csrftoken } });
Also see the ngCookies module .
Using jQuery (1.11.0):
$.ajax("http://api.mydomain.com/myresource", { type: 'POST', dataType : 'json', beforeSend : function(jqXHR, settings) { jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie()); }, cache : false, contentType : "application/json; charset=UTF-8", data : JSON.stringify({ field1 : ..., ... fieldN : ... }), xhrFields: { withCredentials: true } });
Hope this helps!