I'm using AngularJS v1.2.4.
I had an issue with Angular sending a preflight OPTIONS call (Chrome was showing the OPTIONS call as 'canceled') and resolved it with:
$httpProvider.defaults.useXDomain = true; delete $httpProvider.defaults.headers.common['X-Requested-With'];
That worked for all my $resource calls, and everything was good.
Now I'm trying to implementation authentication, and a login page that sends a POST request to my server with the user's credentials. I'm seeing the problem I was facing before, but $resource calls are still working fine.
What's really frustrating is that the problem happens intermittently; I'll change a few options surrounding the headers, then it'll work for a bit, and stop working again without any code change.
My server is configured for CORS and works fine with curl, and other REST clients. Here's an example:
curl -X OPTIONS -ik 'https://localhost:3001/authenticate' -H "Origin: https://localhost:8001" HTTP/1.1 200 OK content-type: application/json; charset=utf-8 content-length: 2 cache-control: no-cache access-control-allow-origin: * access-control-max-age: 86400 access-control-allow-methods: GET, HEAD, POST, PUT, DELETE, OPTIONS access-control-allow-headers: Authorization, Content-Type, If-None-Match, Access-Control-Allow-Headers, Content-Type access-control-expose-headers: WWW-Authenticate, Server-Authorization set-cookie: session=Fe26.2**94705d49717d1273197ae86ce6661775627d7c6066547b757118c90c056e393b*2KYqhATojPoQhpB2OwhDwg*W9GsJjK-F-UPqIIHTBHHZx1RXipo0zvr97_LtTLMscRkKqLqr8H6WiGd2kczVwL5M25FBlB1su0JZllq2QB-9w**5510263d744a9d5dc879a89b314f6379d17a39610d70017d60acef01fa63ec10*pkC9zEOJTY_skGhb4corYRGkUNGJUr8m5O1US2YhaRE; Secure; Path=/ Date: Wed, 18 Dec 2013 23:35:56 GMT Connection: keep-alive
Here's the $http.post call:
var authRequest = $http.post('https://' + $location.host() + ':3001/authenticate', {email: email, password: password});
When the call from my app works, this is how the OPTIONS request looks like:
When it doesn't work, this is the OPTIONS request:
It looks like a whole bunch of header attributes are missing. Has anyone encountered a similar issue?
Edit:
Just to clarify, when it doesn't work, the request never makes it to the server - it's instantly aborted in the browser.
In Firebug, the request headers are:
OPTIONS /authenticate HTTP/1.1 Host: localhost:3001 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:25.0) Gecko/20100101 Firefox/25.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.91,en-GB;q=0.82,fr-FR;q=0.73,fr;q=0.64,utf-8;q=0.55,utf;q=0.45,de-DE;q=0.36,de;q=0.27,en-sg;q=0.18,en-ca;q=0.09 Accept-Encoding: gzip, deflate Origin: https://localhost:8001 Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type Proxy-Authorization: Basic cGF0cmljZUB6b25nLmNvbTpjaGFuZ2VtZQ== Connection: keep-alive Pragma: no-cache Cache-Control: no-cache
Update:
I've eliminated the possibly of a problem with the server, I think, by changing the host to a non-existent server. Still seeing the same behavior.
Here's some code:
App.services.factory('AuthService', function ($http, $location, $q) { var currentUser; return { authenticate: function (email, password) { //promise to return var deferred = $q.defer(); var authRequest = $http.post('https://this.does.not.exist.com:3001/authenticate', {email: email, password: password}); authRequest.success(function (data, status, header, config) { currentUser = data; console.log('currentUser in service set to:'); console.log(currentUser); //resolve promise deferred.resolve(); }); authRequest.error(function (data, status, header, config) { console.log('authentication error'); console.log(status); console.log(data); console.log(header); console.log(config); //reject promise deferred.reject('authentication failed..'); }); return deferred.promise; }, isAuthenticated: function () { return currentUser !== undefined; } }; });
and HTTP Config:
App.config(['$httpProvider', function ($httpProvider) { $httpProvider.defaults.useXDomain = true; //$httpProvider.defaults.headers.common = {}; console.log('logging out headers'); console.log($httpProvider.defaults); console.log($httpProvider.defaults.headers.common); console.log($httpProvider.defaults.headers.post); console.log($httpProvider.defaults.headers.put); console.log($httpProvider.defaults.headers.patch); console.log('end logging out headers'); $httpProvider.defaults.headers.common = {Accept: "application/json, text/plain, */*"}; $httpProvider.defaults.headers.post = {"Content-Type": "application/json;charset=utf-8"}; console.log('after: logging out headers'); console.log($httpProvider.defaults.headers.common); console.log($httpProvider.defaults.headers.post); console.log($httpProvider.defaults.headers.put); console.log($httpProvider.defaults.headers.patch); console.log('after: end logging out headers'); $httpProvider.interceptors.push(function ($location, $injector) { return { 'request': function (config) { console.log('in request interceptor!'); var path = $location.path(); console.log('request: ' + path); //injected manually to get around circular dependency problem. var AuthService = $injector.get('AuthService'); console.log(AuthService); console.log(config); if (!AuthService.isAuthenticated() && $location.path() != '/login') { console.log('user is not logged in.'); $location.path('/login'); } //add headers console.log(config.headers); return config; } }; }); }]);
Preflight is a web security feature implemented by the browser. For Chrome you can disable all web security by adding the --disable-web-security flag. For example: "C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:\newChromeSettingsWithoutSecurity" .
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers. It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method , Access-Control-Request-Headers , and the Origin header.
A preflight request is a small request that is sent by the browser before the actual request. It contains information like which HTTP method is used, as well as if any custom HTTP headers are present. The preflight gives the server a chance to examine what the actual request will look like before it's made.
This feels like it might be related to the fact that you're hitting an https
endpoint on your localhost. That means you're probably using some sort of self-signed SSL certificate, which may mean Chrome considers it untrusted.
I'd first try going directly to the /authenticate endpoint and see if Chrome gives you a warning about an untrusted certificate. See if accepting that warning works.
Otherwise, possibly while you're testing locally you can hit just an http
endpoint and see if that solves things?
A huge thank you to Michael Cox for pointing me in the right direction. I accept his answer since it led me to the solution, but here are more details:
Looking into the https issue, I found:
My problem was slightly different though. It still wasn't working after I followed the instructions in the links above. I read the chrome "untrusted" message carefully and it was something like "you're trying to access mylocalhost.com but the server is representing itself as ".
It turns out that my hastily created self signed certificate was "server.crt" when it should be "mylocalhost.crt"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With