0

I'm developing a CakePHP with a GraphQL endpoint built on webonyx's GraphQL-PHP package. The problem is post and multipart methods return 400 bad request. I initial thought it was due preflight request get handled and the follow-up ignored or a simple cors problem, but after extensive debugging and modification to the code. I've determined the those are setup properly. I use this plugin to manage the preflight request. The default configuration allows all origins and the issues persist.

The action method that handles the request look like so.

    public function index(){
        $this->autoRender = false;

        $data = $this->request->input('json_decode');

        if(!$data) {
            $data = $this->request->getParam('?');
        }

        if(!$data) $data = [];
        $data += ['query' => null, 'variables' => null];
        $result = $this->GraphQL->query($data);

        $this->response = $this->response
        ->withHeader('Access-Control-Allow-Origin', '*')
        ->withHeader('Access-Control-Allow-Methods', ['GET', 'POST', 'OPTIONS'])
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Access-Control-Max-Age','8600')
        ->withType('application/json')
        ->withStringBody(json_encode($result));
        return $this->response;
    }

But thats not the cause of the problem. The POST and MULTIPART don't even make into this function before they return the error. I have been test the endpoint with GraphiQL Feen.

This is the resulting stack trace from the error log.

2018-01-29 22:28:35 Warning: DebugKit not enabled. You need to either install pdo_sqlite, or define the "debug_kit" connection name.
2018-01-29 22:28:36 Warning: DebugKit not enabled. You need to either install pdo_sqlite, or define the "debug_kit" connection name.
2018-01-29 22:28:47 Warning: DebugKit not enabled. You need to either install pdo_sqlite, or define the "debug_kit" connection name.
2018-01-29 22:28:48 Error: [Cake\Controller\Exception\AuthSecurityException] '_Token' was not found in request data.
Request URL: /api
Stack Trace:
#0 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php(316): Cake\Controller\Component\SecurityComponent->_validToken(Object(App\Controller\ApiController))
#1 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php(121): Cake\Controller\Component\SecurityComponent->_validatePost(Object(App\Controller\ApiController))
#2 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Event/EventManager.php(416): Cake\Controller\Component\SecurityComponent->startup(Object(Cake\Event\Event))
#3 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Event/EventManager.php(393): Cake\Event\EventManager->_callListener(Array, Object(Cake\Event\Event))
#4 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php(110): Cake\Event\EventManager->dispatch(Object(Cake\Event\Event))
#5 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Controller/Controller.php(506): Cake\Controller\Controller->dispatchEvent('Controller.star...')
#6 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/ActionDispatcher.php(114): Cake\Controller\Controller->startupProcess()
#7 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/ActionDispatcher.php(93): Cake\Http\ActionDispatcher->_invoke(Object(App\Controller\ApiController))
#8 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/BaseApplication.php(108): Cake\Http\ActionDispatcher->dispatch(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#9 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(65): Cake\Http\BaseApplication->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#10 /path/radioactive-cake/app/vendor/ozee31/cakephp-cors/src/Routing/Middleware/CorsMiddleware.php(31): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#11 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(65): Cors\Routing\Middleware\CorsMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#12 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php(104): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#13 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(65): Cake\Routing\Middleware\RoutingMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#14 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php(88): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#15 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(65): Cake\Routing\Middleware\AssetMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#16 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php(95): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#17 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(65): Cake\Error\Middleware\ErrorHandlerMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#18 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Runner.php(51): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#19 /path/radioactive-cake/app/vendor/cakephp/cakephp/src/Http/Server.php(81): Cake\Http\Runner->run(Object(Cake\Http\MiddlewareQueue), Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#20 /path/radioactive-cake/app/webroot/index.php(40): Cake\Http\Server->run()
#21 {main} 

Also it states the _Token is missing from the request data but I disable CSRF in my beforeFilter function like this.

public function beforeFilter(Event $event)
{
    parent::beforeFilter($event);
    $this->Auth->allow('index');
    $this->eventManager()->off($this->Csrf);
}

On a side note would using the debug_kit connection mentioned in the first three lines provided a more detailed output?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Geoff Taylor
  • 496
  • 3
  • 17
  • 1
    If the error is triggered on the CakePHP side, then there should be an according error message and stacktrace in the logs (see the `logs` folder), please always include those in your questions. If there is no log entry, then the error might occour on server level, or at least outside of the CakePHP request cycle. – ndm Jan 29 '18 at 12:57
  • Thanks I completely neglected the app log files, I have checked my server logs and no errors are thrown during the request. – Geoff Taylor Jan 29 '18 at 22:37
  • I actually modified it to the way is it done in the answer to this question https://stackoverflow.com/questions/37360734/token-was-not-found-in-request-data-in-cakephp3-after-server-migration and I don't get the error anymore. I would have never solved that without your help. Thanks a lot, you've been my hero two days in a row. – Geoff Taylor Jan 29 '18 at 22:57
  • You're welcome. [**DebugKit**](https://book.cakephp.org/3.0/en/debug-kit.html) most likely wouldn't have given you any more details, the message is just the default message that the plugin logs at bootstrapping time in case it is enabled, but will not be able to store generated debug data. If it can store data, then you'll be able to check the debug history for previous requests via the plugins GUI (ie on requests that respond with HTML documents). – ndm Jan 29 '18 at 23:07
  • I see. Well, my graphql component works perfectly now, but I haven't implemented mutations yet and the ability to view request contents would save me a lot of trouble. Right now, I just use a custom component to write `json_encode($this->request)` to a log file when I debug. – Geoff Taylor Jan 30 '18 at 01:13

1 Answers1

0

The CSRF is improperly disabled. My beforeFilter should look like this.

public function beforeFilter(Event $event)
{
    parent::beforeFilter($event);
    $this->Auth->allow('index');
    $actions = [
        'index',
    ];

    if (in_array($this->request->params['action'], $actions)) {
        // for csrf
        $this->eventManager()->off($this->Csrf);

        // this is a must have
        $this->Security->config('unlockedActions', $actions);
    }
}
Geoff Taylor
  • 496
  • 3
  • 17