TRACK

Symfony 3 >

Security Setup

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

With FOSUserBundle setup, the only things we can't do is login and logout. FOSUserBundle does give us a /login page, but it's just a static HTML form: setting up the actual authentication part is entirely up to us. And that's why, if you try to login now, you get this angry message!

To prove how little FOSUserBundle is doing, go to /login. If you hover over your web debug toolbar, you can see that the controller behind this page is called SecurityController. Cool! In my editor, I'll find that file by filename - Shift+Shift in PHP Storm.

Sweet! See loginAction? This renders the login page. And all it does is check for any authentication errors stored in the session and render a template. It has no logic whatsoever for processing the form submit, logging in, or anything else related to security.

Configuring Logout

So let's finally add some security goodness, starting with logging out. Right now, if you go to /logout, you see an error message. This is coming from that same controller: FOSUserBundle gives us a /logout route, but its controller is never supposed to be called. To fix this, in security.yml, add logout: ~. That's it.

... lines 1 - 2
security:
... lines 4 - 12
firewalls:
... lines 14 - 18
main:
... lines 20 - 22
logout: ~
... lines 24 - 31

Try going to /logout again. It works! We are anonymous! By adding the logout key, Symfony is now waiting for us to go to /logout. When we do, it intercepts the request and logs us out. Other than giving us the /logout route, FOSUserBundle has nothing to do with this.

Configuring form_login Security

What about logging in? It's the same thing. Under your firewall, add form_login. That's actually all you need. But, I'll add a bit more: csrf_token_generator: security.csrf.token_manager. That will make sure the CSRF token - which is already added in the FOSUserBundle login template - is verified when we submit.

... lines 1 - 2
security:
... lines 4 - 12
firewalls:
... lines 14 - 18
main:
... lines 20 - 24
form_login:
csrf_token_generator: security.csrf.token_manager
... lines 27 - 31

As soon as we do that, go to /login and login with aquanaut1 password turtles. Winning! We are in! FOSUserBundle gives us a login form, but we need to take care of the rest... which is pretty easy.

Adding Remember Me Functionality

Oh, and on the login form, we also have a remember me checkbox. If you want this to work, you'll need to add one more setting: remember_me: with secret: '%secret%' to use the secret from parameters.yml.

... lines 1 - 2
security:
... lines 4 - 12
firewalls:
... lines 14 - 18
main:
... lines 20 - 24
remember_me:
secret: '%secret%'
... lines 27 - 34

Ok, so about 5 lines to get our entire security system working. That kicks butt! And now, we can hook up the login link for real. Open app/Resources/views/base.html.twig and find the static link. Add an if statement: if is_granted('ROLE_USER'), then else and endif. FOSUserBundle guarantees that every user always at least has ROLE_USER. So it's safe to use this to figure out whether or not the user is logged in.

<!DOCTYPE html>
<html>
... lines 3 - 13
<body>
... lines 15 - 19
<header class="header">
... lines 21 - 22
<ul class="navi">
... line 24
{% if is_granted('ROLE_USER') %}
... line 26
{% else %}
... line 28
{% endif %}
</ul>
</header>
... lines 32 - 50
</body>
</html>

For the logout link, use the route fos_user_security_logout, then we'll say "Logout". Oh, put all of this stuff inside an li tag. If you run:

... lines 1 - 19
<header class="header">
... lines 21 - 22
<ul class="navi">
... line 24
{% if is_granted('ROLE_USER') %}
<li><a href="{{ path('fos_user_security_logout') }}">Logout</a></li>
... lines 27 - 28
{% endif %}
</ul>
</header>
... lines 32 - 53

php bin/console debug:router

you can see that this is one of the routes we imported. Use a similar one for login: just copy the logout line, and change it to login.

... lines 1 - 19
<header class="header">
... lines 21 - 22
<ul class="navi">
... line 24
{% if is_granted('ROLE_USER') %}
... line 26
{% else %}
<li><a href="{{ path('fos_user_security_login') }}">Login</a></li>
{% endif %}
</ul>
</header>
... lines 32 - 53

Nice! Go back, and refresh. Hit Logout! Woohoo!

Next, let's see what this looks like in the database, and talk about roles.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.*", // v3.3.18
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.5.4
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.3.0
        "sensio/distribution-bundle": "^5.0", // v5.0.18
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.25
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.5.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "friendsofsymfony/user-bundle": "^2.0" // v2.0.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.4
        "symfony/phpunit-bridge": "^3.0", // v3.2.7
        "nelmio/alice": "^2.1", // v2.3.1
        "doctrine/doctrine-fixtures-bundle": "^2.3", // v2.4.1
        "symfony/web-server-bundle": "^3.3"
    }
}