8000 RememberMe logout handler not auto registered · Issue #6751 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

RememberMe logout handler not auto registered #6751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
michelsalib opened this issue Jan 15, 2013 · 17 comments
Closed

RememberMe logout handler not auto registered #6751

michelsalib opened this issue Jan 15, 2013 · 17 comments

Comments

@michelsalib
Copy link

I am currently implementing the remember me functionality. The REMEMBERME cookie is correctly set, but the logout fails.

What happens is that the LogoutListener is first called on the GetResponseEvent, but when comming here there is no token in the context, so the remember me logout is not called and does not delete the cookie. On the same listener routine, the remember me service is called later on and thus identifies the user.

Am I doing something wrong, or the listening sequence is wrong ?

EDIT I fixed the issue here, but it was because the remember me service is not registered as a logout handler automatically.

@lazyhammer
Copy link
Contributor

Works fine for me on 2.1.6 and 2.2-beta1. What's in your firewall config?

@michelsalib
Copy link
Author

Sure, here it is:

    providers:
        OAuthProvider:
            id: my.oauth.provider
        SymfonyProvider:
            id: my.symfony.provider
        ChainProvider:
            chain:
                providers: [OAuthProvider, SymfonyProvider]

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

        oauth_token:
            pattern:    ^/oauth/v2/token
            security:   false

        api:
            pattern:   ^/api
            fos_oauth: true
            stateless: true
            provider: SymfonyProvider
            security: true

        main:
            pattern: .*
            security: true
            anonymous: true
            switch_user: true
            form_login:
                check_path:  security_symfony_check
                login_path:  my_security_login
                use_referer: false
                provider:    SymfonyProvider
                default_target_path: '/'
            oauth:
                resource_owners:
                    facebook: security_facebook_check
                    google:   security_google_check
                    twitter:  security_twitter_check
                login_path:   my_security_login
                failure_path: my_security_login
                oauth_user_provider:
                    service:  my.oauth.provider
            logout:
                path:               security_logout
                target:             my_homepage_show
                invalidate_session: true
                delete_cookies:     ~
            remember_me:
                always_remember_me: true
                key:                6ql1rhhjhukg04gwgww4ow0sckok4gw
                user_providers:     [ChainProvider]

@michelsalib
Copy link
Author

Also I am working on the beta of sf2.2. Is there anything I could do to fix the problem ?

@michelsalib
Copy link
Author

Ok, I just fixed it, that was because I did not configure my rememberme handler in the logout section:

logout:
  path:               security_logout
  target:             my_frontend_homepage_show
  invalidate_session: true
  delete_cookies:     ~
  handlers:           [security.authentication.rememberme.services.simplehash.main]

As it is not intuitie, I think it should be done automatically. Can you point me on how to to fix so I can submit a PR ?

@lazyhammer
Copy link
Contributor

Theoretically, you should not set handler manually since RememberMeFactory does it for you, if "logout" and "remember_me" sections are present in your firewall config; and they are. Another question is why it doesn't make what it should.

Could you show the output of app/console container:debug --show-private | grep RememberMe with and without the "handlers" key set?

@michelsalib
Copy link
Author

Here with the handler key set:

532f4a9b7b326709b2af8744aee4b595_46                           container Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider
532f4a9b7b326709b2af8744aee4b595_69                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_71                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_77                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_83                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_85                           container Symfony\Component\Security\Http\Firewall\RememberMeListener
532f4a9b7b326709b2af8744aee4b595_86                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
security.rememberme.response_listener                         container Symfony\Component\Security\Http\RememberMe\ResponseListener

And without:

532f4a9b7b326709b2af8744aee4b595_46                           container Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider
532f4a9b7b326709b2af8744aee4b595_69                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_75                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_81                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
532f4a9b7b326709b2af8744aee4b595_83                           container Symfony\Component\Security\Http\Firewall\RememberMeListener
532f4a9b7b326709b2af8744aee4b595_84                           container Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices
security.rememberme.response_listener                         container Symfony\Component\Security\Http\RememberMe\ResponseListener

