Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OPTIONS request gets a "401 Unauthorized" response

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());
}
like image 337
Kamel Mili Avatar asked Mar 14 '26 05:03

Kamel Mili


2 Answers

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.

like image 106
Quentin Avatar answered Mar 15 '26 18:03

Quentin


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

like image 36
Kamel Mili Avatar answered Mar 15 '26 18:03

Kamel Mili



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!