Cover of the book Symfony 5: The Fast Track

Symfony 5: The Fast Track is the best book to learn modern Symfony development, from zero to production. +300 pages in full color showing how to combine Symfony with Docker, APIs, queues & async tasks, Webpack, Single-Page Applications, etc.

Buy printed version

New in Symfony 5.1: Updated Security System

Contributed by
Wouter De Jong
in #33558, #36570 and #36574.

Symfony 5.1 will include a new Security system as one of its biggest new features. After several months of planning, discussions and hard work, we could finish it on time for Symfony 5.1 as an experimental feature.

The main differences with respect to the previous system are:

1) Removed everything but Guards

In the new Security system, there's only one listener that passes the request to an authenticator manager provided by Symfony. That manager takes care of session management, storing the token, "Remember me" functionality, etc.

Everything is related to a single concept and interface: authenticators. This simplifies the internals of the Security component and makes everything easier to understand for developers.

2) Moved to an event-based system

The Security component didn't use Symfony events to extend all its features. The new system changes that and it's based on three events:

  • CheckPassportEvent, this is the main event and checks the validity of the given credentials (a password, a certificate, a CSRF token, etc.)
  • LoginSuccessEvent, dispatched when credentials are valid.
  • LoginFailureEvent, dispatched when credentials are wrong.

That's all! Three simple events give you all the flexibility needed by your applications.

3) Next generation Guards

Security Guards were introduced in Symfony 2.8 via the GuardAuthenticatorInterface. The new Security system allows to simplify some Guard features and improves others. For starters, the checkCredentials() method is removed and the getCredentials() and getUser() methods have been merged into a method called authenticate().

This introduces a couple of new concepts:

  • Passport, the authenticate() method returns a "security passport" that contains the user object and any credentials needed to authenticate it;
  • Badges, the extra information needed by the passport.

Passports and badges are used by listeners of the CheckPassportEvent, which will validate and check the passport and all its badges. If all badges are resolved, the user is successfully authenticated.

Here's a simplified example of the new system in action when used in a login form:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;

class FormAuthenticator implements AuthenticatorInterface
{
    // ...

    public function authenticate(Request $request): PassportInterface
    {
        // find a user based on an "email" form field
        $user = $this->userRepository->findOneByEmail($request->get('email'));
        if (!$user) {
            throw new UsernameNotFoundException();
        }

        return new Passport($user, new PasswordCredentials($request->get('password')), [
            // and CSRF protection using a "csrf_token" field
            new CsrfTokenBadge('loginform', $request->get('csrf_token')),

            // and add support for upgrading the password hash
            new PasswordUpgradeBadge($request->get('password'), $this->userRepository)
        ]);
    }
}

The new Security system is disabled by default, but you can enable it as follows:

1
2
3
4
# config/packages/security.yaml
security:
    # ...
    enable_authenticator_manager: true

We'll revamp all Symfony Doc articles about security very soon to start using this new Security system. Meanwhile, you can read the following blog post: Meet the new Symfony Security: Authenticators published by Wouter De Jong, the main developer behind this new Security system. This article was completely based on Wouter's blog post.

Help the Symfony project!

As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.

Comments

This is so great, Security component was quite complex and it was feeling over-engineered for most applications. I'm quite happy to see a revamp coming, thank you Wouter!
> $user = $this->userRepository->findOneByEmail($request->get('email'));

What if we cannot read the user data without the authentication?

For example, the userRepository may have a method to authenticate the provided credentials ([email, password] → user or null), but it may not allow reads without authentication — the repository has additional layer of guards regarding both security and workflows.

Similarly, when authenticating against LDAP, we must call `bind(userDN, password)` before reading anything from the LDAP server because the LDAP server has its own set of access rules.
@Josef you may check the following Pull Request as an example, because it added LDAP support for the new Security system:

https://github.com/symfony/symfony/pull/36600
Does it mean that I won't be able to use `form_login` authentication in new security system? https://github.com/TomaszGasior/RadioLista-v3/blob/master/config/packages/security.yaml#L23
@Tomasz Gąsior no, all built-in Security authentication providers are rewritten to support both the new and "legacy" systems. This means form_login, json_login (+ their LDAP versions), guard and http_basic are supported.

So unless you're implementing AuthenticationProviderInterface (or use a bundle that does this), you can switch to the new system without a problem.

I would highly advise anyone to do so, and show their findings on Slack or in a GitHub issue. That'll help us make the new system stable and perfect once it's out of experimental phase.
Great!

I just added support for the new authentication system in the ecphp/cas-bundle !

See: https://github.com/ecphp/cas-bundle/pull/8
May be helpful for anybody: You need to ensure that `symfony/security-guard` is on 5.1-* and not only the security-bundle. The bundle doesn't do this for you. Otherwise it won't find the new GuardBridgeAuthenticator.
@Matthias Althaus thanks for sharing your experience, I've created a PR to add a descriptive exception if a wrong version of security-guard is installed: https://github.com/symfony/symfony/pull/36984
Sounds awesome! Maybe add bin/console make:auth --passport or something like that to automatically configure this? make:auth has really made integrating authentication much, much easier.
I'm eagerly awaiting for the docs to be updated as I'd like to use this on a new API system I'm working on. This looks very nice and should simplify an otherwise complex system that was intense in the past, I love the direction Guard is going.

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.