@lazyhammer
Copy link
Contributor

Ok, so there's one missing, when no handlers specified. Take a look at app/cache/dev/appDevDebugProjectContainerCompiler.log to see what's going on with your security.authentication.rememberme.services.simplehash.main service.

@michelsalib
Copy link
Author

Yes, the service is inlined several times then removed:

Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.rememberme.services.simplehash.main"; reason: unused

@michelsalib
Copy link
Author

I updated the issue to reflect this new development.

@lazyhammer
Copy link
Contributor

It's okay, as far as I know, since it was inlined. And what about security.logout_listener.main? Also, could you look at app/cache/dev/appDevDebugProjectContainer.php and search for method getSecurity_Firewall_Map_Context_MainService() there.

8000

@michelsalib
Copy link
Author

Without the manual configuration:


    /**
     * Gets the 'security.firewall.map.context.main' service.
     *
     * This service is shared.
     * This method always returns the same instance of the service.
     *
     * @return Symfony\Bundle\SecurityBundle\Security\FirewallContext A Symfony\Bundle\SecurityBundle\Security\FirewallContext instance.
     */
    protected function getSecurity_Firewall_Map_Context_MainService()
    {
        $a = $this->get('hwi_oauth.user.provider.entity.main');
        $b = $this->get('hwi_oauth.account.connector');
        $c = $this->get('security.context');
        $d = $this->get('monolog.logger.security');
        $e = $this->get('event_dispatcher');
        $f = $this->get('security.http_utils');
        $g = $this->get('http_kernel');
        $h = $this->get('security.authentication.manager');

        $i = new \Symfony\Component\Security\Core\User\ChainUserProvider(array(0 => $a, 1 => $b));

        $j = new \Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices(array(0 => $i), '6ql1rhhjhukg04gwgww4ow0sckok4gw', 'main', array('always_remember_me' => true, 'name' => 'REMEMBERME', 'lifetime' => 31536000, 'path' => '/', 'domain' => NULL, 'secure' => false, 'httponly' => true, 'remember_me_parameter' => '_remember_me'), $d);

        $k = new \Symfony\Component\Security\Http\Firewall\LogoutListener($c, $f, new \Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler($f, 'bestcomparator_frontend_homepage_show'), array('csrf_parameter' => '_csrf_token', 'intention' => 'logout', 'logout_path' => 'security_logout'));
        $k->addHandler(new \Symfony\Component\Security\Http\Logout\SessionLogoutHandler());
        $k->addHandler($j);

        $l = new \Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy('migrate');

        $m = new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler($f, array('default_target_path' => 'bestcomparator_frontend_homepage_show', 'login_path' => 'bestcomparator_frontend_security_login', 'use_referer' => false, 'always_use_default_target_path' => false, 'target_path_parameter' => '_target_path'));
        $m->setProviderKey('main');

        $n = new \Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener($c, $h, $l, $f, 'main', $m, new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler($g, $f, array('login_path' => 'bestcomparator_frontend_security_login', 'failure_path' => NULL, 'failure_forward' => false, 'failure_path_parameter' => '_failure_path'), $d), array('check_path' => 'security_symfony_check', 'use_forward' => false, 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', 'intention' => 'authenticate', 'post_only' => true), $d, $e);
        $n->setRememberMeServices($j);

        $o = new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler($f, array('login_path' => 'bestcomparator_frontend_security_login', 'always_use_default_target_path' => false, 'default_target_path' => '/', 'target_path_parameter' => '_target_path', 'use_referer' => false));
        $o->setProviderKey('main');

        $p = new \HWI\Bundle\OAuthBundle\Security\Http\Firewall\OAuthListener($c, $h, $l, $f, 'main', $o, new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler($g, $f, array('login_path' => 'bestcomparator_frontend_security_login', 'failure_path' => 'bestcomparator_frontend_security_login', 'failure_forward' => false, 'failure_path_parameter' => '_failure_path'), $d), array('check_path' => '/login_check', 'use_forward' => false), $d, $e);
        $p->setResourceOwnerMap($this->get('hwi_oauth.resource_ownermap.main'));
        $p->setCheckPaths(array(0 => 'security_facebook_check', 1 => 'security_google_check', 2 => 'security_twitter_check', 3 => 'security_windows_live_check'));
        $p->setRememberMeServices($j);

        return $this->services['security.firewall.map.context.main'] = new \Symfony\Bundle\SecurityBundle\Security\FirewallContext(array(0 => $this->get('security.channel_listener'), 1 => new \Symfony\Component\Security\Http\Firewall\ContextListener($c, array(0 => $a, 1 => $b, 2 => $i, 3 => $this->get('security.user.provider.concrete.apiprovider')), 'main', $d, $e), 2 => $k, 3 => $n, 4 => $p, 5 => new \Symfony\Component\Security\Http\Firewall\RememberMeListener($c, $j, $h, $d, $e), 6 => new \Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener($c, '50f903d90476d', $d), 7 => $this->get('security.access_listener'), 8 => new \Symfony\Component\Security\Http\Firewall\SwitchUserListener($c, $a, $this->get('hwi_oauth.user_checker'), 'main', $this->get('security.access.decision_manager'), $d, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $e)), new \Symfony\Component\Security\Http\Firewall\ExceptionListener($c, $this->get('security.authentication.trust_resolver'), $f, 'main', new \HWI\Bundle\OAuthBundle\Security\Http\EntryPoint\OAuthEntryPoint($f, 'bestcomparator_frontend_security_login'), 'bestcomparator_frontend_security_unauthorized', NULL, $d));
    }

And with the configuration:

/**
     * Gets the 'security.firewall.map.context.main' service.
     *
     * This service is shared.
     * This method always returns the same instance of the service.
     *
     * @return Symfony\Bundle\SecurityBundle\Security\FirewallContext A Symfony\Bundle\SecurityBundle\Security\FirewallContext instance.
     */
    protected function getSecurity_Firewall_Map_Context_MainService()
    {
        $a = $this->get('hwi_oauth.user.provider.entity.main');
        $b = $this->get('hwi_oauth.account.connector');
        $c = $this->get('security.context');
        $d = $this->get('monolog.logger.security');
        $e = $this->get('event_dispatcher');
        $f = $this->get('security.http_utils');
        $g = $this->get('http_kernel');
        $h = $this->get('security.authentication.manager');

        $i = new \Symfony\Component\Security\Core\User\ChainUserProvider(array(0 => $a, 1 => $b));

        $j = new \Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices(array(0 => $i), '6ql1rhhjhukg04gwgww4ow0sckok4gw', 'main', array('always_remember_me' => true, 'name' => 'REMEMBERME', 'lifetime' => 31536000, 'path' => '/', 'domain' => NULL, 'secure' => false, 'httponly' => true, 'remember_me_parameter' => '_remember_me'), $d);

        $k = new \Symfony\Component\Security\Http\Firewall\LogoutListener($c, $f, new \Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler($f, 'bestcomparator_frontend_homepage_show'), array('csrf_parameter' => '_csrf_token', 'intention' => 'logout', 'logout_path' => 'security_logout'));
        $k->addHandler(new \Symfony\Component\Security\Http\Logout\SessionLogoutHandler());
        $k->addHandler($j);
        $k->addHandler($j);

        $l = new \Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy('migrate');

        $m = new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler($f, array('default_target_path' => 'bestcomparator_frontend_homepage_show', 'login_path' => 'bestcomparator_frontend_security_login', 'use_referer' => false, 'always_use_default_target_path' => false, 'target_path_parameter' => '_target_path'));
        $m->setProviderKey('main');

        $n = new \Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener($c, $h, $l, $f, 'main', $m, new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler($g, $f, array('login_path' => 'bestcomparator_frontend_security_login', 'failure_path' => NULL, 'failure_forward' => false, 'failure_path_parameter' => '_failure_path'), $d), array('check_path' => 'security_symfony_check', 'use_forward' => false, 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', 'intention' => 'authenticate', 'post_only' => true), $d, $e);
        $n->setRememberMeServices($j);

        $o = new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler($f, array('login_path' => 'bestcomparator_frontend_security_login', 'always_use_default_target_path' => false, 'default_target_path' => '/', 'target_path_parameter' => '_target_path', 'use_referer' => false));
        $o->setProviderKey('main');

        $p = new \HWI\Bundle\OAuthBundle\Security\Http\Firewall\OAuthListener($c, $h, $l, $f, 'main', $o, new \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler($g, $f, array('login_path' => 'bestcomparator_frontend_security_login', 'failure_path' => 'bestcomparator_frontend_security_login', 'failure_forward' => false, 'failure_path_parameter' => '_failure_path'), $d), array('check_path' => '/login_check', 'use_forward' => false), $d, $e);
        $p->setResourceOwnerMap($this->get('hwi_oauth.resource_ownermap.main'));
        $p->setCheckPaths(array(0 => 'security_facebook_check', 1 => 'security_google_check', 2 => 'security_twitter_check', 3 => 'security_windows_live_check'));
        $p->setRememberMeServices($j);

        return $this->services['security.firewall.map.context.main'] = new \Symfony\Bundle\SecurityBundle\Security\FirewallContext(array(0 => $this->get('security.channel_listener'), 1 => new \Symfony\Component\Security\Http\Firewall\ContextListener($c, array(0 => $a, 1 => $b, 2 => $i, 3 => $this->get('security.user.provider.concrete.apiprovider')), 'main', $d, $e), 2 => $k, 3 => $n, 4 => $p, 5 => new \Symfony\Component\Security\Http\Firewall\RememberMeListener($c, $j, $h, $d, $e), 6 => new \Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener($c, '50f91a081e4b4', $d), 7 => $this->get('security.access_listener'), 8 => new \Symfony\Component\Security\Http\Firewall\SwitchUserListener($c, $a, $this->get('hwi_oauth.user_checker'), 'main', $this->get('security.access.decision_manager'), $d, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $e)), new \Symfony\Component\Security\Http\Firewall\ExceptionListener($c, $this->get('security.authentication.trust_resolver'), $f, 'main', new \HWI\Bundle\OAuthBundle\Security\Http\EntryPoint\OAuthEntryPoint($f, 'bestcomparator_frontend_security_login'), 'bestcomparator_frontend_security_unauthorized', NULL, $d));
    }

As you can see, there is something wrong when adding the handler configuration. The RememberMe service is registered twice and the the chain provider is overriden.

So why is the chain user provider breaking the logout handler logic ?

@lazyhammer
Copy link
Contributor

It's weird. You have identical contexts and I can't see a reason why duplicate logout handler should solve the issue.

@michelsalib
Copy link
Author

Me too. If someone more proficient un the security component could help us figure it out, it would be very nice.

@lazyhammer
Copy link
Contributor

Hmm.. Could you show debug log for logout request, again with and without manually set handler?

@michelsalib
Copy link
Author

I found the mistake. It comes from the fact that the logout listeners are registered too late. Because they handle GetResponseEvent after the login routine, the security context is not provided with a token and thus the logout cannot occurs.

@michelsalib
Copy link
Author

I am preparing a PR.

nicolas-grekas added a commit that referenced this issue May 15, 2018
This PR was squashed before being merged into the 2.7 branch (closes #24805).

Discussion
----------

[Security] Fix logout

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | no
| Fixed tickets | #6751, #7104
| License       | MIT

Commits
-------

9e88eb5 [Security] Fix logout
@chalasr
Copy link
Member
chalasr commented May 15, 2018

Thanks @MatTheCat for fixing this 5 years old bug 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
0