lightSAML / SpBundle

SAML2 SP Symfony Bundle based on LightSAML
https://www.lightsaml.com/SP-Bundle/
MIT License
66 stars 70 forks source link

Infinite redirection loop when logging in #35

Closed gabyquiles closed 7 years ago

gabyquiles commented 7 years ago

I'm trying to configure this bundle, but I'm having the problem that when I try to login it ends up in an infinite redirection loop. I have my own Id Provider and I registered this installation as an SP with AssertionConsumerService as /saml/login_check and the SingleLogoutService as /logout.

Or there is a way to set up this bundle as my own IdP so that other service can authenticate against it.

Thanks for your help

meDavid commented 7 years ago

We have the same issue and I have debugged it in my own case. What happens is that the authentication is actually successful but when the SamlSpToken is stored in the tokenstorage (which is produced when authenticating with a SamlSpRequestToken) any subsequent request authorizations in the AuthenticationProviderManagers cannot find a provider that supports a SamlSpToken. The LightSamlSpAuthenticationProvider only supports only SamlSpRequestTokens.

@gabyquiles did you manage to find a fix or workaround?

superhaggis commented 7 years ago

Seem to be having the same issue.

When I try to authenticate as a user that already exists within my Symfony project, the SAML auth and login works as intended. If I attempt to access a secured path as a first-time user of the project, the user is correctly created in the database but there's a redirect loop between the secured path and /saml/login_check.

I look at my env log and see the following:

[2017-03-01 22:52:17] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\Component\Security\Core\Exception\ProviderNotFoundException(code: 0): No Authentication Provider found for token of class \"LightSaml\SpBundle\Security\Authentication\Token\SamlSpToken\". at C:\MyProject\var\cache\dev\classes.php:4633)"} []

Does anyone have any pointers on how I can fix this?

meDavid commented 7 years ago

@superhaggis

I eventually applied the following patch. Let me know if it works for you.


diff -r abcd086bd6d8 -r fc4c44f5b626 vendor/lightsaml/sp-bundle/src/LightSaml/SpBundle/Security/Authentication/Provider/LightsSamlSpAuthenticationProvider.php
--- a/vendor/lightsaml/sp-bundle/src/LightSaml/SpBundle/Security/Authentication/Provider/LightsSamlSpAuthenticationProvider.php Tue Jan 03 13:05:39 2017 +0100
+++ b/vendor/lightsaml/sp-bundle/src/LightSaml/SpBundle/Security/Authentication/Provider/LightsSamlSpAuthenticationProvider.php Wed Jan 04 00:44:10 2017 +0100
@@ -123,9 +123,12 @@
             $this->userChecker->checkPostAuth($user);
         }

-        $attributes = $this->getAttributes($token);
+        if($token instanceof SamlSpResponseToken)
+           $attributes = $this->getAttributes($token);
+        else
+           $attributes = $token->getAttributes();

-        if ($this->tokenFactory) {
+        if ($this->tokenFactory && $token instanceof SamlSpResponseToken) {
             $result = $this->tokenFactory->create(
                 $this->providerKey,
                 $attributes,
@@ -153,7 +156,7 @@
      */
     public function supports(TokenInterface $token)
     {
-        return $token instanceof SamlSpResponseToken;
+        return $token instanceof SamlSpToken;
     }

     /**
@@ -163,15 +166,19 @@
      *
      * @throws UsernameNotFoundException
      */
-    private function loadUser(SamlSpResponseToken $token)
+    private function loadUser(SamlSpToken $token)
     {
-        if (null === $this->usernameMapper || null === $this->userProvider) {
-            throw new UsernameNotFoundException();
-        }
+       if($token instanceof SamlSpResponseToken) {
+           if (null === $this->usernameMapper || null === $this->userProvider) {
+               throw new UsernameNotFoundException();
+           }

-        $username = $this->usernameMapper->getUsername($token->getResponse());
+           $username = $this->usernameMapper->getUsername($token->getResponse());

-        $user = $this->userProvider->loadUserByUsername($username);
+           $user = $this->userProvider->loadUserByUsername($username);
+       } else {
+           $user = $token->getUser();
+       }

         if (false === $user instanceof UserInterface) {
             throw new \LogicException('User provider must return instance of UserInterface');
tmilos commented 7 years ago

SUMMARY

I'm unable to reproduce error ProviderNotFoundException(code: 0): No Authentication Provider found for token of class "LightSaml\SpBundle\Security\Authentication\Token\SamlSpToken", no matter what I try. Which Symfony version are you getting that on? How do you get it? Paste whole log so I can see what's before that. Please, provide more description on how I can reproduce it.

I have reproduced following loop: secured_page -> login_path -> IDP -> check_path -> login_path -> IDP -> check_path -> login_path ...

In summary, I found that when you have all of these:

you will get a that loop with IDP. If you change any single one of those you will not get a loop.

DETAILS

With user creator service set as explained in https://www.lightsaml.com/SP-Bundle/Getting-started/ when user in not found by user provider (UsernameNotFoundException thrown), and when user creator creates the user and saves it, it's working as expected - that new user is logged in.

If I try w/out user creator and with force option false (in app/config/security.yml commented line firewalls.main.light_saml_sp.user_creator) then LightsSamlSpAuthenticationProvider throws AuthenticationException which evetually is handled by Symfony's DefaultAuthenticationFailureHandler::onAuthenticationFailure which creates RedirectResponse with /saml/login path since it find no value for failure_path option, and defaults to login_path option.

When I try w/out user creator, but with force option true, LightsSamlSpAuthenticationProvider creates token w/out roles, and if trying to open secured route (one requiring ROLE_USER, or ROLE_ADMIN for example) I get normal 403 Access denied, w/out any redirection loop.

If I set failure_path option, once lightsmal auth proviuder throws auth exception, Symfony will redirect to that given path.

If you find any case I didn't cover, please provide full description on how I can reproduce it, or describe the debug trace.

Possible resolutions

I can change that and avoid loop in misconfiguration, either by

1) throwing some other exception type that Symfony will not handle (and make RedirectResponse to login_path), or

2) setting force option default value to true so token w/out roles will be created

Any preferences or some other idea?

tmilos commented 7 years ago

@meDavid your fix seems fine to make auth provider support both SamlSpResponseToken and SamlSpToken, but I don't understand how can it be called to authenticate or checked to support SamlSpToken. Can you describe the case when you get that? Once SamlSpToken is created it's always authenticated and symfony should not call auth providers anymore to authenticate it.

tmilos commented 7 years ago

Did few things which hopefully will prevent issues reproted here, like unsupported SamlSpToken and IDP redirect loop. Released in v1.1.0. Closing the issue. If anything similar noticed, feel free to submit an issue, but please give details on how to reproduce.