No Cors headers added to response - reactjs

When trying to log in using an API (hosted locally) from a React application, I get this error every time:
I know there area lot of topics on this subject, but none of them helped me. Perhaps because I missed something, or don't understand the concept.
I have no idea anymore how to fix this.
Things I already tried:
- Added a HTTP middleware (code will follow): didn't work.
- Tried fixing it with the spatie/laravel-cors package: didn't work.
- Tried fixing it with the barryvdh/laravel-cors: didn't work either.
I am out of ideas. Does someone know what I am doing wrong?
My code
protected $middleware = [
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
\Spatie\Cors\Cors::class, // <-- this line would be pointed to my own middleware when that would be in use
];
The following code is pointed to instead of \Spatie\Cors\Cors::class, if I where to use my own middleware
class ApiCors
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH')
->header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
}
}

There are some tricky things with implementing CORS support:
Your CORS middleware should be added to global middleware stack, because browser could send Preflight request, and you wouldn't want to have specific OPTIONS route for every API route.
Middleware doesn't need to pass Preflight request deeper to application.
CORS header(s) should be added both to Preflight requests and API request.
So, that's how it should be done:
Create middleware:
php artisan make:middleware ApiCors
Put the code:
<?php
namespace App\Http\Middleware;
use Closure;
class ApiCors {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$isPreflight = $request->isMethod('options') && $request->hasHeader('origin');
// we don't need to process Preflight request further
$response = $isPreflight ? response()->make() : $next($request);
if($isPreflight) {
$response
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH')
->header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization')
->header('Access-Control-Max-Age', 86400);
}
$response->header('Access-Control-Allow-Origin', $isPreflight ? '*' : ($request->header('origin') ?? '*'));
return $response;
}
}
Register middleware:
app/Http/Kernel.php:
<?php
// ...
use App\Http\Middleware\ApiCors;
class Kernel extends HttpKernel {
// ...
protected $middleware = [
// ...
ApiCors::class,
];
// ...
protected $middlewarePriority = [
ApiCors::class, // move to the top of the chain
// ...
];
}
Test middleware:
Let's add simple API route.
routes/api.php:
Route::put('/v1/test', function () {
return response()->json(['answer' => 42]);
});
Let's start simple server (run in Laravel project root folder):
php -S localhost:8088 -t public
Next, open any webpage (or use current one) and run in developer console:
fetch('http://localhost:8088/api/v1/test', {
method: 'PUT', headers: { 'accept': 'application/json' }
}).then(r => r.json()).then(console.log.bind(console))
You should get response:
{answer: 42}
Don't forget that you should add your [external] API routes to routes/api.php, not to the routes/web.php, because web routers group has numerous middlewares which could interfere with your api ones, such as VerifyCsrfToken, for example.
MDN: Cross-Origin Resource Sharing (CORS)

Related

CORS errors because of internal app error

