Question

I am implementing a simple authenticated area in any Symfony2 project.

Nothing is hard about it, I do not use FOSUserBundle even if I could have, but this is not the problem anyway, my question is really pointing on the security scheme and firewalls.

The basic process :

  • Define a firewall that require an authenticated user profile with a sufficient role
  • Create a login form page that allow users to authenticate
  • Manage errors, and redirect authenticated users inside the firewall

I can do that quite easily with SF2.

But, I need something that is a bit different on this project :

The firewall does not require any authenticated profile.

It displays additional informations only if an authenticated profile is found, but should be accessible anonymously.

So I would a given page to be the login form page AND a classic firewall page

More informations here :

  • I have a home page, that displays public informations
  • The login form is a part of this home page, for graphic needs by the way (a dropdown)
  • If any user log in with the form, I do not want to redirect it into a defined firewall page, but allow the authenticated user to stay on this home page and show additional informations

In fact, the login form page === the firewall home page === the public home page ;

The problem is that on this page, the user is never considered as authenticated, even after log-in with the form, but can access all the privates ^/members pages.

# Security.yml
    firewalls:
        home:
            pattern: ^/$
            anonymous: ~

        members_area:
            pattern: ^/
            provider: <any_working_entity-based_provider>
            form_login:
                login_path: /
                check_path: /login_check
                post_only: true
                default_target_path: /
                use_referer: false
            logout:
                path: /logout
                target: /
                invalidate_session: false

    access_control:
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Public page, login page AND firewall page
        - { path: ^/members, roles: ROLE_USER } # Other firewall pages

# My/Super/Bundle/Resources/views/home.html.twig
    # [...]
    {% if app.user %}
        // This part is never displayed, even when the user is successfully authenticated
        <h3>Welcome home {{ app.user.username }}</h3>
    {% else %}
        // This part is always shown...
        <form action='{{ path('MySuperBundle_members_login_check') }}' method='post'>
            <div>
                <label for='_username'>Username</label>
                <input type='text' id='_username' name='_username' value='{{ last_username }}' />
            </div>
            <div>
                <label for='_password'>Password</label>
                <input type='text' id='_password' name='_password' value='{{ last_username }}' />
            </div>
            [... CSRF ...]
            <div>
                <input type='submit' name='submit' value='Submit' />
            </div>
        </form>
    {% endif %}

So finally:

  • The home page (/) is available anonymously: OK
  • The members area (^/members) requires any authenticated profile: OK
  • The home page always displays the login form, even if the user has already logged-in (the twig test {% if app.user %} always return false on the home page only)
Was it helpful?

Solution

I finally fixed it, I was just confusing firewalls and access control list.

As shown in the question, I had created 2 firewalls on the home page, and in symfony2, you cannot be logged in 2 different firewalls - in no way that I know -

In this case, if you want to have the login form inside any firewall page, you do not manage it with the firewall, but with the access control list.

# Security.yml
firewalls:
    members_area:
        pattern: ^/
        provider: <any_working_entity-based_provider>
        anonymous: ~ # To allow unauthenticated users to access the firewall --> The login form
        form_login:
            login_path: /
            check_path: /login_check
            post_only: true
            default_target_path: /
            use_referer: false
        logout:
            path: /logout
            target: /
            invalidate_session: false

access_control:
    - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Public page, login page AND firewall page
    - { path: ^/members/suscribe, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Suscribe is a public activity
    - { path: ^/members/retrieve_password, roles: IS_AUTHENTICATED_ANONYMOUSLY } # As well as forgotten password
    - { path: ^/members/reset_password, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Or reset password features with the token protected page...
    - { path: ^/members, roles: ROLE_USER } # BUT, you set up the access control to require any role of the authenticated users, for the different firewall pages
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top