1

I've a backend app working with Laravel 7 and a frontend which works with VueJs. My Laravel app is running on laradock (nginx, postgres etc...) Using Postman the API (Laravel 7) works properly.

This API replies by dns or by ip. http://192.168.0.3:80/api/mobile or http://laraapi.com/api/mobile

Once I'm still developing the VueJs app I'm running it with "npm run serve" which provides two ways to access my app, first by localhost and the second one by IP address. Both of them running on port 8081.

When Axios consume the API which uses the GET verb, everything works fine. When Axios consumes a POST verb than a get error.

Access to XMLHttpRequest at 'http://larapi.com/api/mobile/startorder/' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

By default laravel 7 already have a pre-done configuration for CORS which is provided by "Fruitcake"

So my kernel.php is like that:

    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,
];

Fruitcake was moved in to be the first one, the tip from another StackOverflow which didn't help anyway.

My cors configuration:

'paths' => ['*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => false,

'max_age' => false,

'supports_credentials' => true,

See that supports_credentials and allowed_origins were changed. Anyway, changing allowed_origins to "*" does not work.

I've created another route file named "mobile" and I'm using this one instead of "api.php", the content is:

    Route::group(['middleware' => 'auth:api'], function(){
    Route::namespace('Mobile')->group(function () {
        Route::post('startorder',
            ['as' => 'startorder', 'uses' => 'PRC\PurchaseController@startOrder']);
    });
});

This new file was created because the idea is use api.php for another purpose.

I've tried already to set a proxy on VueJs side but unfortunately, the result was the same one.

Axios call

import { http } from "./config";
    startOrder: (order, user, token) => {
        var data = {
          "order": order,
          "user": user,      
        }
        return http.post("startorder/",data, {
          headers: {
            Authorization: "Bearer " + token,
            "Content-Type": "application/json",
          },
          withCredentials: true,
        });
      }

my config.js

import axios from "axios";

export const http = axios.create({
  baseURL: "http://192.168.0.3:80/api/mobile/",
  withCredentials: true
});

vue.config.js

module.exports = {
    devServer: {
        proxy: "http://192.168.0.3:80/api/mobile/",
        open: process.platform === 'darwin',
        host: '0.0.0.0',
        port: 8081, 
        https: false,
        hotOnly: false,
      },
    chainWebpack: config => {
        config
            .plugin('html')
            .tap(args => {
                args[0].title = 'LaraAPi'
                return args
            })
    }
}

For sure something is missing but actually I don't know which side is wrong anymore after a lot of tries.

I would appreciate it a lot if someone would help with that issue.

romulos
  • 268
  • 1
  • 2
  • 10
  • 1
    This might help. https://stackoverflow.com/questions/60168052/laravel-cors-with-fruitcake – Ankit Singh Sep 13 '20 at 16:28
  • Access to XMLHttpRequest at 'http://192.168.0.3/api/mobile/login' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response. – romulos Sep 13 '20 at 17:31
  • Setting on axios withCredentials to false now the login is also working... Just to preventing request coming from all ips I've changed the header('Access-Control-Allow-Origin: http://192.168.0.3:8081'); And now seems to be working fine. but anyway, I will keep looking for a solution using FuitCake, make no sense at all to provide something that does not work properly. But thank you @AnkitSingh – romulos Sep 13 '20 at 17:45
  • @romulos Any chance you could post your solution? I'm running into the same issue trying to hit openWeatherMaps API via axios with Laravel 8. – Keith Gulbro Oct 08 '20 at 06:09
  • Keith Gulbro I post the code below, let me know if you need something else ;) – romulos Oct 09 '20 at 18:05

4 Answers4

0

if you use axios withCredentials = true you need to enable laravel cros.php file supports_credentials = true

for axios code example:

axios.get('/user?ID=12345', { withCredentials: true })
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
  .then(function () {
    // always executed
  });

for cros.php code example

[
'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true
]
-1

I don't know about this '*' stuff. Remember, this is very bad practice in production!

Access to XMLHttpRequest at 'http://larapi.com/api/mobile/startorder/' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Try to put the failed origin inside cors.php:

'paths' => ['api/*'],

'allowed_origins' => ['http://192.168.0.3:8081', 'http://192.168.0.3:80', 'http://laraapi.com'],

All three origins above are allowed to make requests to this endpoint.

It's always recommended to create an environment variable to better control this type of configurations. If you make it work in development, it will automatically work in production too!

'paths' => ['api/*'],

'allowed_origins' => env('CORS_ALLOWED_ORIGINS'),

.env

CORS_ALLOWED_ORIGINS=http://192.168.0.3:8081,http://192.168.0.3:80,http://laraapi.com

Update your production .env file accordingly.

