Search code examples
phpsymfonysilexsymfony-security

How make a just registered SimpleUser log in secured area in Silex/Symfony with?


I'm loooking for help in using the SimpleUser for Silex. My composer.json is available at the end of needed.

In SimpleUser, we can have access to a page to allow users to register themselves (and possibly confirm their email). When not confirming emails, these users are automatically logged in after completing the register form (through the RegisterAction line 89, which call the loginAsUser on line 114).

I was assuming that after being logged, the user would have access to my secure area, even if not yet granted right to most pages/functions (which sill be done later manually by an admin).

What is actually done is that after registering, when I call any url, I got a 302 redirection to the root / page (which is done by my controllers if the right to use a page is not granted), then I got another 302 redirection (that is done "automatically") to my login url. I see in the logs that an exception is thrown :

An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at C:\\path\\to\\wamp\\www\\Easytrip2\\vendor\\symfony\\security\\Http\\Firewall\\AccessListener.php:53)"} []

The point here is that the login page knows the user, as it display the name, email, etc. So the user is definitely logged in.

To summarize, my problem is the fourth step :

  1. Access a page as anonymous,
  2. Getting reidrected to login page,
  3. Register as a user,
  4. Try to access (manually or with a link) to any page redirects to root (which is expected) and root redirect to login form (not expected),
  5. See that you are logged on the login page.

My thinking, after a few hours of mining code, found two interesting points :

  • The exception thrown is legitimate : there is actually no token_storage. Following the constructors, I found that it is supposed to be populated while registering my SecurityServiceProvider, line 314. From here I guess that I have a wrong setup in my firewall...
  • I tried to debug and watch what is inside of the $app['security.token_storage'], and it is null, whenever I access pages with a fully working and registered user or with a newly registered user.
  • I looked at the loginAsUser function, and I find something odd. The token is changed from the current value to a new token used to authenticate the user. The values authenticated,user, and key are changed from true, "anon.", and "public" to false, instance of SimpleUser\\User, and "public" (the property changed from key to providerKey). Could it come from here ?

EDIT 1 : After some time with a Symfony friend, I found that when registering a new user (thus in the public firewall), the loginAsUser (see last bullet point in the list above) does the token switch taking into account this firewall. When changing the public firewall name, it does change the $currentToken->key and $token->providerKey. I think it is linked to a misconception : we cannot login into a firewall on a page belonging to another firewall. Is that right ?

So, do you have any idea of what I'm doing wrong ?

Here are my codes :

composer.json :

{
    "require": {
        "silex/silex": "~1.3",
        "doctrine/dbal": "2.5.*",
        "symfony/security": "2.7.*",
        "twig/twig": "1.21.*",
        "symfony/twig-bridge": "2.7.*",
        "symfony/form": "2.7.*",
        "symfony/translation": "2.7.*",
        "symfony/config": "2.7.*",
        "jasongrimes/silex-simpleuser": "*",
        "twig/extensions": "1.3.*",
        "symfony/validator": "2.*",
        "phpoffice/phpexcel": "1.*",
        "symfony/monolog-bridge": "*",
        "box/spout": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "*",
        "symfony/browser-kit": "*",
        "symfony/css-selector": "*",
        "silex/web-profiler": "*"
    },
    "autoload":{
        "psr-4":{"Easytrip2\\": "src"}
    },
    "autoload-dev":{
        "psr-4":{"Easytrip2\\": "tests"}
    }
}

Classic controller code :

public function fileSearchAction(Request $request, Application $app) {
    if ($app ['security.authorization_checker']->isGranted ( 'IS_AUTHENTICATED_FULLY' ) and $app ['security.authorization_checker']->isGranted ( 'ROLE_FOLLOWUP' )) {
        //Do something and return
    } else {
        $app ['session']->getFlashBag ()->add ( 'error', 'Don\'t have the rights...' );
        return $app->redirect ( $app ['url_generator']->generate ( 'home' ) );
    }
}

My firewall :

$app->register ( new Silex\Provider\SecurityServiceProvider (), array (
        'security.firewalls' => array (
                'public' => array (
                        'pattern' => '^/user/(login|logout|register)$',
                        'form' => array (
                                'login_path' => '/user/login',
                                'check_path' => '/user/login_check'
                        ),
                        'logout' => array (
                                'logout_path' => '/user/logout',
                                'invalidate_session' => true
                        ),
                        'anonymous' => true,
                        'users' => $app->share ( function () use ($app) {
                            return $app ['user.manager'];
                        } )
                ),
                'secured' => array (
                        'pattern' => '^(/|(/user/.*)|(/view/.*)|(/search/.*)|(/admin/.*))$',
                        'form' => array (
                                'login_path' => '/user/login',
                                'check_path' => '/user/login_check'
                        ),
                        'logout' => array (
                                'logout_path' => '/user/logout',
                                'invalidate_session' => true
                        ),
                        'anonymous' => false,
                        'users' => $app->share ( function () use ($app) {
                            return $app ['user.manager'];
                        } )
                )
        )
) );

And my home route :

$app->get ( '/', "Easytrip2\Controller\HomeController::indexAction" )->bind ( 'home' );

Please let me know if you need any more information.

I know I should have manage the right into a $app['security.access_rules'], and it is one of the next improvements. It shouldn't cause this problem though.

Thanks for your help !


Solution

  • I commented the line 114 of UserController.php from SimpleUser. IMO, we should not try to log the user in a public page : symfony/silex attribute a token linked to the public firewall, thus refuse any access to secured pages.

    Everything works great now, thanks for your help !