7π
So I found my own solution to this, seems to work great.
This is the new snippets of my code:
Backend API LoginView ( added a decorator forcing the csrf token to be added to the body )
class LoginView(APIView):
renderer_classes = (JSONPRenderer, JSONRenderer)
@method_decorator(ensure_csrf_cookie)
def post(self, request, format=None):
c = {}
c.update(csrf(request))
serializer = LoginSerializer(data=request.DATA)
if serializer.is_valid():
userAuth = authenticate(username=serializer.data['username'], password=serializer.data['password'])
if userAuth:
if userAuth.is_active:
login(request, userAuth)
loggedInUser = AuthUserProfile.objects.get(pk=1)
serializer = UserProfileSerializer(loggedInUser)
user = [serializer.data, {'isLogged': True}]
else:
user = {'isLogged': False}
return Response(user, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
AngularJS Client side ( add token to the request header )
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
Server side settings file ( Specificly for django-cors-headers )
First 5 are added by default, but you need to add βX-CSRFTokenβ to allow such a header from the client to the API using CORS, else the post will be denied.
CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'X-CSRFToken'
)
Thats it!
37π
AngularJS Single Page Web Application on Sub-domain A, talking to a Django JSON (REST) API on Sub-domain B using CORS and CSRF protection
Since Iβm currently working on a similar setup and was battling to get CORS to work properly in combination with CSRF protection, I wanted to share my own learnings here.
Setup β The SPA and the API are both on different sub-domains of the same domain:
- AngularJS (1.2.14) Single Page Web Application on sub-domain app.mydomain.com
- Django App (1.6.2) implements a JSON REST API on sub-domain api.mydomain.com
The AngularJS app is served through a Django App in the same project as the Django API APP such that it sets a CSRF Cookie. See, for instance, also How to run multiple websites from one Django project
Django API App β In order to get CORS and CSRF protection working I needed to do the following at the API backend.
In settings.py for this app (an extension of the Django project settings.py):
- Add the corsheaders app and middleware and the CSRF middleware:
INSTALLED_APPS = ( ... 'corsheaders', ... ) MIDDLEWARE_CLASSES = ( ... 'django.middleware.csrf.CsrfViewMiddleware', ... 'corsheaders.middleware.CorsMiddleware', )
Also see Django CORS headers on GitHub
- Add the domain for the SPA Webapp to the CORS_ORIGIN_WHITELIST
CORS_ORIGIN_WHITELIST = [ ... 'app.mydomain.com', ... ]
- Set CORS_ALLOW_CREDENTIALS to True. This is important, if you donβt do this, no CSRF cookie will be sent with the request
CORS_ALLOW_CREDENTIALS = True
Add the ensure_csrf_cookie decorator to your views handling the JSON API requests:
from django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def myResource(request): ...
Django App for AngularJS β The AngularJS app is served through a Django App in the same project. This Django App is set-up to set a CSRF Cookie. The CSRF token from the cookie is then used for requests to the API (which thus runs as a part of the same Django project).
Note that almost all files related to the AngularJS application are just static files from the Django perspective. The Django App only needs to serve the index.html to set the cookie.
In settings.py for this app (again an extension of the Django project settings.py), set the CSRF_COOKIE_DOMAIN such that subdomains can also use them:
CSRF_COOKIE_DOMAIN = β.mydomain.comβ
In views.py, I only need to render the AngularJS index.html file, again using the ensure_csrf_cookie decorator:
from django.shortcuts import render from django.views.decorators.csrf import ensure_csrf_cookie # Create your views here. @ensure_csrf_cookie def index(request): return render(request, 'index.html')
Sending requests to the API using AngularJS β In the AngularJS App config set the following $httpProvider defaults:
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; $httpProvider.defaults.withCredentials = true;
Again, take note of the withCredentials, this ensures that the CSRF Cookie is used in the request.
Below I show how you can make requests to the api using the AngularJS $http service and JQuery:
$http.post("http://api.mydomain.com/myresource", { field1 : ..., ... fieldN : ... }, { headers : { "x-csrftoken" : $cookies.csrftoken } });
Also see 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 } });
I hope this helps!!
- [Django]-Django β Rotating File Handler stuck when file is equal to maxBytes
- [Django]-Requirements.txt greater than equal to and then less than?
- [Django]-How to deal with "SubfieldBase has been deprecated. Use Field.from_db_value instead."
15π
Directly from the docs https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax
If your view is not rendering a template containing the csrf_token
template tag, Django might not set the CSRF token cookie. This is
common in cases where forms are dynamically added to the page. To
address this case, Django provides a view decorator which forces
setting of the cookie: ensure_csrf_cookie().
Since your application is a single-page application, you can add ensure_csrf_cookie()
to the view that is responsible for the initial page load.
- [Django]-How to simplify migrations in Django 1.7?
- [Django]-How to use pdb.set_trace() in a Django unittest?
- [Django]-Proper way to test Django signals
6π
A small update to this solution.
Since AngularJS 1.2.10 you need to set the CSRF cookie for each request type in the client:
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers.put['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers['delete']['X-CSRFToken'] = $cookies.csrftoken;
This due the following change that happened between 1.2.9 and 1.2.10
https://github.com/cironunes/angular.js/commit/781287473bc2e8ee67078c05b76242124dd43376
Hope this helps someone!
- [Django]-Serving gzipped content from django
- [Django]-Command not found: django-admin.py
- [Django]-Circular dependency in Django Rest Framework serializers
2π
After So Much Search i landed on this solution and its work form me on local system and also on live web faction server this is my solution for Django users please go to your apache folder located in project then in bin you find
httpd.conf or your server config for php or other users (usually located in a *.conf file, such as httpd.conf or apache.conf), or within a .htaccess. then just add this code
<IfModule mod_headers.c>
SetEnvIf Origin (.*) AccessControlAllowOrigin=$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
then in angular js app you just needed to place
angular.module('app', ['ngCookies'])
.config([
'$httpProvider',
'$interpolateProvider',
function($httpProvider, $interpolateProvider, $scope, $http) {
$httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
}]).
run([
'$http',
'$cookies',
function($http, $cookies) {
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
}]);
Its Worked for me on Django Angularjs platform.
- [Django]-Django Imagefield not working properly via ModelForm
- [Django]-Simple Log to File example for django 1.3+
- [Django]-Django Background Task