I am using Django with Angular JS to access the Google Drive API. I am following this document from Google. The FLOW.step1_get_authorize_url() gives me the URL similar to the sample URL mentioned on the page. But the problem is that after return HttpResponseRedirect(authorize_url) the browser does not redirect to the authorize_url and gives the error as shown in the picture below (Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access. The response had HTTP status code 405).
But if I copy pasted the URL, it works fine.
The oauth2 function looks like this.
def index(request):
FLOW = flow_from_clientsecrets(
settings.GOOGLE_OAUTH2_CLIENT_SECRETS_JSON,
scope='https://www.googleapis.com/auth/drive',
redirect_uri='http://127.0.0.1:8000/oauth2callback/'
)
FLOW.params['access_type'] = 'offline'
authorize_url = FLOW.step1_get_authorize_url()
return HttpResponseRedirect(authorize_url)
And here is the oauth2callback function.
def auth_return(request):
credential = FLOW.step2_exchange(request.GET)
return HttpResponseRedirect("/mycustomurl")
I used this to enable CORS in the Django Server Side. Here is my part of service in Angular that makes the call to oauth2.
(function () {
'use strict';
angular.module('myApp')
.service('myService', function ($http) {
this.saveToDrive = function (startYear, endYear, shape) {
var config = {
params: {
start: '1999',
end: '2002',
action: 'download-to-drive'
},
headers: {
'Access-Control-Allow-Origin': '*',
'X-Requested-With': null
}
}
var promise = $http.get('/oauth2/', config)
.then(function (response) {
return response.data;
});
return promise;
};
});
})();
Please suggest what am I missing here. Any help or suggestions are highly appreciated.
I found it be a minor design issue rather than the code issue. I separated the logic that sends the oauth2 request to the client, and after the oauth2 request, I sent request to internal API with the params options. And now it's working fine.
Related
I have created a demo using JavaScript for Flickr photo search API.
Now I am converting it to the AngularJs.
I have searched on internet and found below configuration.
Configuration:
myApp.config(function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
});
Service:
myApp.service('dataService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.flickrPhotoSearch = function() {
return $http({
method: 'GET',
url: 'http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=3f807259749363aaa29c76012fa93945&tags=india&format=json&callback=?',
dataType: 'jsonp',
headers: {'Authorization': 'Token token=xxxxYYYYZzzz'}
});
}
});
Controller:
myApp.controller('flickrController', function($scope, dataService) {
$scope.data = null;
dataService.flickrPhotoSearch().then(function(dataResponse) {
$scope.data = dataResponse;
console.log($scope.data);
});
});
But still I got the same error.
Here are some links I tried:
XMLHttpRequest cannot load URL. Origin not allowed by Access-Control-Allow-Origin
http://goo.gl/JuS5B1
You don't. The server you are making the request to has to implement CORS to grant JavaScript from your website access. Your JavaScript can't grant itself permission to access another website.
I had a similar problem and for me it boiled down to adding the following HTTP headers at the response of the receiving end:
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *
You may prefer not to use the * at the end, but only the domainname of the host sending the data. Like *.example.com
But this is only feasible when you have access to the configuration of the server.
Try using the resource service to consume flickr jsonp:
var MyApp = angular.module('MyApp', ['ng', 'ngResource']);
MyApp.factory('flickrPhotos', function ($resource) {
return $resource('http://api.flickr.com/services/feeds/photos_public.gne', { format: 'json', jsoncallback: 'JSON_CALLBACK' }, { 'load': { 'method': 'JSONP' } });
});
MyApp.directive('masonry', function ($parse) {
return {
restrict: 'AC',
link: function (scope, elem, attrs) {
elem.masonry({ itemSelector: '.masonry-item', columnWidth: $parse(attrs.masonry)(scope) });
}
};
});
MyApp.directive('masonryItem', function () {
return {
restrict: 'AC',
link: function (scope, elem, attrs) {
elem.imagesLoaded(function () {
elem.parents('.masonry').masonry('reload');
});
}
};
});
MyApp.controller('MasonryCtrl', function ($scope, flickrPhotos) {
$scope.photos = flickrPhotos.load({ tags: 'dogs' });
});
Template:
<div class="masonry: 240;" ng-controller="MasonryCtrl">
<div class="masonry-item" ng-repeat="item in photos.items">
<img ng-src="{{ item.media.m }}" />
</div>
</div>
This issue occurs because of web application security model policy that is Same Origin Policy Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. That means requester must match the exact host, protocol, and port of requesting site.
We have multiple options to over come this CORS header issue.
Using Proxy - In this solution we will run a proxy such that when request goes through the proxy it will appear like it is some same origin.
If you are using the nodeJS you can use cors-anywhere to do the proxy stuff. https://www.npmjs.com/package/cors-anywhere.
Example:-
var host = process.env.HOST || '0.0.0.0';
var port = process.env.PORT || 8080;
var cors_proxy = require('cors-anywhere');
cors_proxy.createServer({
originWhitelist: [], // Allow all origins
requireHeader: ['origin', 'x-requested-with'],
removeHeaders: ['cookie', 'cookie2']
}).listen(port, host, function() {
console.log('Running CORS Anywhere on ' + host + ':' + port);
});
JSONP - JSONP is a method for sending JSON data without worrying about cross-domain issues.It does not use the XMLHttpRequest object.It uses the <script> tag instead. https://www.w3schools.com/js/js_json_jsonp.asp
Server Side - On server side we need to enable cross-origin requests.
First we will get the Preflighted requests (OPTIONS) and we need to allow the request that is status code 200 (ok).
Preflighted requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if it uses methods other than GET or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
If you are using the spring just adding the bellow code will resolves the issue.
Here I have disabled the csrf token that doesn't matter enable/disable according to your requirement.
#SpringBootApplication
public class SupplierServicesApplication {
public static void main(String[] args) {
SpringApplication.run(SupplierServicesApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
If you are using the spring security use below code along with above code.
#Configuration
#EnableWebSecurity
public class SupplierSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().antMatchers("/**").authenticated().and()
.httpBasic();
}
}
I encountered a similar problem like this, problem was with the backend . I was using node server(Express). I had a get request from the frontend(angular) as shown below
onGetUser(){
return this.http.get("http://localhost:3000/user").pipe(map(
(response:Response)=>{
const user =response.json();
return user;
}
))
}
But it gave the following error
This is the backend code written using express without the headers
app.get('/user',async(req,res)=>{
const user=await getuser();
res.send(user);
})
After adding a header to the method problem was solved
app.get('/user',async(req,res)=>{
res.header("Access-Control-Allow-Origin", "*");
const user=await getuser();
res.send(user);
})
You can get more details about Enabling CORS on Node JS
This answer outlines two ways to workaround APIs that don't support CORS:
Use a CORS Proxy
Use JSONP if the API Supports it
One workaround is to use a CORS PROXY:
angular.module("app",[])
.run(function($rootScope,$http) {
var proxy = "//cors-anywhere.herokuapp.com";
var url = "http://api.ipify.org/?format=json";
$http.get(proxy +'/'+ url)
.then(function(response) {
$rootScope.response = response.data;
}).catch(function(response) {
$rootScope.response = 'ERROR: ' + response.status;
})
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
Response = {{response}}
</body>
For more information, see
GitHub: CORS Anywhere
Use JSONP if the API supports it:
var url = "//api.ipify.org/";
var trust = $sce.trustAsResourceUrl(url);
$http.jsonp(trust,{params: {format:'jsonp'}})
.then(function(response) {
console.log(response);
$scope.response = response.data;
}).catch(function(response) {
console.log(response);
$scope.response = 'ERROR: ' + response.status;
})
The DEMO on PLNKR
For more information, see
AngularJS $http Service API Reference - $http.jsonp
Answered by myself.
CORS angular js + restEasy on POST
Well finally I came to this workaround:
The reason it worked with IE is because IE sends directly a POST instead of first a preflight request to ask for permission.
But I still don't know why the filter wasn't able to manage an OPTIONS request and sends by default headers that aren't described in the filter (seems like an override for that only case ... maybe a restEasy thing ...)
So I created an OPTIONS path in my rest service that rewrites the reponse and includes the headers in the response using response header
I'm still looking for the clean way to do it if anybody faced this before.
Apache/HTTPD tends to be around in most enterprises or if you're using Centos/etc at home. So, if you have that around, you can do a proxy very easily to add the necessary CORS headers.
I have a blog post on this here as I suffered with it quite a few times recently. But the important bit is just adding this to your /etc/httpd/conf/httpd.conf file and ensuring you are already doing "Listen 80":
<VirtualHost *:80>
<LocationMatch "/SomePath">
ProxyPass http://target-ip:8080/SomePath
Header add "Access-Control-Allow-Origin" "*"
</LocationMatch>
</VirtualHost>
This ensures that all requests to URLs under your-server-ip:80/SomePath route to http://target-ip:8080/SomePath (the API without CORS support) and that they return with the correct Access-Control-Allow-Origin header to allow them to work with your web-app.
Of course you can change the ports and target the whole server rather than SomePath if you like.
var result=[];
var app = angular.module('app', []);
app.controller('myCtrl', function ($scope, $http) {
var url="";// your request url
var request={};// your request parameters
var headers = {
// 'Authorization': 'Basic ' + btoa(username + ":" + password),
'Access-Control-Allow-Origin': true,
'Content-Type': 'application/json; charset=utf-8',
"X-Requested-With": "XMLHttpRequest"
}
$http.post(url, request, {
headers
})
.then(function Success(response) {
result.push(response.data);
$scope.Data = result;
},
function Error(response) {
result.push(response.data);
$scope.Data = result;
console.log(response.statusText + " " + response.status)
});
});
And also add following code in your WebApiConfig file
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
we can enable CORS in the frontend by using the ngResourse module.
But most importantly, we should have this piece of code while making the ajax
request in the controller,
$scope.weatherAPI = $resource(YOUR API,
{callback: "JSON_CALLBACK"}, {get: {method: 'JSONP'}});
$scope.weatherResult = $scope.weatherAPI.get(YOUR REQUEST DATA, if any);
Also, you must add ngResourse CDN in the script part and add as a dependency
in the app module.
<script src="https://code.angularjs.org/1.2.16/angular-resource.js"></script>
Then use "ngResourse" in the app module dependency section
var routerApp = angular.module("routerApp", ["ui.router", 'ngResource']);
My app uses IBM Watson Speech-to-Text, which requires an access token. From the command line I can get the access token with curl:
curl -X GET --user my-user-account:password \
--output token \
"https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api"
When I make an HTTP request using Angular's $http service I get a CORS error:
var data = {
user: 'my-user-account:password',
output: 'token'
};
$http({
method: 'GET',
url: 'https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api',
data: data,
}).then(function successCallback(response) {
console.log("HTTP GET successful");
}, function errorCallback(response) {
console.log("HTTP GET failed");
});
The error message says:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:8080' is therefore not allowed
access. The response had HTTP status code 401.
As I understand, it's not possible to do CORS from Angular; CORS has to be done from the server. I know how to do CORS with Node but I'm using Firebase as the server.
Firebase has documentation about making HTTP requests with CORS. The documentation says to write this:
$scope.getIBMToken = functions.https.onRequest((req, res) => {
cors(req, res, () => {
});
});
First, that doesn't work. The error message is functions is not defined. Apparently functions isn't in the Firebase library? I call Firebase from index.html:
<script src="https://www.gstatic.com/firebasejs/4.3.0/firebase.js"></script>
My controller injects dependencies for $firebaseArray, $firebaseAuth, and $firebaseStorage. Do I need to inject a dependency for $firebaseHttp or something like that?
Second, how do I specify the method ('GET'), the URL, and the data (my account and password)?
if you want to send credentials with angular, just set withCredentials=true. I am also using CORS with Angular v4, for your HTTP header error, you are right. Header Access-Control-Allow-Origin must be added on server side, check if you have settings in your api to allow certain domains, urls, pages, because google api's has this function, so check where you get token there should be some settings.
Here is example, how I am calling API with CORS, using typescript:
broadcastPresense(clientId: string) {
const headers = new Headers({'Content-Type':'application/json','withCredentials':'true'});
return this.http.post('http://localhost/api.php',
{
'jsonrpc': '2.0',
'method': 'somemethod',
'params': {'client_id': clientId},
'id': CommonClass.generateRandomString(16)
},{headers: headers, withCredentials:true}).map(
(res: Response) => {
console.log(res);
const data = res.json();
console.log(data);
if (data.error == null) {
return data.result;
} else if (data.error != null) {
throw data.error;
}
throw data.error;
}
).catch(
(error) => {
this.router.navigate(['/error',3],{queryParams: {desc:'Server error'}});
return Observable.throw(error);
}
);
}
Hope it helps :)
The answer is to use Cloud Functions for Firebase, which enable running Node functions from the server. Then you use the Node module request to send the HTTP request from Node.
In my angular app, I have the following error when I try to make an REST api.
My Code is given below:
Angular Controller
$scope.saveTeam = function() {
var club = {};
club.name = $scope.selectedClub;
var service = API.getService();
service.create( {}, { club: club },
function( res ) {
}, function(err) {
console.log("club err : ", err);
});
}
}
Angular Factory
// Clubs service used for communicating with the coaches REST endpoint
angular
.module('app')
.factory('API', ['$resource', 'session', function($resource, session) {
var mainUrl = '/clubs';
return {
getService : function() {
var token = session.getToken();
return $resource(mainUrl, { }, {
createClub: {
method: 'POST',
url: mainUrl,
isArray: false,
headers: { 'Token': token }
}
})
}
}
});
How can I solve this error? Thanks in Advance.
Install this chrome extension to avoid CORS error. This error generally comes because of the security headers in the request made by a client. Use the line of code shown below before making any request to server.
$http.defaults.headers.post["Content-Type"] = "application/json";
Working principles of CORS is a simple HEADERS game between Client and Server. The browser (the client) set in the Origin key the current domain in your case set "http://localhost:9001". The server, in turn, verified that this value is among those trusted and responds with another piece of information (always in the header) with the key Access-Control-Allow-Origin. If the two values are equal, then the browser uses the response, otherwise you will have an error.
So in the future you need to configure the server but for development you can start the Chrome browser with Disable same origin policy. With disable security the browser don't set the origin and you don't have a problem. This is the problem so check this link:
Disable same origin policy in Chrome
My REST backend [ based on NodeJS/express/mongojs] is complaining 404 (not found) when Params are attached as part of URL. Backend rest interface is coded as below;
var express = require('express');
var router = express.Router();
router.get('/login', auth.signin); //auth.signin is code to verify user
Above REST service is consumed by AngularJS based frontend through $resource as below;
Definition:
angular.module('myapp').factory('signinmgr', function($resource) {
return $resource("http://localhost:3000/login", {}, {
'get': {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}});
Usage:
signinmgr.get({'username':'myname', 'password':'mypass'}, function(data){
//success
}, function(x){
//failed
});
Problem:
Frontend code above produces a URL to consume REST service where parameters are part of URL i.e. http://localhost:port/login?username=myname&password=mypass [if I use GET method, POST is OK]. I wanted my front end to keep URL as http://localhost:port/login and post any parameters through body as backend is using req.body.paramName to read those. [Actual Solution]
If (1) cannot be done, and my frontend is sending params as part of URL, I needed help as to know how to equip my backend to allow this URL with parameters so that backend doesnt return 404 as the base URL http://localhost:port/login is already there.
PS: for (1), I tried this thread with data:{username:'',password:''} but of no use. Please help if I am missing something very obvious or some concept.
Try the $http service instead:
angular.module('myapp').factor('signinmgr', function($http) {
return {
login: function (username, password) {
$http.post("http://localhost:3000/login", {
username: username,
password: password
}
}
};
});
signinmgr.login('myname', 'mypass').then(function(data){
//success
}, function(x){
//failed
});
Each request that my nodejs/expressjs backend receives has three places for passed attributes;
params{}
query{}
body{}
My problem (1) cannot be fixed in case I want to use GET method since with GET request parameters are visible as part of URL i.e. http://localhost:port/login?username=myname&password=mypass. To send my username/password I had to use POST that sends parameters as part of body{}.
My problem (2) was that I was using GET and mistakenly looking for parameters in body{} of request. Instead, parameters passed as part of URL in GET request are added to query{} of the request.
I'm trying to access an API with AngularJS but I get the following error:
XMLHttpRequest cannot load http://www.football-data.org/alpha/soccerseasons/398/leagueTable?callback=JSON_CALLBACK. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://purepremier.com' is therefore not allowed access.
This is my code for the service:
angular.module('PremierLeagueApp.services', []).
factory('footballdataAPIservice', function($http) {
var footballdataAPI = {};
footballdataAPI.getTeams = function() {
$http.defaults.headers.common['Auth-Token'] = 'token';
return $http.get('http://www.football-data.org/alpha/soccerseasons/398/leagueTable?callback=JSON_CALLBACK');
};
return footballdataAPI;
});
I use an authentication token (api key) to access the api, but according the API owner this API key header is not sent or recognized. Do you have any idea how I can adapt the code to make this work? thanks!
You should hide that API key before posting on a public site such as this. I would advise you regenerate your key (if possible) just in case - better safe than sorry.
Assuming your site url is 'http://purepremier.com' from the error message, the API should add a 'Access-Control-Allow-Origin' header with your site URL to allow you access. Have a look here for more information.
This is not directly related to your problem, but I notice you are setting $http defaults every time getTeams() is called. You should either set this outside of the actual function call (preferably in a run block), or just send the GET request with that header specifically applied. As the API key is specific (I assume) to that call, you may not want to be sending it to anyone and everyone, every time you make a HTTP request.
Change your factory code like this:
factory('footballdataAPIservice', function($http) {
return {
getTeams: function(){
return $http({
url:'http://www.football-data.org/alpha/soccerseasons/398/leagueTable',
headers: { 'X-Auth-Token': 'your_token' },
method: 'GET'
}).success(function(data){
return data;
});
}
}
});
Inject factory in your controller and retreive the data:
.controller('someController',function(footballdataAPIservice,$scope){
footballdataAPIservice.getTeams().then(function(data){
$scope.teams=data;
console.log($scope.teams)
});
});
Here is the working plunker
You change the Auth-Token To Authorization
$http.defaults.headers.common['Authorization'] = 'token';
Because token is send via headers using Authorization
try jsonp
angular.module('PremierLeagueApp.services', []).
factory('footballdataAPIservice', function($http) {
var footballdataAPI = {};
footballdataAPI.getTeams = function() {
$http.defaults.headers.common['Auth-Token'] = 'token';
return $http.jsonp('http://www.football-data.org/alpha/soccerseasons/398/leagueTable?callback=JSON_CALLBACK');
};
return footballdataAPI;
});