12

Description

When I'm doing a normal request to my symfony server running on http://localhost:8000/api/admin/login_check it returns the desired jwt token.

However, when I do it with the functional tests (with ./bin/phpunit) I get the following error:

Error: Unable to find the controller for path \"/api/admin/login_check\". The route is wrongly configured.

I also went through the functional test docs.

Bug Reproduced

Don't hesitate to clone or fork this project to test. There is README.md explaining installation steps.

I was also able to reproduce the bug by cloning a working example provided by one of the creators of the lexikjwtauthenticationbundle.

Logs

Test Logs (error)

Occurs when running ./bin/phpunit

[2019-01-31 09:37:49] request.INFO: Matched route "api_admin_login_check". {"route":"api_admin_login_check","route_parameters":{"_route":"api_admin_login_check"},"request_uri":"http://localhost/api/admin/login_check","method":"POST"} []
[2019-01-31 09:37:49] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2019-01-31 09:37:49] request.WARNING: Unable to look for the controller as the "_controller" parameter is missing. [] []

Dev Logs (success)

Occurs when doing a curl or postman request

[2019-01-29 21:16:26] request.INFO: Matched route "api_admin_login_check". {"route":"api_admin_login_check","route_parameters":{"_route":"api_admin_login_check"},"request_uri":"https://localhost:8000/api/admin/login_check","method":"POST"} []
[2019-01-29 21:16:27] doctrine.DEBUG: SELECT t0.id AS id_1, t0.email AS email_2, t0.password AS password_3 FROM admin t0 WHERE t0.email = ? LIMIT 1 ["email@test.com"] []
[2019-01-29 21:16:27] security.INFO: User has been authenticated successfully. {"username":null} []

Relevant Code:

Test method:

    public function testLogin(){

        $client = static::createClient();
        $client->request('POST', '/api/admin/login_check', [], [],
            [
                'Content-Type' => 'application/json',
                'Accept' => 'application/json'
            ],
            json_encode([
                'email' => 'email@test.com',
                'password' => 'qwerty123'
            ])
        );

        $this->assertEquals(200, $client->getResponse()->getStatusCode());

    }

Routes:

# Admin Routes
api_admin_login_check:
    path: /api/admin/login_check
    methods:  [POST]

Security:

security:

# more configs here

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login_admin:
            pattern: ^/api/admin/login
            stateless: true
            anonymous: true
            json_login:
                username_path: email
                provider: app_admin_provider
                check_path: /api/admin/login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        admin_api:
            pattern: ^/api/admin
            stateless: true
            provider: app_admin_provider
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator

    access_control:
        - { path: ^/api/admin/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/admin, roles: ROLE_ADMIN }
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Question

Why is there a 404 route not found for /api/admin/login_check route during Functional Testing but works fine with curl and postman?

Github #610

kemicofa ghost
  • 16,349
  • 8
  • 82
  • 131
  • Have you looked to log? – Imanali Mamadiev Jan 28 '19 at 17:44
  • `var/logs/dev.log` – Imanali Mamadiev Jan 28 '19 at 17:46
  • in `app/logs/dev.log` – Imanali Mamadiev Jan 28 '19 at 17:57
  • Are you sending registration data to the login check? This doesn’t make any sense. You should send the data to the registration url along with the authorization token. If you wish to obtain the token first, send the _username and the _password to the mentioned login check. – Mike Doe Jan 28 '19 at 19:02
  • @emix no I'm sending login data to the login_check. I already have a user created in the database. It works fine when I curl and use postman to retrieve the token. – kemicofa ghost Jan 28 '19 at 19:08
  • Email and plain_password doesn’t sound like a valid authentication payload for the login check: https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/index.md#usage – Mike Doe Jan 28 '19 at 19:11
  • @emix yes ! You're right. It was a typo. That's something I've already changed, but doesn't solve the problem. – kemicofa ghost Jan 28 '19 at 19:11
  • @ImanaliMamadiev I've added logged information. – kemicofa ghost Jan 31 '19 at 13:00
  • different is here `"request_uri":"http://localhost/api/admin/login_check","method":"POST"` `"request_uri":"https://localhost:8000/api/admin/login_check","method":"POST"` – Imanali Mamadiev Jan 31 '19 at 19:13
  • @imanaliMamadiev yes but that's normal. All test requests do the same thing. And the others tests I do work. – kemicofa ghost Jan 31 '19 at 19:33
  • have you checked this configuration including _controller https://stackoverflow.com/questions/24424759/symfony-2-unable-to-find-the-controller-for-path-login-check – calm Feb 08 '19 at 12:20
  • @calm I just checked. It does seem similar except that it's for production mode and uses symfony2 and no answers :/. Also, it gives no indication of what the problem could be. – kemicofa ghost Feb 08 '19 at 12:36

3 Answers3

6

You have wrong name for content-type header in your test request. It should have name CONTENT_TYPE.

$client->request(
   'POST',
   '/login_check',
   ['_username' => 'lexik', '_password' => 'dummy'],
   [],
   ['CONTENT_TYPE' => 'application/json']
);

Checking is here: vendor/symfony/security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php::handle

if (false === strpos($request->getRequestFormat(), 'json')
   && false === strpos($request->getContentType(), 'json')
) {
  return;
}

It helps in your "bug reproduced" code at least.

2

Make sure that /login_check is behind a firewall /login_check path has to match a firewall pattern. change this

access_control:

- { path: ^/your-route-for-login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }

to

access_control:

- { path: ^/your-route-for-login$, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }

i tried :P

Community
  • 1
  • 1
Vaibhav Chauhan
  • 350
  • 2
  • 8
  • Actually this does not solve the problem at all. It throws an access denied before it even attempts to access the controller. – kemicofa ghost Feb 05 '19 at 16:38
  • Did u add it to both firewall patterns and path ..secondly https://stackoverflow.com/questions/12409998/symfony2-1-unable-to-find-the-controller-for-path-login-check. check this out for your solution, this will do i think.. just trying to help :) – Vaibhav Chauhan Feb 05 '19 at 22:56
  • The problem is not that it's unable to match the correct route, it's that it's unable to find the "controller". My project works fine but doesn't when it's tested with phpunit. I would tend to assume it's an issue with the test environment and the lexikjwtauthentication listener. – kemicofa ghost Feb 05 '19 at 23:06
0

The following scenario worked for me after some sleepless nights.

I initially had issues connecting to Postman, clicking on the header and changing it to Application/JSON was not enough as I keep getting ORM/Doctrine errors. what worked for me was that I used deprecated functions in one of my PHP classes.

I simply went to my log file Var/log/dev.log and found this error:

php.CRITICAL: Uncaught Error: Cannot assign Doctrine\ORM\PersistentCollection to property Guess\Domain\Player\Player::$guesses of type Doctrine\Common\Collections\ArrayCollection

Further research shows that I used the ArrayCollecton object which has been deprecated to Collection, so change the ArrayCollection to just Collection including the getter and setter method and try again.

This worked for me now and am so happy.

Reference: Doctrine assignment error: Cannot assign Doctrine\ORM\PersistentCollection to property

This could go a long way to help someone in the near future.

Olasunkanmi
  • 902
  • 15
  • 23