I crafted an API with Symfony 4 that uses a custom token verification. I tested the API on Postman and everything works perfectly, now I want to use the API using jQuery and fetch all the data , but in the browser, I'm facing CORS issues like below:
Access to XMLHttpRequest at 'http://localhost:8000/api/reports' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Here is my server-side API:
I've implemented a CORSEventSubscriber to allow the CORS like below :
class CORSSubscriber implements EventSubscriberInterface
{
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function onKernelResponse(FilterResponseEvent $event)
{
$responseHeaders = $event->getResponse()->headers;
$responseHeaders->set('Access-Control-Allow-Origin', '*');
$responseHeaders->set('Access-Control-Allow-Headers', 'x-auth-token, content-type');
$responseHeaders->set('Access-Control-Allow-Methods', 'POST, GET');
}
/**
* @inheritDoc
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onKernelResponse',
];
}
This is the action that I'm calling in the controller:
/**
* @Route("/api/reports",name="reports",methods={"GET","OPTIONS"})
* @param Request $request
* @return Response
* @throws Exception
*/
function getReports(Request $request){
return new JsonResponse('test', Response::HTTP_UNAUTHORIZED);
}
I tried consuming the API like this
<script>
$(document).ready(function(){
authenticate().then(function (data) {
// the promise resolve the token and data.token output the correct value
$.ajax({
url:'http://localhost:8000/api/reports',
type: 'GET',
headers: {'X-Auth-Token' : data.token },
success: function (data) {
//append data to your app
console.log(data);
}
})
})
});
function authenticate() {
let data={
"username": "test",
"password": "test"
};
return new Promise(function(resolve, reject) {
$.ajax({
url:'http://localhost:8000/api/auth/check',
type:'POST',
data:JSON.stringify(data),
dataType:'Json',
success: function (data) {
console.log(data);
resolve(data);
},
error:function () {
}
})
});
}
</script>
I added this to debug closely the issue and i found out that this function only executes for POST when there's a token method like OPTIONS it doesn't execute
public function onKernelRequest(GetResponseEvent $event)
{
$this->logger->info($event->getRequest()->getRealMethod());
}
You are making a cross-origin request and adding a non-standard header. This means it is a Preflighted Request.
The browser is sending an OPTIONS request to ask permission to make the request with custom headers.
You can't control the format of the preflight request. You definitely can't add credentials to it. (Adding credentials is another thing which turns a simple request into a preflighted request).
You need to respond to the OPTIONS request with permission via CORS headers. Since the request won't have any credentials associated with it your server must not require credentials.
Change the server to remove the requirement for the credentials when the request type of OPTIONS.
I don't know my way around the server-side framework you are using, but extrapolating from the code you've provided I suspect you should provide separate routes for GET and OPTIONS.
The OPTIONS request should concern itself only with CORS (and not fetch any data which requires authorisation).
The GET request should require authorisation and return the data.
After days through this, I fixed it by adding to the CorsSubscriber
public function onKernelResponse(FilterResponseEvent $event)
{
$responseHeaders = $event->getResponse()->headers;
$responseHeaders->set('Access-Control-Allow-Origin', 'http://localhost:8080');
$responseHeaders->set('Access-Control-Allow-Credentials', 'true');
$responseHeaders->set('Access-Control-Allow-Headers', ' content-type ,x-auth-token');
$responseHeaders->set('Access-Control-Allow-Methods', 'POST, GET');
if($event->getRequest()->getRealMethod()=='OPTIONS'){
$responseHeaders->set('Access-Control-Max-Age', '1728000');
$event->getResponse()->setStatusCode(200);
}
}
after handling the response I send 200 as status code so I won't have any CORS issue
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