I am creating a simple API with CakePHP 4, and I am having some issues with some CORS requests.
Access to XMLHttpRequest at 'http://localhost/myapp.api/elaborations/add.json' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Everything works with every other request and after some digging I've found that the error was an undefined index in my controller. If I fix that, the CORS error disappears. I just didn't see it in my log files, it's my bad.
It's a bit confusing seeing a CORS error because of a coding error though. I guess the issue could be in my CORS configuration, and hence this question.
This is what I've ended with, after a little bit of web search, trials and errors. I know it's ugly but I couldn't find anything better that actually worked.
How can I avoid having CORS errors for coding issues? I guess there is some redirect action somewhere, but I can't figure out how to avoid it.
<?php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Server\MiddlewareInterface;
class CorsMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface
{
// Calling $handler->handle() delegates control to the *next* middleware
// In your application's queue.
$response = $handler->handle($request);
if ($request->getHeader('Origin')) {
$allowedDomains = [
'https://myapp.it',
'https://www.myapp.it',
'http://localhost:3000',
];
$origin = $_SERVER['HTTP_ORIGIN'];
if (in_array($origin, $allowedDomains)) {
header('Access-Control-Allow-Origin: ' . $origin);
}
header('Access-Control-Allow-Methods: POST, GET, PUT, PATCH, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: *');
if (strtoupper($request->getMethod()) === 'OPTIONS') {
exit(0);
}
}
return $response;
}
}
First of all, do not access superglobals in CakePHP directly, always use the abstracted APIs! Also you shouldn't echo data manually, that includes headers, again, use the abstracted APIs! Using superglobals and echoing data will only get you in trouble, it messes up the testing environment, it can lead to data not being sent (properly), etc.
That being said, for proper CORS coverage you also need to modify error handling, as the error controller will create a new response instance. You should be able to process the request/response in a custom exception renderer, at ExceptionRenderer::_getController(), like so:
// in src/Error/AppExceptionRenderer.php
namespace App\Error;
use App\Http\Middleware\CorsMiddleware;
use Cake\Controller\Controller;
use Cake\Error\ExceptionRenderer;
class AppExceptionRenderer extends ExceptionRenderer
{
protected function _getController(): Controller
{
$controller = parent::_getController();
$cors = new CorsMiddleware();
$response = $cors->setHeaders(
$controller->getRequest(),
$controller->getResponse()
);
return $controller->setResponse($response);
}
}
// in config/app.php
'Error' => [
'exceptionRenderer' => \App\Error\AppExceptionRenderer::class,
// ...
],
Your CORS middleware would supply the setHeaders() method accordingly, where you set the CORS headers if required, based on your example it could look something like this:
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface
{
$response = $handler->handle($request);
$response = $this->setHeaders($request, $response);
return $response;
}
public function setHeaders(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface
{
if ($request->getHeader('Origin')) {
$allowedDomains = [
'https://myapp.it',
'https://www.myapp.it',
'http://localhost:3000',
];
$origins = $request->getHeader('Origin');
$lastOrigin = end($origins);
if (in_array($lastOrigin, $allowedDomains, true)) {
$response = $response
->withHeader('Access-Control-Allow-Origin', $lastOrigin);
}
if (strtoupper($request->getMethod()) === 'OPTIONS') {
$response = $response
->withHeader(
'Access-Control-Allow-Methods',
'POST, GET, PUT, PATCH, DELETE, OPTIONS'
)
->withHeader('Access-Control-Allow-Headers', '*');
}
}
return $response;
}
See also
Cookbook > Error & Exception Handling > Custom ExceptionRenderer
Cookbook > Request & Response Objects
Cookbook > Middleware > Creating Middleware

How handle CORS issue in laravel with reactjs with ports

I am facing the CORS issue with api's from laravel to reactjs.
I am running laravel at 5555 port and reactjs is running on 3000.
I have a middleware to allow the origin of 'http://localhost:5555'. But it still gives me an error.
My middleware is below.
<?PHP
namespace App\Http\Middleware;
use Closure;
class HandleCors {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
$allowedOrigins = ['http://localhost:5555'];
$origin = $request->server('HTTP_ORIGIN');
if (in_array($origin, $allowedOrigins)) {
return $next($request)
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Origin', $origin)
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Content-Type, Cache-Control'); //in case if you want all (*)
}
return $next($request);
}
}
This is the custom code I have not installed any package for CORS.
How do solve this issue?
this is the error>>
Access to XMLHttpRequest at 'http://localhost:5555/api/MethodName' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:5555' that is not equal to the supplied origin.

Laravel CORS with Fruitcake

