CSRF Cookie not set when posting request with AngularJs - Django Backend - angularjs

I'm building a web app with angularjs and django and I'm submitting form via Ajax request.
My problem is that when posting an Ajxa request with angular (ng-file-upload precisely) the csrfmiddlewaretoken expected by django is not set.
From my lectures on angular documentation and other forums I ended up with the following configuration.
In the config part of angular :
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.withCredentials = true;
and in my controller the code for sending the request is :
Upload.upload({
url: 'http://localhost:8000/image',
headers: {
'X-CSRFToken': $cookies['csrftoken']
},
data: {file: file}
})
With that code the request send has the following headers :
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,de;q=0.2,fi;q=0.2
Connection:keep-alive
Content-Length:16582
Content-Type:multipart/form-data; boundary=----WebKitFormBoundarybWo821vSwcejTATP
Cookie:csrftoken=bC2UpXurGXAg3AUZgSqMVlUs8TKfussS
Host:localhost:8000
Origin:http://127.0.0.1:8000
Referer:http://127.0.0.1:8000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36
X-CSRFToken:UeSt4LoqgU9L28JQBdVbS2IJJXOMQK6n
However for django to be able to handle csrf protection correctly the following header is missing
Cookie:_ga=GA1.1.1358238168.1447425523; XSRF-TOKEN=zWIM6q7O2Nz3PLm8TMUJSLFVRF8bKUbr; csrftoken=UeSt4LoqgU9L28JQBdVbS2IJJXOMQK6n
So far and despite having seen a lot of forums about this topic I didn't manage to set this header. if I try to set it programmatically via :
Upload.upload({
url: 'http://localhost:8000/image',
headers: {
'Cookie': 'csrftoken='+$cookies['csrftoken']
},
data: {file: file}
})
I end up with the following error in my console :
Refused to set unsafe header "Cookie"
My problem is really how to configure the cookie header from the client side. My django code is fine.
I have been struggling with this for quite a time now. Any help would be appreciated ! Thanks

If you added in the csrftoken to client headers: {'X-CSRFToken': $cookies['csrftoken']} means your client is most probably ready, but for security matter if you interact with django api from external application he will still block the request returning unsafe header "Cookie".
try the following configuration to allow the cross site request over your app:
pip install django-cors-headers
and then add it to your installed apps in your settings.py:
INSTALLED_APPS = (
...
'corsheaders',
...
)
You will also need to add a middleware class to listen in on responses and make sure you respect the order as follow:
MIDDLEWARE_CLASSES = (
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
)
and finally add this settings variable:
CORS_ORIGIN_ALLOW_ALL = True
This should be enough but for more customized configuration you can check django-cors-headers

Related

How can I get around CORS restrictions with my api calls? [duplicate]

