2
votes

I'm using the HWI OAuth Bundle to allow users to login with Google Apps. It allows the user to login as expected.

However, after about 5 minutes the cookie expires and it tries to redirect to /login, but it gets stuck in an infinite redirect loop. It's trying to load /login on port 443, but returning a 302 redirect to the same URL every time. If I clear the Symfony cache on the server, or clear cookies in the browser, it shows the login page and works again.

// security.yml:

firewalls:
    secured_area:
        anonymous:                          ~
        oauth:
            resource_owners:
                google:                     "/login/check-google"
            oauth_user_provider:
                service:                    my.security.userprovider
            login_path:                     /login/
            failure_path:                   /login/
        form_login:
            login_path:                     /login/
        logout:
            path:                           /logout
            target:                         /login/

access_control:
    - { path: ^/(_(profiler|wdt)|css|images|js)/, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/connect, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, role: ROLE_STAFF, host: %cms% }

The urls are structured so that:

  • Everything on admin.example.com is secured
  • Everything on any other subdomain is public. Subdomains are generated dynamically.

There is nothing in the nginx, Symfony2 or FPM logs. I've put the same code on a different server in production environment, and the same thing happens. I can't work out whether it's the security bundle, the HWI OAuth bundle, or something in between.

So, question is which method is producing the redirect, and how do I stop it?

1
In { path: ^/, role: ROLE_STAFF, host: %cms% }, what do you mean by %cms%? - cheesemacfly
@cheesemacfly It's a parameter defined in parameters.yml, referring to the admin.example.com URL. - Dan Blows
So when you get stuck in the infinite loop, does the host match this parameter or not? - cheesemacfly
@cheesemacfly Yes, it matches the hostname. So the infinite redirect loop happens when I'm logged into admin.example.com (having logged in at admin.example.com/login). After about 5 minutes, it gets stuck trying to load admin.example.com/login. - Dan Blows
So I may be missing something but when you have this { path: ^/, role: ROLE_STAFF, host: %cms% } it requires you to be in the role ROLE_STAFF to access ^/ which is false when you are not logged in. So it has no way to reach /login/ when you have been logged out because it is under ^/. - cheesemacfly

1 Answers

4
votes

Worked it out. The first time a user logins in UserProvider::loadUserByOAuthUserResponse() receives a `UserResponse' that contains the email address of the user. I'm using this to load the user from my database.

However, every 5 minutes it checks the token against the OAuth provider again, by checking the user's access token (stored in the user's session). This time the UserResponse contains only the access token meaning the email address is null, so it can't find the user. This throws a UsernameNotFoundException exception, but HWIOAuthBundle doesn't catch it.

So, HWIOAuthBundle returns a null user object to the Authentication bundle, which then recognises the user needs to authenticate so redirects to login. Since the user already has an access token, it checks that against the OAuth provider, but again does not return email address, so the user can't be found, and it redirects to login. Hence the loop.

The fix - store the user's access token in the database, and try to load the user by access token.

This gist was particularly useful in seeing how to build a working UserProvider.