So I know there's a lot of CORS posts out there, and I'm just adding to them, but I can't find any with answers that help me out. So I'm building an angular 4 application that relies on my php api. Working locally it's fine, the moment I toss it up on the domain with the app at app.example.com, and the api at api.example.com, I can't get past my login, because I get the following error:
XMLHttpRequest cannot load http://api.example.com/Account/Login.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://app.example.com' is therefore not allowed
access.
My php code looks like this:
$http_origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = array(
'http://example.com',
'https://example.com',
'http://app.example.com',
'https://app.example.com',
'http://www.example.com',
'https://www.example.com'
);
if (in_array(strtolower($http_origin), $allowed_domains))
{
// header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Origin: $http_origin");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Authorization, Content-Type,Accept, Origin");
exit(0);
}
My Angular post looks like this:
public login(login: Login): Observable<LoginResponse> {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization', 'Basic ' + btoa(login.Username + ':' + login.Password));
return this.http.post(this.apiBaseUrl + '/Account/Login', "grant_type=client_credentials", { headers: headers })
.map(response => {
// code
});
}
If I run the request through postman, which doesn't bother with CORS, I get:
{ "error": "invalid_client", "error_description": "Client credentials were not found in the headers or body" }
I've tried setting origin to '*' just to test and see if that was the core of the issue, and it still fails the same way.
Edit
Just updating from information below. Changing casing in headers had no effect, and pulling the code out of their if statements had no effect.
I debugged the php by telling my live app to go to my local api, and the php is working as expected. It's setting the headers and making it into each of the if statements.
Edit take 2
I could really use some help on this one, if someone has any ideas, I'd really appreciate it.
Edit take 3
If I set all the header stuff in my .htaccess rather than my php, it lets me through. However, now I'm stuck on the error listed above that I always get when using postman, however now it's while using the actual site.
{"error":"invalid_client","error_description":"Client credentials were not found in the headers or body"}
My response headers are like so
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:authorization, content-type, accept, origin
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:*
I'll be changing it from * to only my domains once I have it working. But for now i'll leave it as *.
My headers as requested.
OK I had a similar issues recently and I solved everything only on the backend side with no .htaccess stuff.
when the browser sends cross server requests it firsts sends an OPTIONS request to make sure it is valid and it can send the "real" request.
After it gets a proper and valid response from OPTIONS, only then it sends the "real" request.
Now for both request on the backend you need to make sure to return the proper headers: content-type, allow-origin, allow-headers etc...
Make sure that in the OPTIONS request on the backend, the app returns the headers and returns the response, not continuing the full flow of the app.
In the "real" request, you should return the proper headers and your regular response body.
example:
//The Response object
$res = $app->response;
$res->headers->set('Content-Type', 'application/json');
$res->headers->set('Access-Control-Allow-Origin', 'http://example.com');
$res->headers->set('Access-Control-Allow-Credentials', 'true');
$res->headers->set('Access-Control-Max-Age', '60');
$res->headers->set('Access-Control-Allow-Headers', 'AccountKey,x-requested-with, Content-Type, origin, authorization, accept, client-security-token, host, date, cookie, cookie2');
$res->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if ( ! $req->isOptions()) {
// this continues the normal flow of the app, and will return the proper body
$this->next->call();
} else {
//stops the app, and sends the response
return $res;
}
Things to remember:
if you are using: "Access-Control-Allow-Credentials" = true
make sure that "Access-Control-Allow-Origin" is not "*", it must be set with a proper domain!
( a lot of blood was spilled here :/ )
define the allowed headers you will get in "Access-Control-Allow-Headers"
if you wont define them, the request will fail
if you using "Authorization: Bearer", then "Access-Control-Allow-Headers" should also contain "Authorization", if not, the request will fail
In my similar case with Angular frontend and Php backend helped code below. Firstly I send a headers:
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
And after them I'm enable ignoring the options request:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
return 0;
}
This approach helped me handling the "post" and the "delete" embedded request methods from the Angular.
I added below in php and it solved my problem.
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type, origin");
I had a similar issue,
Dev environment: Apache web server behind NginX Proxy
My app is in a virtual host in my Apache server,
configured with name: appname.devdomain.com
When accessing to web app internaly I wasn´t getting through the proxy:
I was using the url: appname.devdomain.com
I had no problem this way.
But, when accessing it externally using public url: appname.prddomain.com
it would load, even got access to the system after login, then load the templates and some session content, then, if an asynchronous call would be made by the client then I would got the following message in chrome console:
"Access to XMLHttpRequest at 'http://appname.devdomain.com/miAdminPanel.php' from origin 'http://appname.prddomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request."
To test this I opened two tabs
1st tab accessed the web using url: appname.devdomain.com, made XMLHttpRequest -> OK
2nd tab accessed the web using url: appname.prddomain.com, made XMLHttpRequest -> CORS error message above.
So, after changing the Nginx proxy configuration:
server {
listen 80;
server_name appname.prddomain.com;
# SSL log files ###
access_log /path/to/acces-log.log;
error_log /path/to/error-log.log;
location / {
proxy_pass http://appname.devdomain.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
basically this tells the proxy to treat the external request as internal request.
Related
So I know there's a lot of CORS posts out there, and I'm just adding to them, but I can't find any with answers that help me out. So I'm building an angular 4 application that relies on my php api. Working locally it's fine, the moment I toss it up on the domain with the app at app.example.com, and the api at api.example.com, I can't get past my login, because I get the following error:
XMLHttpRequest cannot load http://api.example.com/Account/Login.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://app.example.com' is therefore not allowed
access.
My php code looks like this:
$http_origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = array(
'http://example.com',
'https://example.com',
'http://app.example.com',
'https://app.example.com',
'http://www.example.com',
'https://www.example.com'
);
if (in_array(strtolower($http_origin), $allowed_domains))
{
// header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Origin: $http_origin");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Authorization, Content-Type,Accept, Origin");
exit(0);
}
My Angular post looks like this:
public login(login: Login): Observable<LoginResponse> {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization', 'Basic ' + btoa(login.Username + ':' + login.Password));
return this.http.post(this.apiBaseUrl + '/Account/Login', "grant_type=client_credentials", { headers: headers })
.map(response => {
// code
});
}
If I run the request through postman, which doesn't bother with CORS, I get:
{ "error": "invalid_client", "error_description": "Client credentials were not found in the headers or body" }
I've tried setting origin to '*' just to test and see if that was the core of the issue, and it still fails the same way.
Edit
Just updating from information below. Changing casing in headers had no effect, and pulling the code out of their if statements had no effect.
I debugged the php by telling my live app to go to my local api, and the php is working as expected. It's setting the headers and making it into each of the if statements.
Edit take 2
I could really use some help on this one, if someone has any ideas, I'd really appreciate it.
Edit take 3
If I set all the header stuff in my .htaccess rather than my php, it lets me through. However, now I'm stuck on the error listed above that I always get when using postman, however now it's while using the actual site.
{"error":"invalid_client","error_description":"Client credentials were not found in the headers or body"}
My response headers are like so
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:authorization, content-type, accept, origin
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:*
I'll be changing it from * to only my domains once I have it working. But for now i'll leave it as *.
My headers as requested.
OK I had a similar issues recently and I solved everything only on the backend side with no .htaccess stuff.
when the browser sends cross server requests it firsts sends an OPTIONS request to make sure it is valid and it can send the "real" request.
After it gets a proper and valid response from OPTIONS, only then it sends the "real" request.
Now for both request on the backend you need to make sure to return the proper headers: content-type, allow-origin, allow-headers etc...
Make sure that in the OPTIONS request on the backend, the app returns the headers and returns the response, not continuing the full flow of the app.
In the "real" request, you should return the proper headers and your regular response body.
example:
//The Response object
$res = $app->response;
$res->headers->set('Content-Type', 'application/json');
$res->headers->set('Access-Control-Allow-Origin', 'http://example.com');
$res->headers->set('Access-Control-Allow-Credentials', 'true');
$res->headers->set('Access-Control-Max-Age', '60');
$res->headers->set('Access-Control-Allow-Headers', 'AccountKey,x-requested-with, Content-Type, origin, authorization, accept, client-security-token, host, date, cookie, cookie2');
$res->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if ( ! $req->isOptions()) {
// this continues the normal flow of the app, and will return the proper body
$this->next->call();
} else {
//stops the app, and sends the response
return $res;
}
Things to remember:
if you are using: "Access-Control-Allow-Credentials" = true
make sure that "Access-Control-Allow-Origin" is not "*", it must be set with a proper domain!
( a lot of blood was spilled here :/ )
define the allowed headers you will get in "Access-Control-Allow-Headers"
if you wont define them, the request will fail
if you using "Authorization: Bearer", then "Access-Control-Allow-Headers" should also contain "Authorization", if not, the request will fail
In my similar case with Angular frontend and Php backend helped code below. Firstly I send a headers:
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
And after them I'm enable ignoring the options request:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
return 0;
}
This approach helped me handling the "post" and the "delete" embedded request methods from the Angular.
I added below in php and it solved my problem.
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type, origin");
I had a similar issue,
Dev environment: Apache web server behind NginX Proxy
My app is in a virtual host in my Apache server,
configured with name: appname.devdomain.com
When accessing to web app internaly I wasn´t getting through the proxy:
I was using the url: appname.devdomain.com
I had no problem this way.
But, when accessing it externally using public url: appname.prddomain.com
it would load, even got access to the system after login, then load the templates and some session content, then, if an asynchronous call would be made by the client then I would got the following message in chrome console:
"Access to XMLHttpRequest at 'http://appname.devdomain.com/miAdminPanel.php' from origin 'http://appname.prddomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request."
To test this I opened two tabs
1st tab accessed the web using url: appname.devdomain.com, made XMLHttpRequest -> OK
2nd tab accessed the web using url: appname.prddomain.com, made XMLHttpRequest -> CORS error message above.
So, after changing the Nginx proxy configuration:
server {
listen 80;
server_name appname.prddomain.com;
# SSL log files ###
access_log /path/to/acces-log.log;
error_log /path/to/error-log.log;
location / {
proxy_pass http://appname.devdomain.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
basically this tells the proxy to treat the external request as internal request.
I am trying to set up a sandbox API for development, so users can hit the API from local dev machines. But am running into what I believe is a CORS issue.
At first all traffic was being blocked by the CORS policy, so I added the following to my .htaccess file:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
After that, I can see that not only am I getting a 200 status, but my payload is being correctly sent. Good stuff!
However .. The PHP file that is requesting -> /myp/index.php contains ONLY the following:
<?php
print_r ($_POST);
echo "Done";
And the response from the above POST comes back:
Array
(
)
Done
I have read MANY posts that all say the same thing: Add the following to the PHP file:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, X-Auth-Token, Accept");
header ("Access-Control-Expose-Headers: Content-Length, X-JSON");
But when I add that to the PHP file .. I get another CORS error..
WITH CORS RULES APPLIED TO PHP FILE
What else do I need to be looking at? Why is CORS denied when I throw the PHP headers in, but is OK without them? And why does PHP not accept the POST variables when CORS shows a 200 and I can verify post data sent?
And the response from the above POST comes back:
The key there is that you do get the response and you don’t get a CORS error.
So the problem isn’t CORS and you shouldn’t touch your CORS headers.
(When you add the headers with PHP, Apache merges them with the headers you tell it to add and you end up with Access-Control-Allow-Origin: *, * which, as the error message says, is invalid).
If the $_POST superglobal is empty, it is because you aren’t POSTing data in a format that PHP will decode automatically.
Most likely, you are sending a JSON payload in which case you need to either read the body from STDIN and parse it yourself or change the data you are sending to be in a format that PHP does support (URL Encoded or Multipart Encoded).
const body = new URLSearchParams({ foo: 1, bar: "hello, world" });
const url = "http://example.com";
const response = await fetch(url, { body });
Even though I set the headers in the file functions.php, the error keeps appearing, I tried with several different hooks:
function add_cors_http_header()
{
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
// header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// may also be using PUT, PATCH, HEAD etc
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
}
add_action('init', 'add_cors_http_header');
add_action('send_headers', 'add_cors_http_header');
add_action('rest_pre_serve_request', 'add_cors_http_header');
(Not all add_action at the same time)
And I also tried without the isset and in the header.php
EDIT ---
As requested, the client that is running is Vanilla Javascript, I made the same fetch on Postman and it worked, it's about joining a user to a guild in discord, hre is the code:
const args = JSON.stringify({
access_token: token,
});
const response = await fetch(
`https://discord.com/api/guilds/${this.guildID}/members/${userID}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `BOT ${this.bot_token}`,
},
body: args,
}
);
Access-Control-Allow-Origin (and similar headers) are for the server to define, and not the client. In this case, the server is the Discord API.
I'm guessing the confusion here is that your client is JavaScript, but you're trying to modify the headers in PHP. I think you should try to add the headers in JavaScript directly. Perhaps there's an Origin header you need?
I suggest looking at your Postman headers being sent and add them right in your JavaScript. For example:
headers: {
"Content-Type": "application/json",
Authorization: `BOT ${this.bot_token}`,
Origin: "http://localhost",
},
If that doesn't work, open your Chrome Dev Tools, open the Network tab, and look at the outgoing request, and see what headers it is sending and compare them with Postman. If Postman can work, just repeat what it is doing.
Regarding the WordPress function, I think you can remove it entirely as JavaScript is the client, not PHP/WordPress.
I'm trying to send some HTTP requests from my angular.js application to my server, but I need to solve some CORS errors.
The HTTP request is made using the following code:
functions.test = function(foo, bar) {
return $http({
method: 'POST',
url: api_endpoint + 'test',
headers: {
'foo': 'value',
'content-type': 'application/json'
},
data: {
bar:'value'
}
});
};
The first try ended up with some CORS errors. So I've added the following lines to my PHP script:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding, X-Auth-Token, content-type');
The first error is now eliminated.
Now the Chrome's developer console shows me the following errors:
angular.js:12011 OPTIONS http://localhost:8000/test (anonymous
function)
423ef03a:1 XMLHttpRequest cannot load
http://localhost:8000/test. Response for preflight has invalid HTTP
status code 400
and the network request looks like I expected (HTTP status 400 is also expected):
I can't imagine how to solve the thing (and how to understand) why the request will send on localhost as OPTIONS and to remote servers as POST. Is there a solution how to fix this strange issue?
TL;DR answer
Explanation
The OPTIONS request is so called pre-flight request, which is part of Cross-origin resource sharing (CORS). Browsers use it to check if a request is allowed from a particular domain as follows:
The browser wants to send a request to a particular URL, let's say a POST request with the application/json content type
First, it sends the pre-flight OPTIONS request to the same URL
What follows depends on the pre-flight request's response HTTP status code:
If the server replies with a non-2XX status response, the browser won't send the actual request (because he knows now that it would be refused anyway)
If the server replies with a HTTP 200 OK (or any other 2XX) response, the browser will send the actual request, POST in your case
Solution
So, in your case, the proper header is present, you just have to make sure the pre-flight request's response HTTP status code is 200 OK or some other successful one (2XX).
Detailed Explanation
Simple requests
Browsers are not sending the pre-flight requests in some cases, those are so-called simple requests and are used in the following conditions:
One of the allowed methods:
- GET
- HEAD
- POST
Apart from the headers automatically set by the user agent (for example, Connection, User-Agent, etc.), the only headers which are allowed to be manually set are the following:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
Such requests are sent directly and the server simply successfully processes the request or replies with an error in case it didn't match the CORS rules. In any case, the response will contain the CORS headers Access-Control-Allow-*.
Pre-flighted requests
Browsers are sending the pre-flight requests if the actual request doesn't meet the simple request conditions, the most usually:
custom content types like application/xml or application/json, etc., are used
the request method is other than GET, HEAD or POST
the POST method is of an another content type than application/x-www-form-urlencoded, multipart/form-data or text/plain
You need to make sure that the response to the pre-flight request has the following attributes:
successful HTTP status code, i.e. 200 OK
header Access-Control-Allow-Origin: * (a wildcard * allows a request from any domain, you can use any specific domain to restrict the access here of course)
From the other side, the server may refuse the CORS request simply by sending a response to the pre-flight request with the following attributes:
non-success HTTP code (i.e. other than 2XX)
success HTTP code (e.g. 200 OK), but without any CORS header (i.e. Access-Control-Allow-*)
See the documentation on Mozilla Developer Network or for example HTML5Rocks' CORS tutorial for details.
I ran into a very similar problem writing an Angular 2 app - that uses a NODE server for the API.
Since I am developing on my local machine, I kept getting Cross Origin Header problems, when I would try to POST to the API from my Angular app.
Setting the Headers (in the node server) as below worked for GET requests, but my PUT requests kept posting empty objects to the database.
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type,
Origin, Authorization, Accept, Client-Security-Token, Accept-
Encoding, X-Auth-Token, content-type');
After reading Dawid Ferenczy's post, I realized that the PREFLIGHT request was sending blank data to my server, and that's why my DB entries were empty, so I added this line in the NODE JS server:
if (req.method == "OPTIONS")
{
res.writeHead(200, {"Content-Type": "application/json"});
res.end();
}
So now my server ignores the PREFLIGHT request, (and returns status 200, to let the browser know everything is groovy...) and that way, the real request can go through and I get real data posted to my DB!
Just put
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;}
at the beginning of your serverside app and you should be fine.
For spring boot application, to enable cors request, use #CrossOrigin(origins = "*", maxAge = 3600) on your respective controller.
Refer this doc
The best is to :
have proxy.conf.json set:
{
"/api": {
"target": "http://localhost:8080",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
}
}
And then to make sure that URL that you are using in angular to send a request is relative (/api/something) and not absolute (localhost:8080/api/something). Because in that case the proxy won't work.
From Chrome v79+, OPTIONS Check(pre-flight request) will no longer appear in the network tab-Source
So I know there's a lot of CORS posts out there, and I'm just adding to them, but I can't find any with answers that help me out. So I'm building an angular 4 application that relies on my php api. Working locally it's fine, the moment I toss it up on the domain with the app at app.example.com, and the api at api.example.com, I can't get past my login, because I get the following error:
XMLHttpRequest cannot load http://api.example.com/Account/Login.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://app.example.com' is therefore not allowed
access.
My php code looks like this:
$http_origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = array(
'http://example.com',
'https://example.com',
'http://app.example.com',
'https://app.example.com',
'http://www.example.com',
'https://www.example.com'
);
if (in_array(strtolower($http_origin), $allowed_domains))
{
// header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Origin: $http_origin");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Authorization, Content-Type,Accept, Origin");
exit(0);
}
My Angular post looks like this:
public login(login: Login): Observable<LoginResponse> {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization', 'Basic ' + btoa(login.Username + ':' + login.Password));
return this.http.post(this.apiBaseUrl + '/Account/Login', "grant_type=client_credentials", { headers: headers })
.map(response => {
// code
});
}
If I run the request through postman, which doesn't bother with CORS, I get:
{ "error": "invalid_client", "error_description": "Client credentials were not found in the headers or body" }
I've tried setting origin to '*' just to test and see if that was the core of the issue, and it still fails the same way.
Edit
Just updating from information below. Changing casing in headers had no effect, and pulling the code out of their if statements had no effect.
I debugged the php by telling my live app to go to my local api, and the php is working as expected. It's setting the headers and making it into each of the if statements.
Edit take 2
I could really use some help on this one, if someone has any ideas, I'd really appreciate it.
Edit take 3
If I set all the header stuff in my .htaccess rather than my php, it lets me through. However, now I'm stuck on the error listed above that I always get when using postman, however now it's while using the actual site.
{"error":"invalid_client","error_description":"Client credentials were not found in the headers or body"}
My response headers are like so
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:authorization, content-type, accept, origin
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:*
I'll be changing it from * to only my domains once I have it working. But for now i'll leave it as *.
My headers as requested.
OK I had a similar issues recently and I solved everything only on the backend side with no .htaccess stuff.
when the browser sends cross server requests it firsts sends an OPTIONS request to make sure it is valid and it can send the "real" request.
After it gets a proper and valid response from OPTIONS, only then it sends the "real" request.
Now for both request on the backend you need to make sure to return the proper headers: content-type, allow-origin, allow-headers etc...
Make sure that in the OPTIONS request on the backend, the app returns the headers and returns the response, not continuing the full flow of the app.
In the "real" request, you should return the proper headers and your regular response body.
example:
//The Response object
$res = $app->response;
$res->headers->set('Content-Type', 'application/json');
$res->headers->set('Access-Control-Allow-Origin', 'http://example.com');
$res->headers->set('Access-Control-Allow-Credentials', 'true');
$res->headers->set('Access-Control-Max-Age', '60');
$res->headers->set('Access-Control-Allow-Headers', 'AccountKey,x-requested-with, Content-Type, origin, authorization, accept, client-security-token, host, date, cookie, cookie2');
$res->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if ( ! $req->isOptions()) {
// this continues the normal flow of the app, and will return the proper body
$this->next->call();
} else {
//stops the app, and sends the response
return $res;
}
Things to remember:
if you are using: "Access-Control-Allow-Credentials" = true
make sure that "Access-Control-Allow-Origin" is not "*", it must be set with a proper domain!
( a lot of blood was spilled here :/ )
define the allowed headers you will get in "Access-Control-Allow-Headers"
if you wont define them, the request will fail
if you using "Authorization: Bearer", then "Access-Control-Allow-Headers" should also contain "Authorization", if not, the request will fail
In my similar case with Angular frontend and Php backend helped code below. Firstly I send a headers:
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
And after them I'm enable ignoring the options request:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
return 0;
}
This approach helped me handling the "post" and the "delete" embedded request methods from the Angular.
I added below in php and it solved my problem.
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type, origin");
I had a similar issue,
Dev environment: Apache web server behind NginX Proxy
My app is in a virtual host in my Apache server,
configured with name: appname.devdomain.com
When accessing to web app internaly I wasn´t getting through the proxy:
I was using the url: appname.devdomain.com
I had no problem this way.
But, when accessing it externally using public url: appname.prddomain.com
it would load, even got access to the system after login, then load the templates and some session content, then, if an asynchronous call would be made by the client then I would got the following message in chrome console:
"Access to XMLHttpRequest at 'http://appname.devdomain.com/miAdminPanel.php' from origin 'http://appname.prddomain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request."
To test this I opened two tabs
1st tab accessed the web using url: appname.devdomain.com, made XMLHttpRequest -> OK
2nd tab accessed the web using url: appname.prddomain.com, made XMLHttpRequest -> CORS error message above.
So, after changing the Nginx proxy configuration:
server {
listen 80;
server_name appname.prddomain.com;
# SSL log files ###
access_log /path/to/acces-log.log;
error_log /path/to/error-log.log;
location / {
proxy_pass http://appname.devdomain.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
basically this tells the proxy to treat the external request as internal request.