This question already has answers here:
XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header
(11 answers)
Closed 3 years ago.
I've been developing a personal fortnite stats web app. I you use two main API calls. One of them has CORS access policy enabled, so in develoment I can get around this by adding the domain as a proxy and adding the path within the fetch request.
When I build this I cannot call the API, so have tried sending the full URL in the fetch request plus adding headers, mode etc to try and meet the CORS policy. I have tried everything but its still not working.
Could anybody help please?
Thanks
app.js level
fetchFortniteData = username => {
return new Promise((resolve, reject) => {
fetch(`/v1/profile/pc/${username}`, {
headers: new Headers({
'TRN-Api-Key': process.env.REACT_APP_TRN
})
})
package.json
"proxy": "https://api.fortnitetracker.com",
The errors I receive from the console are:
Access to fetch at 'https://api.fortnitetracker.com//v1/profile/pc/popps01' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
2fortniteApi.js:57 OPTIONS https://api.fortnitetracker.com//v1/profile/pc/popps01 404
Which is what was happening in development, which is why I added a proxy.
I have also added mode: 'no-cors' and other allow origin keys to the header but it still doesnt work.
In the network tab:
Request URL: https://api.fortnitetracker.com//v1/profile/pc/popps01
Request Method: OPTIONS
Status Code: 404
Remote Address: [2606:4700:20::6819:9810]:443
Referrer Policy: no-referrer-when-downgrade
Provisional headers are shown
Access-Control-Request-Headers: trn-api-key
Access-Control-Request-Method: GET
Origin: http://localhost:3000
Referer: http://localhost:3000/
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
For anybody that has this problem in the future, if you create an Express app backend and add no cors that will bypass the API cors policy. In development, all you need to do is add a proxy, however, when going into production that will not work so set up an Express backend, point your front-end endpoints to the backend REST requests.
I also hosted the backend on Elastic Beanstalk, the front end of s3 and CloudFront and it works.
Two methods:
Install a chrome extension to Access-control-allow-origin to allow cross-origin redirects.
extention will allow if your backend server is on your local machine
If the backend server is yours(your localhost), put access control allow origin to '*'.
This is the default django server settings (settings file) to allow cors:
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
'GET',
'POST',
'PUT',
'OPTIONS',
)
CORS_ORIGIN_WHITELIST = ('*', )
Do keep this in mind before making your server.

unable to send Cross domain ajax requests with Angular

I am facing a problem in sending cross domain request to a web api(api.worldbank.org) . It says
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1' is therefore not allowed access.
So as someone suggested, i used this-
app.config( function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
);
but still the error is same and also no change occurs in headers with or without adding this code.
Complete code is this-
'use strict';
var app=angular.module('myapp',[]);
app.config( function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
);
app.factory('dataFactory',function($http){
var O={};
O.getLifeExpectancy=function(){
var dataToSend={};
$http({
method:'GET',
url:'http://api.worldbank.org/countries/in/indicators/SP.DYN.LE00.IN?format=json&date=2000:2015'
}).then(function(data){
dataToSend=data;
});
return dataToSend;
};
Am i doing something wrong ?
is $http and $httpProvider two different things, because no change is reflected in headers with or without app.config code.
here are my headers in both the situations-
Accept:application/json, text/plain, / Accept-Encoding:gzip,
deflate, sdch Accept-Language:en-US,en;q=0.8 Cache-Control:max-age=0
Connection:keep-alive Host:api.worldbank.org Origin:127.0.0.1
Referer:127.0.0.1/myApp/ User-Agent:Mozilla/5.0 (Windows NT
10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
This messages says that your server-side code doesn't allow request from different domain, so the best solution is to enable CORS with header Access-Control-Allow-Origin: '*' at the server-side.
EDIT: If your Angular app will run in browser - you need to set CORS server-side or just make some proxy on your domain (some browsers on default block such requests).
But if you're doing Cordova/PhoneGap mobile app you'll be able to disable it in config.xml so currently for testing purposes you can just disable it in your browser.

CSRF token missing or incorrect. Django + AngularJS

I'm getting CSRF token missing or incorrect error while doing a POST request to a remote django api from my localhost machine.
My settings on AngularJS:
.config(['$httpProvider', function($httpProvider){
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);
but im still getting the CSRF token missing or incorrect error.
I check what headers are being sent and apparently angular is not sending HTTP_X_CSRFTOKEN.
But I can see that the cookie csrftoken=something is sent.
Does anyone know what is going on?
Request Header
POST /s/login/ HTTP/1.1
Host: server.somewhere.io:8000
Connection: keep-alive
Content-Length: 290
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost/thesocialmarkt/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6,pt-BR;q=0.4,pt;q=0.2
Cookie: csrftoken=hiYq1bCNux1mTeQuI4eNgi97qir8pivi; sessionid=1nn1phjab5yd71yfu5k8ghdch2ho6exc
As #Chris Hawkes pointed to this stackoverflow answer given by #Ye Liu
Since the angular app isn't served by django, in order to let the
cookie to be set, angular app needs to do a GET request to django
first.
I verified that as long as you don't make http get request, csrftoken cookie doesn't get set. So only
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
would not work. You first need to make if not real then mock http get request to django rest_framework.
Update: Your comments pushed me to further study it, Please read this blog where is has mentioned as,
CLIENT-SIDE GENERATED CSRF-TOKENS. Have the clients generate and send
the same unique secret value in both a Cookie and a custom HTTP
header. Considering a website is only allowed to read/write a Cookie
for its own domain, only the real site can send the same value in both
headers
So lets try with this single request first.
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
where you are injecting $cookies to the controller/service.
If it works then may be writing interceptors would be good choice, and would help you to debug as well.
I am sure you are using AngularJs version at least 1.2, See this changeset
and in recent commit Angular http service checking csrf with this code,
var xsrfValue = urlIsSameOrigin(config.url)
? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
: undefined;
if (xsrfValue) {
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
}
So it's necessary that you are sending same token which is present in cookie.
Further to analyse use developer tool of your browser to see request/response with the http request and analyse headers and cookies.
if you are using $http in AngularJS for ajax request and if you are facing any CSRF token issue then use this:
$http.defaults.xsrfCookieName = 'csrftoken';
$http.defaults.xsrfHeaderName = 'X-CSRFToken';

Angularjs using JWT breaks CORS to Amazon S3 on login

I've got an interesting issue happening on my angularjs app. When I login, all of the template requests in angular made to Amazon S3 stop working, and return a 400 Bad Request. They work completely fine before you login. The only thing that should change when logged in, is a json web token is sent in the headers to verify the person logged in. My thoughts are maybe the interceptor that is sending the jwt in the headers is somehow affecting CORS on Amazon S3. Seems strange.
Here is the interceptor code:
.factory('TokenInterceptor', function ($q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
return response || $q.when(response);
}
};
});
EDIT: It was giving me an Access Origin error but I changed my CORS file on Amazon and it seemed to change to a 400 error now. My CORS file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
EDIT: Including a sample of the response when trying to access the file after logging in:
Remote Address:1.2.3.4:443
Request URL:https://s3.amazonaws.com/bucket/path/to/file/template.html
Request Method:GET
Status Code:400 Bad Request
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-GB,en;q=0.8,en-US;q=0.6,fr;q=0.4,es;q=0.2
Authorization:Bearer xxxXXXxxxXXXxxXXxxxXXXXxxXXxx
Connection:keep-alive
Host:s3.amazonaws.com
Origin:http://domain.com
Referer:http://domain.com/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Response Headersview source
Access-Control-Allow-Methods:GET
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3000
Connection:close
Content-Type:application/xml
Date:Sun, 21 Dec 2014 01:49:16 GMT
Server:AmazonS3
Transfer-Encoding:chunked
Vary:Origin, Access-Control-Request-Headers, Access-Control-Request-Method
x-amz-id-2:xxxXXXxxxXXXxx
x-amz-request-id:xxxXXXxxxXXxxxXXXxx
Just for the record. I was also having the same problem (or a very similar one which is: None files would be found after i did the login on my AngularJS app) and after hours digging on the problem i found what was the issue.
On my specific case it was nothing really related to S3 or CORS but in fact the cookies (and I'm still not really sure how it did cause the problem of not finding the files, but it did). As i saw on my application, i've drastically increased the information stored on my user after i did the login and it was exceeding the 4kb limit for the cookies and for some reason it was breaking down my whole website.
I'm checking the possibility to change from cookies to localStorage, but that is another history. the main thing is that after i reduce the number of information stored on the cookie it started working as it was supposed to.
You need to add transformRequest to http post to delete the Authorization header for that specific request.
transformRequest: function (data, headersGetter) {
//Headers change here
var headers = headersGetter();
delete headers['Authorization'];
return data;
},
Got the same problem on uploading to Amazon S3 the same issue so i added this and it worked.

AngularJS - How to connect to Twitter application-only authentication via Oauth2?

I try to receive an accessToken from the Twitter application-only authentication but I keep receiving a 405 (Method Not Allowed) response from the twitter api. Anybody knows how to solve this? I'm desperately stuck..
I am aware of the fact that:
- best practice is doing this from serverside, but I wanted to try this out with angular on client side
- X-Requested-With should be deleted from the header
This is the factory I created:
twitterServices.factory('Connect', function($http){
var factory = {};
var baseUrl = 'https://api.twitter.com/';
var bearerToken = function(){
var consumerKey = encodeURIComponent('****');
var consumerSecret = encodeURIComponent('****');
var tokenCredentials = btoa(consumerKey + ':' + consumerSecret);
return tokenCredentials;
};
factory.fetchAccessToken = function(scope){
var oAuthurl = baseUrl + "oauth2/token";
var headers = {
'Authorization': 'Basic ' + bearerToken(),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
};
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$http({method: 'POST', url: oAuthurl, headers: headers, data: 'grant_type=client_credentials'}).
success(function(data, status){
scope.status = status;
scope.data = data;
}).
error(function(data, status){
scope.status = status;
scope.data = data || "Request failed";
});
};
factory.fetchTimeLine = function(scope){
scope.fetchAccessToken();
//the rest
};
return factory;
});
This is the header request/response in Chrome:
Request URL:`https://api.twitter.com/oauth2/token`
Request Method:OPTIONS
Status Code:405 Method Not Allowed
Request Headersview source
:host:api.twitter.com
:method:OPTIONS
:path:/oauth2/token
:scheme:https
:version:HTTP/1.1
accept:*/*
accept-encoding:gzip,deflate,sdch
accept-language:en-US,en;q=0.8
access-control-request-headers:accept, authorization, content-type
access-control-request-method:POST
origin:`http://localhost`
referer:`http://localhost/test/app/
user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36
Response Headersview source
content-length:0
status:405 Method Not Allowed
version:HTTP/1.1
My Console shows the following:
OPTIONS https://api.twitter.com/oauth2/token 405 (Method Not Allowed) angular.js:9312
OPTIONS https://api.twitter.com/oauth2/token Origin http://localhost is not allowed by Access-Control-Allow-Origin. angular.js:9312
XMLHttpRequest cannot load https://api.twitter.com/oauth2/token. Origin http://localhost is not allowed by Access-Control-Allow-Origin. (index):1
Check:
Twitter :Application-only authentication error Origin null is not allowed by Access-Control-Allow-Origin
and:
https://dev.twitter.com/discussions/1291
I ran into a similar issue when working with the google API where in request from localhost are denied even if you register it with the system.
We got around this issue by adding a multipart name to our /etc/hosts file and binding it to 127.0.0.1
for example
127.0.0.1 www.devsite.com
This has resolved the most basic issues that I have had writing angular services for APIs
update by request:
One of the ways that companies control access to their APIs is through whitelisting. When you register an application with the service that platform will typically add the domain you list in your application to its whitelist. This is Generally done to force you in to using separate API keys for separate services. This can make work on the dev side difficult when you are testing locally.
In this case I believe that twitter has specifically banned requests using localhost to prevent the use of 3rd party tools and bots.
Binding the domain you registered with your API key into your hosts file will cause any web requests on your machine to that domain to skip a dns lookup and instead route the request to your local dev server. This means that locally you will test your code by visiting:
www.devsite.com:[what ever port your local server is running on]
This may not be the solution to 100% of api access problems but it is one of the most common that I have experienced.
Note based on other responses:
There are Multiple reasons why you might experience a CORS related error. But just because you have received one doesn't mean that it isn't possible to implement your code on the front end. Generally in Angular CORS is encountered when:
a) you have failed to format your request correctly
-- one example of this might be you have added a header to indicate json is an expected result when infact the response it text.
b) the service or API is configured with a whitelist that needs to include explicitly either "localhost" or some other domain as discussed in this post.

Resources