0

As I'm sure all seasoned web devs know, CORS can be a pain! You've probably experienced this a few times:

Access to XMLHttpRequest at 'https://widget.com/widget' from origin 'https://www.website.com' 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.

This error was created when an XMLhttprequest has been created via a JS scripts. The JS that calls the request is as follows:

widget.js

// Create XML HTTP Request
var request = new XMLHttpRequest();

// Open POST request
request.open('POST', 'https://widget.com/widget');

// Set request header for how the request was sent
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

// Set request header for sending data
request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

// Set request header
request.setRequestHeader('Access-Control-Allow-Origin', '*');

// Send request to target
request.send();

// Listen for response
request.onreadystatechange = function() {

    // Finished processing
    if (request.readyState === 4) {
        
        // Check if the request was successful
        if (request.status === 200) {
            
            console.log(request.responseText);

        }
    }
};

There is nothing too special with the JS - I'm fairly confident this issue is not with the JS. The request is sent to request.open('POST', 'https://widget.com/widget');, this is where I know the CORS error issue occurs.

The target URL is a Laravel-run PHP server, with a very standard route structure. Here is the Laravel/PHP code for the route:

// Send widget DOM
Route::post('/widget', function(Request $request) {
    try {
        
        // Widget code

    } catch (Exception $e) {
        // Return error
        return json_encode(['message' => $e->getMessage()]);
    }
})->middleware('widgetCORS');

As you can see, I've created a route middleware to try to combat the CORS errors, please see the code for this middleware:

namespace App\Http\Middleware;

use Closure;

class WidgetCORS {

    public function handle($request, Closure $next) {
        // Continue with request
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Authorizations');
    }

}

To note, the error does not happen when the JS request is called by widget.com (as expected), but any other website it does not work.

I expect this to be my Laravel config that is causing the issue, I could really do with some help to resolve this! I've even tried BarryVDH's middleware, that didn't work either...

Please do let me know if there is any more info needed.

Thanks!

Ryan
  • 1,096
  • 2
  • 16
  • 31
  • Thanks @CBroe, I am aware what the preflight request is. Could you please provide some insight as to how I can resolve this? – Ryan Jul 22 '20 at 12:33
  • @CBroe - Can you please provide some example code to how this can be done? – Ryan Jul 22 '20 at 12:45
  • You aren't posting anything. Don't add a header saying you are posting JSON … and don't use the POST method. Don't add `X-Requested-With`, it's non-standard. Don't send `Access-Control-Allow-Origin` from the client, it's a **response** header. How stupid would it be if your own JavaScript could give itself permission to read data from any website? – Quentin Jul 22 '20 at 12:58
  • @Quentin - The request does send JSON data, I removed the content of the `request.send();` to simplify the question. Thanks for the suggestions regarding X-Requested-With and AC-Allow-Origin. – Ryan Jul 22 '20 at 13:05
  • Your Route::post('/widget' ...) uses widgetCORS, but the OPTIONS verb is being sent to the Laravel server. So you need to set Access-Control-Allow-Origin on the OPTIONS route to /widget. – javascripting.dev Jul 22 '20 at 13:09

1 Answers1

1

Because the error is with the preflight CORS request, I think you want to handle it by routing the OPTIONS verb. Something like:

Route::options('/widget', function(Request $request) {})->middleware('widgetCORS');
Adam Hopkinson
  • 28,281
  • 7
  • 65
  • 99