AngularJS + Django Rest Framework + CORS (CSRF Cookie does not appear in the client)

I am developing a single-page application in an AngularJS application and Django Rest Framework + Django CORS Headers.

My problem is that the cookie "csrftoken" never appears in my browser when I contacted the backend.

For example: I am making a login using a message. I get the "sessionid" cookie correctly, but the "csrftoken" never appears, and therefore I can not make the right messages from my client, as I will be denied due to the lack of the csrf token.

  • I parsed the response headers from the API, and csrftoken is not ther.
  • I looked directly in the browser of the other browsers, and there it looks great.
  • Just to point out, I can do my first POST for login, since the Django Rest Framework only forces CSRF for authenticated users. If I try to rewrite it, it will happen from the moment the "sessionid" -cookie appears.
  • I'm not interested in circumventing CSRF protection, as they offer some posts on stackoverflow.

Some snippets of code from the front / inside. These are incomplete snippets, so don't get hung up on badly written code.

Backend API LoginView API

class LoginView(APIView): renderer_classes = (JSONPRenderer, JSONRenderer) def post(self, request, format=None): 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 Access Control

 .controller('LoginCtrl', ['$scope', '$http', 'uService', '$rootScope', function(scope, $http, User, rootScope) { scope.login = function() { var config = { method: 'POST', withCredentials: true, url: rootScope.apiURL+'/user/login/', data : scope.loginForm }; $http(config) .success(function(data, status, headers, config) { if (status == 200) { console.log(data[0]); //Test code // succefull login User.isLogged = true; User.username = data.username; } else { console.log(data); //Test code User.isLogged = false; User.username = ''; } }) .error(function(data, status, headers, config) { console.log('Testing console error'); User.isLogged = false; User.username = ''; }); }; 

}]);

Anyone with good tips / ideas / examples?

+33
angularjs django cors django-rest-framework django-cors-headers
Jul 29 '13 at 18:09
source share
5 answers

So, I found my own solution, it seems to work fine.

These are the new code snippets:

Backend API LoginView API (a decorator has been added that forces 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) 

Side to AngularJS side (add a token to the request header)

 $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken; 

Server-side configuration file (specially for django-cors-headers)

The first 5 are added by default, but you need to add an β€œX-CSRFToken” to allow such a header from the client to the API using CORS, otherwise the message will be rejected.

 CORS_ALLOW_HEADERS = ( 'x-requested-with', 'content-type', 'accept', 'origin', 'authorization', 'X-CSRFToken' 

)

Here it is!

+6
Jul 30 '13 at 7:41
source share

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 # 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 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!

+32
Mar 22 '14 at 22:13
source share

Directly from the docs https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax

If your view does not display a template containing the csrf_token template tag, Django may not set the CSRF token cookie. This is usually the case when forms are dynamically added to the page. refer to this case, Django provides a decorator that forces the setting of the cookie: secure_csrf_cookie ().

Since your application is single-page, you can add ensure_csrf_cookie() to the view that is responsible for loading the start page.

+15
Jul 30 '13 at 4:03
source share

A small update to this solution.

Since AngularJS 1.2.10 you need to set the CSRF cookie for each type of request 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 is caused by the following change, which occurred between 1.2.9 and 1.2.10 https://github.com/cironunes/angular.js/commit/781287473bc2e8ee67078c05b76242124dd43376

Hope this helps someone!

+6
Mar 02 '14 at 19:56
source share

After such a great search, I landed on this solution, and its work shapes me in the local system, as well as on the real server of web fractions, this is my solution for Django users, please go to your apache folder located in the project, then in the basket you will find

httpd.conf or your server configuration for php or other users (usually located in a * .conf file, such as httpd.conf or apache.conf) or inside .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 need 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; }]); 

It worked for me on the Django Angularjs platform.

https://gist.github.com/mlynch/be92735ce4c547bd45f6

+2
Feb 06 '17 at 4:29
source share



All Articles