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

How to Use Multiple Guard Authenticators

5.2 version

How to Use Multiple Guard Authenticators

The Guard authentication component allows you to use many different authenticators at a time.

An entry point is a service id (of one of your authenticators) whose start() method is called to start the authentication process.

Multiple Authenticators with Shared Entry Point

Sometimes you want to offer your users different authentication mechanisms like a form login and a Facebook login while both entry points redirect the user to the same login page. However, in your configuration you have to explicitly say which entry point you want to use.

This is how your security configuration can look in action:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    # config/packages/security.yaml
    security:
        # ...
        firewalls:
            default:
                anonymous: true
                lazy: true
                guard:
                    authenticators:
                        - App\Security\LoginFormAuthenticator
                        - App\Security\FacebookConnectAuthenticator
                    entry_point: App\Security\LoginFormAuthenticator
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- config/packages/security.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/security
            https://symfony.com/schema/dic/security/security-1.0.xsd">
    
        <config>
            <!-- ... -->
            <firewall name="default" anonymous="true" lazy="true">
                <guard entry-point="App\Security\LoginFormAuthenticator">
                    <authenticator>App\Security\LoginFormAuthenticator</authenticator>
                    <authenticator>App\Security\FacebookConnectAuthenticator</authenticator>
                </guard>
            </firewall>
        </config>
    </srv:container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // config/packages/security.php
    use App\Security\FacebookConnectAuthenticator;
    use App\Security\LoginFormAuthenticator;
    
    $container->loadFromExtension('security', [
        // ...
        'firewalls' => [
            'default' => [
                'anonymous' => true,
                'lazy' => true,
                'guard' => [
                    'entry_point' => LoginFormAuthenticator::class,
                    'authenticators' => [
                        LoginFormAuthenticator::class,
                        FacebookConnectAuthenticator::class,
                    ],
                ],
            ],
        ],
    ]);
    

There is one limitation with this approach - you have to use exactly one entry point.

Multiple Authenticators with Separate Entry Points

However, there are use cases where you have authenticators that protect different parts of your application. For example, you have a login form that protects the secured area of your application front-end and API end points that are protected with API tokens. As you can only configure one entry point per firewall, the solution is to split the configuration into two separate firewalls:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # config/packages/security.yaml
    security:
        # ...
        firewalls:
            api:
                pattern: ^/api/
                guard:
                    authenticators:
                        - App\Security\ApiTokenAuthenticator
            default:
                anonymous: true
                lazy: true
                guard:
                    authenticators:
                        - App\Security\LoginFormAuthenticator
        access_control:
            - { path: '^/login', roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: '^/api', roles: ROLE_API_USER }
            - { path: '^/', roles: ROLE_USER }
    
  • XML
     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
    <!-- config/packages/security.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/security
            https://symfony.com/schema/dic/security/security-1.0.xsd">
    
        <config>
            <!-- ... -->
            <firewall name="api" pattern="^/api/">
                <guard>
                    <authenticator>App\Security\ApiTokenAuthenticator</authenticator>
                </guard>
            </firewall>
            <firewall name="default" anonymous="true" lazy="true">
                <guard>
                    <authenticator>App\Security\LoginFormAuthenticator</authenticator>
                </guard>
            </firewall>
            <rule path="^/login" role="IS_AUTHENTICATED_ANONYMOUSLY"/>
            <rule path="^/api" role="ROLE_API_USER"/>
            <rule path="^/" role="ROLE_USER"/>
        </config>
    </srv:container>
    
  • PHP
     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
    30
    31
    // config/packages/security.php
    use App\Security\ApiTokenAuthenticator;
    use App\Security\LoginFormAuthenticator;
    
    $container->loadFromExtension('security', [
        // ...
        'firewalls' => [
            'api' => [
                'pattern' => '^/api',
                'guard' => [
                    'authenticators' => [
                        ApiTokenAuthenticator::class,
                    ],
                ],
            ],
            'default' => [
                'anonymous' => true,
                'lazy' => true,
                'guard' => [
                    'authenticators' => [
                        LoginFormAuthenticator::class,
                    ],
                ],
            ],
        ],
        'access_control' => [
            ['path' => '^/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/api', 'roles' => 'ROLE_API_USER'],
            ['path' => '^/', 'roles' => 'ROLE_USER'],
        ],
    ]);
    

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.