CORS Error in Laravel 7 using Laravel Lighthouse - laravel

I have an API built with Laravel and Lighthouse-php(for GraphQL). My client is built with Vue js and uses Apollo for the graphQL client-side implementation. Anytime I make a request, I get the following error:
Access to fetch at 'http://localhost:8000/graphql' from origin 'http://localhost:8080' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Naturally, I proceeded to install laravel-cors package but I realized afterwards that it came by default with my Laravel installation (7.2.2). Which meant that \Fruitcake\Cors\HandleCors::class was already added to the middleware array in Kernel.php and the cors config file was already in my config directory.
After some googling, I realized that I needed to add \Fruitcake\Cors\HandleCors::class to the route.middleware array in my config/lighthouse.php file
It still did not work. I have restarted the server, cleared cache, cleared config and run composer dump-autoload but I still get the error. I have no idea how to get past this. Any help will be appreciated.
Versions
Laravel 7.2.2
Laravel Lighthouse 4.10

I got some help from the folks at lighthouse here. The problem was with my cors configuration. I needed to add graphql to the path array in config/cors but I mistakenly added graphql/*. So the path array looked like this
'paths' => ['api/*', 'graphql/*'],
instead of this
'paths' => ['api/*', 'graphql'],
After making the changes, I run the following:
php artisan cache:clear, php artisan config:clear and composer dump-autoload before the CORS error was gone.
The full configuration that worked for me was
return [
'paths' => ['api/*', 'graphql'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => false,
'max_age' => false,
'supports_credentials' => false,
];

Related

Laravel 6 with laravel passport, weird Guzzle error

I have followed the instructions for installing a password client for Laravel passport exactly as written in the Laravel docs and with default Laravel 6.0 composer versions of guzzle, etc. I have done the install on an existing project and as a clean install, both on local dev environment and live server, and every time I try to post to the example.com/oauth/token route, I am greeted with a crazy Guzzle error that seems to have no previous search history on the internet. The error is (summarized):
GuzzleHttp\Exception\ServerException
/var/task/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113
"Return value of Zend\\Diactoros\\normalizeServer() must be of the type array, none returned"
I am running php 7.3 in all environments, but tried php 7.2 and 7.1 and got the same result. I'm running Laravel Valet locally, and have never seen anything like this on any other project. I am also running a staging server with Laravel Vapor, and I get the exact same error. My guzzle request is almost exactly the same as Taylor Otwell's example in the Laravel docs, and looks like this:
$http = new \GuzzleHttp\Client;
$response = $http->post(env('API_TOKEN_URL'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => env('PASSPORT_CLIENT_ID'),
'client_secret' => env('PASSPORT_CLIENT_SECRET'),
'username' => $request['username'],
'password' => $request['password'],
],
]);
return json_decode((string) $response->getBody(), true);
I have data dumped all variables to verify that the username, password, client_id and client_secret are all accurate. It doesn't seem to be an authentication issue at all, but some issue with Guzzle passing proper server headers. I have no idea how to fix, as there is no previous record of this issue that I could find anywhere else on the internet. Any ideas???
if someone face this issue just update the package name: laminas/laminas-diactoros to latest version such as 2.2.2 by running
composer require laminas/laminas-diactoros
the problem comes from
normalize_server.legacy.php
its does not return anything.

CORS issue with laravel rest api POST

Failed to load https://example.com/api/api_details: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://example-international.firebaseapp.com' is therefore not allowed access.
That is my error when requesting a POST method. But its perfectly alright when it was in local and i put these line in header on laravel function:
header('Access-Control-Allow-Origin: *');
I also tried these for online:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
But no luck. All the post and get request are okay in local but in online only get request works. I am using angular 6 and laravel 5.3.
here is my network tab given bellow:
Use this package inside your Laravel application.
https://github.com/barryvdh/laravel-cors
It's very simple and will solve your problem.
Just don't forget to publish the config file using:
$ php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"
create a cors middleware and replace your handle method with
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Accept, Authorization, Content-Type');
}
Building web applications with Microservices Architecture comes with a couple of fixable issues. One of such issues is CORS. Most commonly, you face this issue when you try to test decoupled applications locally on your machine.
Say we had a RESTful API built with Laravel and a SPA built with VueJS, attempting to make a request from the Vue App running on port 8080 to the Laravel backend running on PORT 8000 might lead to an error like such:
[Check Error:][1]
[1]: https://i.stack.imgur.com/rGMmS.png
Thankfully, we can fix this easily in Laravel with the Laravel-cors package.
Installation
The Laravel-Cors package can be installed using composer. Navigate to your Laravel application folder in the terminal and run:
composer require fruitcake/laravel-cors
Configuration
After a successful installation, you should now have the Laravel-cors package added to your packages, you can check that you have it in your composer.json file.
"fruitcake/laravel-cors": "^1.0",
Next, we’ll need to add the HandleCors middleware in the $middleware property of app/Http/Kernel.php class. Open app/Http/Kernel.php and add this line in the $middleware property.
protected $middleware = [
...
\Fruitcake\Cors\HandleCors::class, # this line
];
Finally, we need to publish the package so the configuration file can be copied from the package directory to our application directory.
php artisan vendor:publish --tag="cors"
A new file (config/cors.php) should be added to your config folder. This file should contain default configurations for CORS. You can use the default configuration or tweak it however you wish. Let’s dig in a bit and see what options this file provides us.
<?php
return [
'paths' => [],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => false,
'max_age' => false,
'supports_credentials' => false,
];
paths: This option takes an array value and allows us to enable cors for multiple paths. Some example configurations would be:
'paths' => ['api/*', 'api/admin/*', 'api/users/*', '*']
allowed_methods: This option indicates what HTTP request methods to allow on the specified paths. [*]allows all methods. Some examples of option values would be:
'allowed_methods' => ['POST', 'GET', 'DELETE', 'PUT', '*']
allowed_origins: This option specifies what source requests should be allowed from. If you would want to test from your local machine, you would have to add “localhost” + the port to the list of allowed origins.
'`enter code here`allowed_origins' => ['http://localhost:8080', 'https://client.myapp.com']
allowed_origins_patterns: This option matches the request origin with patterns.
'allowed_origins_patterns' => ['Google\']
allowed_headers: This option is used to set the Access-Control-Allow-Headers, which is used in response to a preflight request which includes the Access-Control-Request-Headers to indicate which HTTP headers can be used during the actual request.
'allowed_headers' => ['X-Custom-Header', 'Upgrade-Insecure-Requests', '*']
exposed_headers: This option is used to set the value of Access-Control-Expose-Headers response header. This response header indicates which headers can be exposed as part of the response by listing their names.
max_age: This option is used to set the Access-Control-Max-Age response header. The Access-Control-Max-Age response header indicates how long the results of a preflight request ( the information contained in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can be cached.
supports_credentials: This option sets the Access-Control-Allow-Credentials header. The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to frontend JavaScript code when the request's credentials mode (Request.credentials) is included.
Now we have an understanding of the function of each of the options. You can configure these options however you want. Once you’re done, you want to reload your Laravel configurations and allow your changes to reflect.
php artisan config:cache
This should clear your configuration cache and recache the updated configurations (including your changes). From here you can serve your application.
php artisan serve
I hope this post helps you resolve any of your Laravel CORs issues.
Cheers 🍻

CORS request working fine on Chrome but not on Safari/Firefox

The Problem
When making a CORS request to login to my app on localhost, I am getting the following error on the latest version of Safari/Firefox running on Sierra.
Doing this exact thing on Chrome however works absolutely fine without any issues. I am using Laravel 5.6+ with CORS enabled properly.
Safari
Firefox
Firefox Network tab
/login POST OK, /user GET NOPE
Chrome /user GET (notice Access-Control-Allowed-Origin)
But doing the same on Safari/Firefox, on /user GET, that header is missing (?)
Laravel 5.6 CORS setup
'supportsCredentials' => false,
'allowedOrigins' => [env('ALLOWED_ORIGINS')],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['Origin', 'Content-Type', 'X-Requested-With', 'Accept', 'Authorization'],
'allowedMethods' => ['*'],
'exposedHeaders' => ['*'],
'maxAge' => 0,
The tools
Laravel Mix
Laravel 5.6+
axios
Vue.js 2
#websanova/vue-auth
I am also setting the below on axios as defaults. There is no reason that this very code that works on Chrome latest, doesn't work on FF/Safari latest. I have been banging my head against this for hours, to no avail.
window.axios = axios;
window.axios.defaults.headers.post['Content-Type'] = 'application/json';
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.baseURL = baseURL;
UPDATE
I noticed that Authorization Bearer gets set for /user GET on Chrome, but not on Firefox or Safari. This is absurd. What could be going wrong here?
Found the issue:
'exposedHeaders' => ['Authorization'], had to be explicit in my cors.php.
[] or ['*'] wasn't liked by Safari and Firefox. Chrome however accepted it. Weird
Now, this appears to be a violation of CORS by Chrome.
It doesn't care whether the Exposed Headers is set properly or not. It still works.

Laravel mailgun driver ignores verified domain and uses sandbox instead

I'm trying to use mailgun to send notifications to users. I have verified domain, but even though my config\services.php file looks like this:
'mailgun' => [
'domain' => 'mg.mydomain.biz',
'secret' => 'key-3223423423n423j42jklkj23l',
],
all the emails go through sandbox domain.
php artisan config:clear does not help. It is the same with development and production environment. Also I need to point out that emails come from #maydomain.biz when config\mail.php clearly states 'from' => ['address' => 'no-reply#mg.mydomain.biz', 'name' => 'Sender'],. I channged it hours ago and cleared config cache un restarted web server since.
sudo service supervisor restart solved the problem.

Persisting sessions across subdomains in Laravel 5

Using 5.0
in config/session.php I have set 'domain' => '.example.com' but it is not working. I cannot persist a session on even one domain like this.
My site has many subdomains:
vancouver.example.com
newyork.example.com
etc... they are hosted on the same server and are the same Laravel app (share the same storage directory)
I login with the correct credentials, upon which the app redirects to another page on the site, and I have no session at that point. var_dump(Auth::user()) shows null even though I logged in with the correct credentials.
storage/framework/sessions shows 14 different files there, they are all for me and I cleared them out before I started testing this.
I'll attach my AuthController#postLogin method below, which works fine if session.php 'domain' => null
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember'))) {
Session::flash('message', 'You are now logged in.');
Session::flash('status', 'success');
if (str_contains($_SERVER['HTTP_REFERER'], '?goto=')) {
$params = explode('?', $_SERVER['HTTP_REFERER'])[1];
$target = explode('=', $params)[1];
} else {
$target = '/';
}
return redirect($target);
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
Figured it out. Update domain => '.example.com' in session.php and clear the cookies for the site in question.
#gadss
you need to add session table like this
php artisan session:table
composer dump-autoload
php artisan migrate
and change .env to
SESSION_DRIVER=database
also modify config/session.php
'driver' => env('SESSION_DRIVER', 'database') and
'domain' => '.yourdomain.com'
after that clear your browser's cache and cookies.
You'll need to update the session configuration to persist the session in domain-wide including subdomains. Follow the steps given below.
Go to config/session.php and update the domain with prefix . as config => '.your-domain.com'.
Then clear your application cache, Open the Chrome DevTool and Go to Application > Application > Clear Storage. You'll need to clear out the previous cookies also.
run artisan command php artisan config:cache or php artisan config:clear to drop previously cached laravel application configs.
If you are using database as the session driver, You need to create a session table for that. run command php artisan session:table to generate the session table migration and then migrate it using php artisan migrate. Then perform the three steps given above.
With Laravel 8 it becomes more simplier :
Add SESSION_DOMAIN to your .env file :
SESSION_DOMAIN=.yourdomain.tld
Clear configuration cache :
php artisan config:cache
Delete your browser sessions cookies, then session become shared between all your subdomains.
In my case I used to AutoLogin user to subdomain once account is created on www. domain. Worked fine.
Have you tried storing the sessions in the database, memcached, or redis instead of in files? I had a similar situation to yours and storing sessions in the database solved the issue for me.
For some reason Laravel's session driver doesn't handle cross domain sessions correctly when using the file driver.
If someone still gets the problem with subdomain cookie. Try to change Session Cookie Name in config/session.php
If someone needs to sync session in subdomains with different laravel application sharing same database
Follow all the instructions of #Kiran Maniya
Then you have to keep same application name in order to get same session name. Or just change the cookie config in config/session.php
You can hardcode it if keeping same name is not possible.
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
)
to something like:
'cookie' => env(
'SESSION_COOKIE',
'session_sharing_application_session'
)

Resources