Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

More form_login Config

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Using form_login isn't as flexible as a custom authenticator class... though a lot of stuff can be configured.

For example, right now, it's not checking our CSRF token. Enable that by saying enable_csrf: true:

security:
... lines 2 - 16
firewalls:
... lines 18 - 20
main:
... lines 22 - 24
form_login:
... lines 26 - 29
enable_csrf: true
... lines 31 - 54

That's it! Over in the options, when you enable CSRF protection, it looks for a hidden field called _csrf_token with the string authenticate used to generate it. Fortunately, in our template, we're already using both of those things... so this is just going to work.

Seeing the Full List of Options

And there are even more ways we can configure this. Remember: to get this config, I ran debug:config security... which shows your current configuration, including defaults. But not all options are shown here. To see a full list, run config:dump security.

symfony console config:dump security

Instead of showing your actual config, this shows a huge list of example config. This is a much bigger list... here's form_login. A lot of this we saw before... but success_handler and failure_handler are both new. You can search the docs for these to learn how to control what happens after success or failure.

But also, later, we're going to learn about a more global way of hooking into the success or failure process by registering an event listener.

Rendering "last_username" On the Login Form

Anyways, we're not using our LoginFormAuthenticator anymore, so feel free to delete it.

And... I have good news! The core authenticator is doing one thing that our class never did! Up in authenticate()... this calls getCredentials() to read the POST data. Let me search for "session"... yup! This took me into getCredentials(). Anyways, after grabbing the submitted email - in this code that's stored as $credentials['username'] - it saves that value into the session.

It's doing that so that if authentication fails, we can read that and pre-fill the email box on the login form.

Let's do it! Go to our controller: src/Controller/SecurityController.php. This AuthenticationUtils has one other useful method. Pass a new variable to the template called last_username - you can call it last_email if you'd like - set to $authenticationUtils->getLastUsername():

... lines 1 - 9
class SecurityController extends AbstractController
{
... lines 12 - 14
public function login(AuthenticationUtils $authenticationUtils): Response
{
return $this->render('security/login.html.twig', [
... line 18
'last_username' => $authenticationUtils->getLastUsername(),
]);
}
... lines 22 - 29
}

Once again, this is just a helper to read a specific key off of the session.

Now, in the template - login.html.twig - up here on the email field, add value="{{ last_username }} ":

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="login-form bg-light mt-4 p-4">
<form method="post" class="row g-3">
... lines 10 - 15
<div class="col-12">
... line 17
<input type="email" name="email" id="inputEmail" class="form-control" value="{{ last_username }}" required autofocus>
</div>
... lines 20 - 33
</form>
</div>
</div>
</div>
{% endblock %}

Cool! If we go to /login... it's already there from filling out the form a minute ago! If we enter a different email... yes! That sticks too.

Next: let's get back to authorization by learning how to deny access in a controller... in a number of different ways.

Leave a comment!

4
Login or Register to join the conversation

how can we implement this if we kept using the old loginFormAuthonticator on the onAuthenticationFailure method?

Reply

Hey @Soufiyane!

Are you referring to how to print the "last username" thing? If so, the 2 steps are:

1) In your custom authenticator, in authenticate(), after reading the email, call:

$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $credentials['username']);

2) In your controller, read that value in the same way as see in this chapter :).

Let me know if that helps!

Cheers!

Reply
Lechu85 Avatar
Lechu85 Avatar Lechu85 | posted 1 year ago | edited

Hi. I stayed with the custom login in my project. Can you show me how to add $authenticationUtils-> getLastUsername(), in this case? In case without form_login: it didn't work :)

Reply

Hey Leszek,

You only need to inject the service Symfony\Component\Security\Http\Authentication\AuthenticationUtils into your Controller's method, or in case you need it in your authenticator, you can add another argument constructor and inject it there

Cheers!

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.3", // v3.3.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/annotations": "^1.0", // 1.13.2
        "doctrine/doctrine-bundle": "^2.1", // 2.6.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.1.1
        "doctrine/orm": "^2.7", // 2.10.1
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.1
        "pagerfanta/doctrine-orm-adapter": "^3.3", // v3.3.0
        "pagerfanta/twig": "^3.3", // v3.3.0
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "scheb/2fa-bundle": "^5.12", // v5.12.1
        "scheb/2fa-qr-code": "^5.12", // v5.12.1
        "scheb/2fa-totp": "^5.12", // v5.12.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.0
        "stof/doctrine-extensions-bundle": "^1.4", // v1.6.0
        "symfony/asset": "5.3.*", // v5.3.4
        "symfony/console": "5.3.*", // v5.3.7
        "symfony/dotenv": "5.3.*", // v5.3.8
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/form": "5.3.*", // v5.3.8
        "symfony/framework-bundle": "5.3.*", // v5.3.8
        "symfony/monolog-bundle": "^3.0", // v3.7.0
        "symfony/property-access": "5.3.*", // v5.3.8
        "symfony/property-info": "5.3.*", // v5.3.8
        "symfony/rate-limiter": "5.3.*", // v5.3.4
        "symfony/runtime": "5.3.*", // v5.3.4
        "symfony/security-bundle": "5.3.*", // v5.3.8
        "symfony/serializer": "5.3.*", // v5.3.8
        "symfony/stopwatch": "5.3.*", // v5.3.4
        "symfony/twig-bundle": "5.3.*", // v5.3.4
        "symfony/ux-chartjs": "^1.3", // v1.3.0
        "symfony/validator": "5.3.*", // v5.3.8
        "symfony/webpack-encore-bundle": "^1.7", // v1.12.0
        "symfony/yaml": "5.3.*", // v5.3.6
        "symfonycasts/verify-email-bundle": "^1.5", // v1.5.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.3
        "twig/string-extra": "^3.3", // v3.3.3
        "twig/twig": "^2.12|^3.0" // v3.3.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.3.*", // v5.3.4
        "symfony/maker-bundle": "^1.15", // v1.34.0
        "symfony/var-dumper": "5.3.*", // v5.3.8
        "symfony/web-profiler-bundle": "5.3.*", // v5.3.8
        "zenstruck/foundry": "^1.1" // v1.13.3
    }
}
userVoice