So I know there are a lot of CORS posts out there and I'm just adding them but I can't find any answers that can help me. So I'm building an Angular 4 application that relies on my php api. It works fine locally, when I throw it on the domain with the app at app.example.com
and the api at api.example.com
I can't Because of my login I get the following error:
XMLHttpRequest cannot load http://api.example.com/Account/Login. Response to preflight request failed access control check: No 'Access-Control-Allow-Origin' header present in request resource. So source "http://app.example.com" is 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 (this does not affect CORS) I get:
{ "error": "invalid_client", "error_description": "Client credentials were not found in the headers or body" }
I tried setting the origin to '*
' just to test and see if this was the core of the problem, but it still failed the same way.
edit Just updating with the information below. Changing the case in the header has no effect, and pulling the code out of the if statement has no effect either.
I debugged the php by telling my live application to go to the local api and the php is working as expected. It sets the header and puts it into every if statement.
Edit Shot 2 I really need some help and if anyone has any ideas I would really appreciate it.
Edit Shot 3 If I set all the header stuff in .htaccess instead of php it lets me pass. However, now I'm stuck with the error listed above, which is the error I always get when using Postman, but now when using the actual website.
{"error":"invalid_client","error_description":"Client credentials not found in header or body"}
My response headers are like this
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:*
Once it's working I'll change it from * to just my domain. But now I leave it as *.
As requested my header.
P粉9523651432023-10-22 00:47:32
In my similar case, Angular frontend and Php backend helped with the code below. First I send a header:
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");
After them I can ignore option requests:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { return 0; }
This approach helped me handle "post" and "delete" embedded request methods in Angular.
P粉8603709212023-10-22 00:29:25
Well, I had a similar problem recently and I solved it all on the backend only, without the .htaccess stuff.
When the browser sends a cross-server request, it first sends an OPTIONS request to ensure that it is valid and that a "real" request can be sent. The "real" request is sent when it gets a correct and valid response from OPTIONS.
Now, for both requests to the backend, you need to make sure the correct headers are returned: content-type, allow-origin, allow-headers, etc...
Ensure that in the OPTIONS request on the backend, the application returns the header and returns the response instead of continuing the full flow of the application.
In a "real" request you should return the correct headers and 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 use: "Access-Control-Allow-Credentials" = true Make sure "Access-Control-Allow-Origin" is not "*", it must be set to the correct domain! (A lot of blood was shed here :/)
Define the allow headers you will get in "Access-Control-Allow-Headers" If you don't define them, the request will fail