I make react project with laravel Back-end ... I have a CORS problem, I do everything like on link below, with fruitcake.
Laravel 6 CORS policy issue with API
but still not working.
cors.php:
'paths' => ['api/*'],
/*
* Matches the request method. `[*]` allows all methods.
*/
'allowed_methods' => ['*'],
/*
* Matches the request origin. `[*]` allows all origins.
*/
'allowed_origins' => ['*'],
/*
* Matches the request origin with, similar to `Request::is()`
*/
'allowed_origins_patterns' => [],
/*
* Sets the Access-Control-Allow-Headers response header. `[*]` allows all headers.
*/
'allowed_headers' => ['*'],
/*
* Sets the Access-Control-Expose-Headers response header.
*/
'exposed_headers' => false,
/*
* Sets the Access-Control-Max-Age response header.
*/
'max_age' => false,
/*
* Sets the Access-Control-Allow-Credentials header.
*/
'supports_credentials' => false,
And, kernel middle-ware is:
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\Fruitcake\Cors\HandleCors::class,
];
what else could be the problem?
Here are some gotchas when using fruitcake/laravel-cors:
Put HandleCors middleware at the top of $middleware in app/Http/Kernel.php:
protected $middleware = [
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
Putting it at the bottom or somewhere between won't work because requests might be rejected by other middlewares with higher priority.
Do NOT die or exit in controller.
For example the following won't work:
Route::get('/cors-test', function() {
dd("This won't work");
});
Because Fruitcake\Cors\HandleCors::handle method adds relevant headers AFTER handling request:
Fruitcake\Cors\HandleCors.php
public function handle($request, Closure $next)
{
// --- omitted
// Handle the request
$response = $next($request); // <--- if you die here
if ($request->getMethod() === 'OPTIONS') {
$this->cors->varyHeader($response, 'Access-Control-Request-Method');
}
// you will never reach here
return $this->addHeaders($request, $response);
}
dump does not work either
Clear config cache after changing app/config/cors.php:
$ php artisan config:cache
The Fruitcake\Cors\HandleCors::class is troublesome. Just remove it from everywhere and add these 3 headers in api.php route file on the top.
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, Authorization, Accept,charset,boundary,Content-Length');
header('Access-Control-Allow-Origin: *');
Add credentials: 'same-origin' to your request header in the react App
Actually ,just remove dd and die command from you code.
php artisan config:clear
php artisan route:clear
php artisan cache:clear
Make sure your permissions are setup correctly (eg. storage is writable)

ERR_EMPTY_RESPONSE error in authorization Laravel

I have a web service with Laravel that solved the CORS Origin problem, but the next problem is that requests that have authorization headers return the following error server.
OPTIONS https://sandbox.example.com / api / v1 / user / net profile :: ERR_EMPTY_RESPONSE
I'm currently using cloudflare and wanted to know if this is a CDN or something else on the server.
This is preflight requests.
You need allow OPTIONS request. In first you need create CORS middleware
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers'=> 'X-Requested-With, Content-Type, Accept, Origin, Authorization',
'Access-Control-Allow-Origin' => '*'
];
if($request->getMethod() === 'OPTIONS') {
// The client-side application can set only headers allowed in Access-Control-Allow-Headers
return \response('', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
$response->header($key, $value);
return $response;
}
}
then add in Http/Kernel.php in array $middleware:
protected $middleware = [
// other middlewares
Cors::class
];
After it all requests with type OPTIONS will return response 200 with headers.

CORS issue for Angular + Laravel project

I am having trouble to create web app using Angular which is accessing to RESTful API built by Laravel.
Although I have created middleware which passes correct headers, it does not work.
class Cors
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, X-Auth-Token, Origin, Authorization');
}
}
Any one can help me?
Well, this is a kind of annoying issue, I know, but there are 2 solutions.
1.
You define OPTIONS method for every API calling route, and make it pass the middleware you created like following:
Route::options('todos/{any?}', ['middleware' => 'cors', function(){return;}]);
Route::options('projects/{any?}', ['middleware' => 'cors', function(){return;}]);
2
You hack Laravel core class file, so that it passes CORS header for every OPTIONS request.
in the
/vendor/laravel/framework/src/framework/Illuminate/Routing/RouteCollection.php
you will find following function
protected function getRouteForMethods($request, array $methods)
{
if ($request->method() == 'OPTIONS') {
return (new Route('OPTIONS', $request->path(), function () use ($methods) {
return new Response('', 200, ['Allow' => implode(',', $methods)]);
}))->bind($request);
}
$this->methodNotAllowed($methods);
}
Update this function to following, so that it will pass CORS headers for OPTIONS request
protected function getRouteForMethods($request, array $methods)
{
if ($request->method() == 'OPTIONS') {
return (new Route('OPTIONS', $request->path(), function () use ($methods) {
return new Response('', 200, [
'Allow' => implode(',', $methods),
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, X-Auth-Token, Origin, Authorization',
]);
}))->bind($request);
}
$this->methodNotAllowed($methods);
}
So for me, both solutions work okay. Choice is yours.
But solution #2 is something hack on Laravel core, you might have some issues if you upgrade Laravel itself? But at least it has less coding. :D
Hope these solutions will be helpful.

Resources