1
votes

Old problem but I have exactly the same error that @ChocoboGordo had (Cakephp 3.x ADmad/JwtAuth doesn't work). When I try to use JWT in a CakePHP Rest API (not using CRUD plugin) I get a MissingRouteException because it doesn't find login route (please see the response below). Any idea?

This is my code: App\Controller\Api\AppController

public function initialize()
{
    parent::initialize();
    $this->loadComponent('RequestHandler');

    $this->loadComponent('Auth', [
        'storage' => 'Memory',
        'loginAction' => [
            'prefix' => 'api',
            'controller' => 'Usuario',
            'action' => 'login',
            'plugin' => false
        ],
        'authenticate' => [
            'Form' => [
                'userModel' => 'Usuario',
                'scope' => ['Usuario.Activo' => 1],
                'fields' => [
                    'username' => 'DNI',
                    'password' => 'Clave'
                ],
            ],
            'ADmad/JwtAuth.Jwt' => [
                'parameter' => 'token',
                'userModel' => 'Usuario',
                'scope' => ['Usuario.Activo' => 1],
                'fields' => [
                    'username' => 'DNI',
                    'password' => 'Clave'
                ],
                'queryDatasource' => true
            ]
        ],
        'unauthorizedRedirect' => false,
        'checkAuthIn' => 'Controller.initialize'
    ]);
}

config\routes.php:

Router::prefix('api', function ($routes) {
$routes->extensions(['json']);
...
$routes->resources('Usuario', [
    'map' => [
        'login' => [
            'controller' => 'Usuario',
            'action' => 'login'
        ]
    ]
]);
...
});

Controller\Api\UsuarioController:

public function initialize()
{
    parent::initialize();
    $this->Auth->allow(['add', 'login']);
}
...
public function login() {
    if ($this->request->is(['post'])) {
        $this->response->header('Access-Control-Allow-Origin', '*');

        $postParams = $this->request->input('json_decode', true) ;
        if (!isset($postParams['DNI']) || !isset($postParams['Clave']))
            throw new BadRequestException();

        $_POST['DNI'] = $postParams['DNI'];
        $_POST['Clave'] = $postParams['Clave'];

        $user = $this->Auth->identify();
        if (!$user) {
            throw new UnauthorizedException('Auth error');
        }

        $this->set(array(
            'message' => 'Login Ok',
            'token' => JWT::encode([
                'sub' => $user['DNI'],
                'exp' =>  time() + 604800
            ],
            Security::salt()),
            '_serialize' => array('message', 'token')
        ));

        return;
    }

    throw new BadRequestException();
}

When I call to login it works and give me the token correctly. But when I try to call to another action (including the Autorization header with Bearer+token) this error is shown:

Response:

Error: [Cake\Routing\Exception\MissingRouteException] A route matching    'prefix' => 'api',
  'controller' => 'Usuario',
  'action' => 'login',
  'plugin' => NULL,
  '_ext' => NULL,
)" could not be found.
Exception Attributes: array (
  'url' => 'array (
  \'prefix\' => \'api\',
  \'controller\' => \'Usuario\',
  \'action\' => \'login\',
  \'plugin\' => NULL,
  \'_ext\' => NULL,
)',
  'context' => 
  array (
    '_base' => '/registro-api',
    '_port' => '80',
    '_scheme' => 'http',
    '_host' => 'localhost',
    'params' => 
    array (
      'pass' => 
      array (
      ),
      'controller' => 'Usuario',
      'action' => 'index',
      '_method' => 'GET',
      'prefix' => 'api',
      'plugin' => NULL,
      '_matchedRoute' => '/api/usuario',
      '_ext' => NULL,
    ),
  ),
)
Request URL: /api/usuario

I do not use CRUD plugin. Please any help?!

1
Whenever receiving errors, please always post the complete error, that is, including the full stacktrace (ideally copied from the logs where it is available in a properly readable fashion), even if the problem might be obvious to people who are familiar with CakePHP! Also please always mention your exact CakePHP version (last line in vendor/cakephp/cakephp/VERSION.txt) - thanks! - ndm

1 Answers

2
votes

As the error message says, there is no matching route for that URL array. Whenever encountering that error, look at what routes there are connected in your application (bin/cake routes), and check the error stacktrace to figure from where the problematic call stems. In this case it's probably safe to say that it stems from the auth component which utilizes the loginAction URL array.

Resource routes are HTTP method specific

Resource routes define the HTTP request method required for them to be matched. When generating URLs, you have to provide the method accordingly via the _method option key, which your loginAction configuration is missing, hence the error.

The default method for custom resource endpoints is GET, so you'd have to add

'_method' => 'GET'

to your loginAction configuration. However that method contradicts your login action code, which requires POST (you can use $this->request->allowMethod('post') btw instead of manually testing and throwing an exception). So you have to change your custom resource endpoint definiton to map to the POST method:

$routes->resources('Usuario', [
    'map' => [
        'login' => [
            'controller' => 'Usuario',
            'action' => 'login',
            'method' => 'POST' // <<<<
        ]
    ]
]);

and change your loginAction config accordingly:

'loginAction' => [
    'prefix' => 'api',
    'controller' => 'Usuario',
    'action' => 'login',
    'plugin' => false,
    '_method' => 'POST' // <<<<
],

ps.

fiddling with the $_POST superglobal doesn't seem to make too much sense. Generally it should be avoided to access superglobals in CakePHP, as it can easily bite you at some point, especially in the testing environment.

There should be no need to parse the input manually, the request handler component should handle that, given that you send a proper request, that is with the .json extension, or with a proper Accept header.

See also