I'm developing an angular web application that will replace the current website that we have. The current website uses session based authentication. At the moment, I can't access the hosted API with get or post requests.
I'm developing the angular application on my local computer using a python simple server, whereas the api is hosted online.
I would prefer to find a fix that's completely in angular since I can't change the API without help (it was written by my boss a while ago, and is now used in the production version). I don't have a login page so I'm just trying to provide the authentication information in my headers and requests.
My angular application was written independent of django. I just want to access the django backend
So far I'm trying the following to set the headers:
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.headers.common = {'username': btoa('myUsername'), 'password': btoa('myPassword')
};
}]);
And in my service:
app.factory('Test', ['$resource', function($resource) {
return $resource('https://www.phonywebsite.org/en/api/test/')
};
I consistently get 301, 400 and 403 errors. Lately it's been mostly 301 errors and I get no response from the api. I'm using the Allow CORS chrome extension as a temporary fix to try to get to the api without getting a CORS policy error.
My questions
How can I fix the CORS errors without using the chrome extension?
How do I provide my authentication to my django backend that uses session based authentication making sure the csrf cookie its looking for is in the header?
To answer your first question, using the cors extension is a temporary solution and should mostly never be used cause your clients might not use it. To handle CORS, you need to understand how cross site API calls work. In short CORS is a mechanism that allows AJAX requests to circumvent their same origin limits. To handle such situations you need to update your backend and add
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
. Once you add this your settings.py should stop getting CORS issues.
To answer your second question, angular already provides you with support for CSRF so half of your battle is already won. What you need to do is add a patch on your module to start accepting csrf tokens (The name is a bit different in angular). You have already done this and done a good job of it as well:
var app = angular.module('app', ['...']);
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);
What this would do is make sure that whenever you make a $http call your csrf token is set as well.
As a learning oppurtunity, you could also try using ng-cookies as well. To go further to explain this, whenever you make a call in angular , your request in bundled with cookies as well so you can easily access them in request.COOKIES.
You need to change how you are calling your API as well, something like:
app.factory('APIService', function ($http) {
return $http({url: 'https://www.phonywebsite.org/en/api/test/',
method: 'GET'})
}
You can obviously make modifications to this but I think this shows the $http usage to make you understand the general gist.
You can try to add some more authentication around your application here as well (or replace django auth with your own custom auth), but that is on your use case.
Hope this helps.
Related
I've been using JSON strings to store my data and was thinking of moving to Cloudant. I've been searching long and hard for a tutorial or example code of this but one doesn't seem to exist (there are one with Node + Angular which uses the RESTAPI, but I'm a beginner in NodeJS and can't see a way to attach it to AngularJS)
I currently use this code to connect to my local.json file
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("data.json")
.success(function(response) {$scope.experience = response.experience;});
}
Can someone help me connect to a Cloudant database?
Based on your question found below links:
https://cloudant.com/blog/chaise-blog-building-a-diary-with-angular-js-grunt-js-and-cloudant/#.VjuuD6QT2ko this article has an example code on how to connect to cloudant by:
$http({
url: [root, post._id].join('/')
, method: 'PUT'
, data: post
})
.success(function(data, status){
post._rev = data.rev;
$timeout(_autosave, 5000);
})
Accessing the DB in Cloudant with Angular refers to a cloudant article which you can apply to angularjs https://cloudant.com/for-developers/crud/#browser-update
A few things you can try:
API Keys
https://docs.cloudant.com/authorization.html
After you've read up on Cloudant API Keys, try prepending your calls with one, like this:
https://<YOUR API KEY>:<YOUR API KEY PASSWORD>#<YOUR CLOUDANT USERNAME>.cloudant.com/<YOUR DATABASE NAME>
This will, obviously, not be good for the long term, b/c you'll have your creds in your client-side code, which anyone who views your Javascript can see.
You'll probably want to dig back into Node, to avoid this. Check out Cloudant's supported library for Node: https://github.com/cloudant/nodejs-cloudant
Enable CORS in Cloudant
https://docs.cloudant.com/cors.html
This will allow your client-side code running on, say, 127.0.0.1, to make Cloudant requests. (Make sure that everyone has _reader privileges.)
There are plenty of other things you can do, too, like creating proxies, but this should get you going.
I've figured it out, finally. No idea how or why there is no clear documentation on what header values are needed at the start, figured I'd share what I found:
You first have to enable CORS settings through the cloudant website-- (Users > Cors > Enable cors) and then set withCredentials to true in your http request.
The full code to connect is available here:
https://github.com/gweennnnn/cloudantangularconnect
So for the past few months I have been developing the 'login functionality' of my Angular apps like this. The user logs in and if the credentials are correct, the REST API returns a token. I take that token and store it as a cookie:
$cookies.put('authorisation', data['token']);
Whenever I call the $http service, I submit the authorisation cookie as a header and it authorises the http request. Then on the controller of each view I add:
if (!$cookies.get('authorisation')) {
$location.path('/login');
}
So if the cookie doesn't exist, the user is automatically kicked to the login screen.
This has worked for me just fine up until now but I can't help but feel that it is not the 'correct' way of doing things. Could anyone shed a little light on what the best practice method for this could be? And perhaps why what I'm doing is 'wrong'?
Are you familiar with Angular $http Interceptors:
https://docs.angularjs.org/api/ng/service/$http#interceptors
You could use the request interceptor to have your authorization checked before each $http request.
If you do this you also have to integrate a custom Flag on each $http config object (e.g. skipAuthorization) in order to allow the user to perform Requests without being logged in (useful for actually logging in ;-))
#AzzyDude to your comment:
I'm using ui-router to do the navigation inside of my Angular 1.6.X Application.
You can either integrate own config-properties on the states (isGuestState) or if its a closed application such as mine, hard-coded in a $stateChange event, like this:
I am trying to complete a PayPal payment from an Angular app, using PayPal's classic express checkout API. In this checkout flow, I obtain a URL to send the buyer to in order to authorize their payment on PayPal's site. When I request that URL, PayPal returns a 302 response and attempts to redirect the buyer. However, when the browser attempts to redirect, I understandably get this error:
XMLHttpRequest cannot load 'PAYPAL_SITE'. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'MY_HOST' is therefore not allowed access.
What is the best way to deal with this? I have seen that PayPal also has a newer, RESTful API. If this API supports CORS, would switching to that solve the problem? Is there something I should change in my Angular configuration? From what I have read it seems like the PayPal server need certain headers to allow my host access, but I'm not too sure...
The way I solved this was to send the user away from the angular app to a "dummy" page, which just immediately made an (non-XML) HTTP request to the PayPal site. I passed the data I needed as params in the URL.
In your js:
window.location.href = "dummypage.html" + build_params();
Assuming you are using some sort of MVC framework, in your controller:
index() {
paypalUrl = getParams(paypalUrl);
redirect_to(paypalUrl);
}
When the user is confirming the payment they are redirected back to the angular app to complete the transaction. I don't believe this is an optimal solution, and it feels pretty messy to use, but I haven't come up with anything better yet!
I am trying to get a basic $resource request via angularjs. I am running this on my local computer.
However, I get a 404. A simple get request on the browser loads the json.
Here is the request shown by chrome devtools. (Question: why is the request mode: OPTIONS? Could that be the problem?)
Request URL:http://example.com/api/rentable_properties.json
Request Method:OPTIONS
Status Code:404 Not Found
Here is my controller that is attempting to make the request:
'use strict';
angular.module('adminApp')
.factory('Property', function($resource)
{
return $resource('http://example.com/api/rentable_properties.json/:propertyId');
});
angular.module('adminApp')
.controller('PropertiesCtrl', function ($scope, Property, $location) {
$scope.properties = Property.query()
The answer here is that your browser is running into a cross domain issue
See http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/ for an explanation and Why am I getting an OPTIONS request instead of a GET request? for a question basically the same as yours.
As mentioned by #JSager, the problem was a cross domain issue.
Since I had control over the server side (running a Rails server), I had to change the server to it to receive cross-site requests.
In my case, I had to install a gem and follow it's instructions.
https://github.com/cyu/rack-cors
This fixed the issue for me. No changes made on the Angularjs based app.
I'm making a call to the WebApi service, which sets the cookie in the response object.
The call is made from angularjs via $resource
So this is the server code:
CookieHeaderValue cookie = new CookieHeaderValue("Token", "blah") { HttpOnly = true, Expires = DateTime.Now.AddYears(10), Path="/" };
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
This works, I can see the Set-Cookie header in a response, however the cookie is not being set.
A friend of mine had to set xhrFields' withCredentials to true when he was using jQuery, so I wonder if there's something that needs to be configured in angular as well ?
There could be a number of things going on.
First, since you are on separate domains, you may need to implement CORs (cross origin resource sharing), but it seems that the request is being made successfully. I'm not sure why that works, I would think that browsers would prevent it. In any case here's a jsfiddle that illustrates using CORs with angularjs to make both $http & $resource requests. The trick seems to be to configure the $http service:
$http.defaults.useXDomain = true;
Another thought is that cookies from one domain, can't be accessed by another domain. Here is another question on cookies with angularjs, but the request and server seem to be on the same domain. Here is a discussion on cookie domains, and how they are applied.
If it's possible I would try to get the cookie request/response working on the same domain, and then move the client to another domain.