Preflight Headers Not As Expected - php

I'm trying to send a request from an Angular 8 app to Laravel 5.8 Passport API, but without success. I mean, with only a limited success. When I set withCredientials into the Angular request to true, the Preflight headers are trying to see whether the API would return proper headers, including Access-Control-Allow-Credentials: true, but the response shows that there's no such header, even though I'm setting it into the backend.
If I don't set withCredentials, the response headers include Access-Control-Allow-Credentials: true, just as expected, but I need that response in the preflight response as well.
I have tried enveloping the preflight request case in separate block, using
if (!$_SERVER['REQUEST_METHOD']=='OPTIONS')
and setting that header response explicitly, but without success as well.
A side note is that requesting that same URL from Postman works as expected (ever since Postman doesn't mess with CORS).
The request is being fired from the following code snippet:
await this.http.post(this.logInEndPoint, credentials, {
headers: this.httpHeaders,
withCredentials: true
}).subscribe(async res => {
...
The CORS middleware looks as follows:
$res->headers->set('Content-Type', 'application/json');
$res->headers->set('Access-Control-Allow-Origin', 'http://127.0.0.1:4200');
$res->headers->set('Access-Control-Allow-Credentials', 'true');
$res->headers->set('Access-Control-Max-Age', '60');
$res->headers->set('Access-Control-Allow-Headers', 'x-requested-with, Content-Type, origin, authorization, accept, client-security-token');
$res->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if (!$_SERVER['REQUEST_METHOD']=='OPTIONS') {
$next($res);
} else {
return $res;
}
And that's where I define the use of the CORS middleware
api.php
Route::middleware('web', 'json.response', 'cors')->group(function() {
Route::post('login', 'AuthController#login');
...
I expect "Successfully logged in" message, but instead got Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/login' from origin 'http://127.0.0.1:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. in the Developer Tools.

After several days of struggling with that problem, I finally figured it out.
If I have to summarize the majority of problems, related to Angular consuming Laravel APIs, I'd point out the header settings in the backend. For me it was somewhere within the custom CORS middleware.
If I have to be honest, I'm not sure what's exactly wrong in the configuration above (I guess it is in the Allow-Methods header), but I'd share how I fixed my issue.
First of all, I removed all the custom middlewares I made. I started using the Barry vd. Heuvel's CORS one, adding it for the API's group only. There, I changed the default configuration. I set allowOrigins to my Angular server URI and allowedMethods to the requests I'm expecting to use. After publishing the CORS configuration file, I made sure to run my Angular app on 127.0.0.1 (and not on localhost). The same I did for the Laravel server.
After running both the apps on one and the same IP address and using the new CORS middleware, everything ran smoothly and without problems.

Related

What i need write in SANCTUM_STATEFUL_DOMAINS, so that I can log in from a different port of the local address?

I need to login in my SPA on React, it work on 127.0.0.1:8000
Laravel working on 127.0.0.1:3000.
When i use axios request, i get error in log in chrome:
Access to XMLHttpRequest at 'http://localhost:8000/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In sanctum.php i added
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,localhost:8000,127.0.0.1:3000,::1',
Sanctum::currentApplicationUrlWithPort()
))),
In .env i write SANCTUM_STATEFUL_DOMAINS=127.0.0.1:3000 and try with SANCTUM_STATEFUL_DOMAINS=127.0.0.1:8000, but it also didn't work;
In documentation i found information for only domain and subdomain, but i dont work with domains.
Of course I can write in 'cors.php' 'paths' => ['api/*', 'sanctum/csrf-cookie', 'login'] but i think is bad solutions.
Request code axios:
const config={
headers:{
accept:'application/json',
referer:'127.0.0.1:8000/',
'Access-Control-Allow-Origin':'*'
}
}
function fetchCookie(){
const response = axios.get('http://localhost:8000/sanctum/csrf-cookie')
const response1 = axios.post('http://localhost:8000/login',loginData,config)
console.log(response1);
}
Therefore, where and what should I write to make it work for me.
Apologies in advance for my English
In your Laravel app, you'll need to set specific HTTP Response headers. These specific headers are generally called CORS headers, because they tell the browser that it's okay to send these HTTP Requests to localhost:8000.
Where can I set headers in laravel
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
This single HTTP Response header should be the minimum you need:
Access-Control-Allow-Origin: *
But you can also be more explicit:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Needed create a middleware how answered #RaphaelRafatpanah and added in it headers.
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$response->header('Access-Control-Allow-Origin', 'http://127.0.0.1:3000');
$response->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
$response->header('Access-Control-Allow-Headers', 'x-xsrf-token,Content-Type,withcredentials');
$response->header('Access-Control-Allow-Credentials', 'true');
return $response;
}
These headers will solve the initial errors and subsequent.

React + PHP API throws CORS preflight error

I am trying to call a PHP API running on localhost:8000 from a React app running on localhost:3000. After many tries I am still getting "CORS Preflight Did Not Succeed" error.
Sent from the React app:
Sent from the devtools:
My API has following headers:
if (#$_SERVER['HTTP_ORIGIN']) {
header("Origin: http://localhost:8000");
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
}
I call the API with fetch like this (but it somehow sends empty request body):
let inputData:object = {email, password}
fetch("http://localhost:8000/data/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(inputData)
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
The strange thing is that the requests are working normally when sent directly from the browser devtools (2nd screenshot) or API clients like Insomnia:
Problem
Your first screenshot indicates that the response to the preflight request has status code 404. However, a necessary condition for CORS preflight to succeed is an ok status (i.e. a status in the range 2xx). See the relevant section (3.2.3) of the Fetch standard:
A successful HTTP response, i.e., one where the server developer intends to share it, to a CORS request can use any status, as long as it includes the headers stated above with values matching up with the request.
A successful HTTP response to a CORS-preflight request is similar, except it is restricted to an ok status, e.g., 200 or 204.
(my emphasis)
Solution
Make sure your server responds with a 2xx status to preflight requests that are meant to succeed.
Additional remarks
Allowing the Origin header is never necessary, simply because it's set by the user agent. You can drop Origin from the value of the Access-Control-Allow-Headers response header.
Why you're setting an Origin header in the response is unclear... Origin is a request header. You should be able to drop that header("Origin: http://localhost:8000"); line.
Instead of "manually" implementing CORS (which is error-prone), you should consider using a proven CORS middleware.
Your cors origin must be localhost:3000.
header("Origin: http://localhost:3000");
Because your frontend running on 3000.
Where the request comes from should be added as cors definition.
Make sure you are not outputting anything in php before returning the response to the frontend application. A simple echo "test"; or a print_r, vardump etc. can trigger this error.
Also, make sure there are no empty lines before the opening <?php tags since they send a premature response to the frontend that may cause this error.

Why does it seem like the HTTP OPTIONS method posts data in the database? [duplicate]

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

Weird behavior of PHP fix for CORS policy

I am developing an application using Angular which runs on port 4200 (localhost). It gets the information from PHP REST API which runs on post 80 (localhost). When I developed a contact us page it was throwing error related to CORS policy which was fixed by placing the following header information in .htaccess file.
Header add Access-Control-Allow-Origin "http://localhost:4200"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Header add Access-Control-Max-Age "36000"
The contact us page is working fine now and I am able to post information (queries) and they are updating in the database correctly.
But now I have developed register user functionality and I am facing a weird problem here. Sometimes I am seeing the CORS policy issue that
Access to XMLHttpRequest at 'http://localhost/carrentalnew/register' from origin 'http://localhost:4200'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does
not have HTTP ok status.
So when I get this error, I am going to PHP api program I developed, and entering some info in the middle of the script so that the script fails.
For example the following is piece of code where in the middle I entered "hello" so that the script fails.
catch(PDOException $ex) {
// log connection error for troubleshooting and return a json error response
error_log("Connection Error: ".$ex, 0);
$response = new Response();
$response->setHttpStatusCode(500);
$response->setSuccess(false);
$response->addMessage("Database connection error");
$response->send();
exit;
}
hello
// handle creating new user
// check to make sure the request is POST only - else exit with error response
if($_SERVER['REQUEST_METHOD'] !== 'POST') {
$response = new Response();
$response->setHttpStatusCode(405);
$response->setSuccess(false);
$response->addMessage("Request method not allowed");
$response->send();
exit;
}
Now I am removing the word "hello" and I am trying to post the register user again and it is working fine and no issues at all. This is a very weird behavior I am seeing. Can any one tell me what mistake I am doing here?
Thanks,
Subbu.
For cross origin requests, browser sends pre-flight request with Method Options.
In that case we have to send response with access control allow origin header
In your case as you have already added in ht access, we can just exit if it is options method
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
return 0;
}
As you are sending http error response, browser thinks this is a invalid Cross origin request

Safari on IOS ignoring CORS / not sending CORS preflight OPTIONS request

I have a problem using Axios and VUEJS where I keep on getting an error xmlhttprequest cannot load XXX due to access control checks on Safari only. The application works find on Chrome and Brave, but not Safari.
In addition, Safari seems to make a CORS request for the expected URL, but then cancels it without waiting for the response.
My endpoint is written in PHP
$origin = 'http://192.168.1.6:8080'; // For testing
$response = $response->withAddedHeader('Access-Control-Allow-Origin', $origin);
$response = $response->withAddedHeader('Access-Control-Allow-Headers', 'Authorization,Origin,X-Requested-With,Content-Type,Range');
$response = $response->withAddedHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
The problem seems to be due Safari not having previously defined CORS data for the website. The API call in Axios was the result of a 302 response (I don't know if that is relevant) and for whatever reason it did not like that.
The solution was to add a ping request that established CORS (at least, that's what I think it's doing) when the application first loads. Like this:
this.$axios.get('api/ping').then(() => {
if (localStorage.getItem('id_token')) {
this.$router.push('/app/requests')
}
})
That ping, which just has an unauthenticated reply with CORS headers, allows subsequent API calls to execute just fine.

Categories