Jonathan Martins
  • 734
  • 7
  • 24
  • First of all thanks for replying. Yes, I know about the "*" the idea to keep it was to "expanding" the chance of working for now. About the 'allowed_origins' => ['http://192.168.0.3:8081', 'http://192.168.0.3:80', 'http://laraapi.com'], Still doesn't work. The config and cache were cleaned. The error is still there "Access to XMLHttpRequest at 'http://192.168.0.3/api/mobile/startpurchase/' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource." – romulos Sep 12 '20 at 21:02
  • Do you have ```cors``` like configurations inside nginx? – Jonathan Martins Sep 12 '20 at 21:14
  • No. Although I tried to set up a reverse proxy, example below: location /api/mobile { proxy_pass http://192.168.0.3:8081/; } For the frontend 'im using laravel backpack. So I've the minimum setup for the "/" – romulos Sep 12 '20 at 21:18
  • Try to change ```supports_credentials``` to ```false``` and ```exposed_headers``` to and empty array ```[]``` – Jonathan Martins Sep 12 '20 at 21:22
  • still failing Access to XMLHttpRequest at 'http://192.168.0.3/api/mobile/login' from origin 'http://192.168.0.3:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. – romulos Sep 12 '20 at 21:24
  • Can you inspect this request in Chome's console? The Response header section of the request should have ```Access-Control-Allow-Origin: http://192.168.0.3:8081``` for this to work. – Jonathan Martins Sep 12 '20 at 21:29
  • No, there is no entry for Access-Control-Allow-Origin: Connection: keep-alive Content-Encoding: gzip Content-Type: text/html; charset=UTF-8 Date: Sat, 12 Sep 2020 21:32:20 GMT Server: nginx Transfer-Encoding: chunked X-Powered-By: PHP/7.4.1 – romulos Sep 12 '20 at 21:33
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/221399/discussion-between-jonathan-martins-and-romulos). – Jonathan Martins Sep 12 '20 at 21:48
  • It's ok, thank you for your help. Replying the chat, no it was not solved, i'm still getting the same error. It's clear that is missing the Access-Control-Allow-Origin on the responde header. I tried to add this header on the bootstrap/app.php and also on Nginx... both cases I get error, but a different one complaining about "Response to preflight request doesn't pass access control check" – romulos Sep 13 '20 at 13:08
  • This second error is correct because you are bypassing the cors flow. ```A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers.``` It's an **OPTION** request. Probably this is related to the first problem. Did you try to test it with a new Laravel installation? I like to do this type of test when nothing else works. If your test with the new installation passes, you can use an app called ```Meld``` todo a diff between your current project and the new working project. – Jonathan Martins Sep 13 '20 at 21:54
  • Take a look at this: https://stackoverflow.com/a/63869169/3681102. The guy forgot to fill the other endpoints. Are your configurations like that? – Jonathan Martins Sep 14 '20 at 17:34
-1

cors.php I strongly suggest you change paths

<?php

return [

/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/

'paths' => ['*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => false,

'max_age' => false,

'supports_credentials' => false,

];

Kernel.php

    <?php

namespace App\Http;

use App\Http\Middleware\cors;
use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    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,
    ];
/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

}

mobile.php (similar to api.php)

    <?php
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: http://192.168.0.4:8081');
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::post('login', 'Mobile\Login\UserController@login');
Route::post('register', 'Mobile\Login\UserController@register');

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

VueJs Side

//const configureAPI = require('./src/server/configure')

module.exports = {
    devServer: {
        proxy: "http://192.168.0.4:80/api/mobile/",
        open: process.platform === 'darwin',
        host: '0.0.0.0',
        port: 8081, // CHANGE YOUR PORT HERE!
        https: false,
        hotOnly: false,
      }
}

config.js

import axios from "axios";

export const http = axios.create({
  baseURL: "http://192.168.0.4:80/api/mobile/",
  withCredentials: false
});

service.js (consumes the API)

   start: (parameter, token) => {
    var data = {
      parameter: parameter,
      user: user,
    };
    return http.post("start/", data, {
      headers: {
        Authorization: "Bearer " + token,
        "Content-Type": "application/json",
      },
      withCredentials: false,
    });
  },

@Keith Gulbro I hope this helps you to fix that nightmare. Let me know if you need something else.

romulos
  • 268
  • 1
  • 2
  • 10
-2

Folks, seems the issue has been solved at least for now. I will keep looking for a better solution.

Below, the details how this was solved.

1- remove the \Fruitcake\Cors\HandleCors::class from protected middleware on kernel.php

2 - On the header of api routes file you must set those lines below:

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: http://192.168.0.3:8081');

In my case, I removed the wildcard * and put my valid origin. The wildcard is insecure.

3 - I've changed my Axios post method to send withCredentials as false.

export default {
login: data => {
    return http.post("login",data, {
      headers: {
        "Content-Type": "application/json",
      },
      withCredentials: false,
    });
  },

4 - Config and cache were cleared.

php artisan config:clear
php artisan cache:clear

Now the response header is fulfilled correctly and the Access-Control-Allow-Origin' error disappeared. Anyway, might have a better solution using FruitCake, otherwise would make no sense at all to provide an inefficient package.

If someone has a better solution, please share it!

Thank's

romulos
  • 268
  • 1
  • 2
  • 10