Search code examples
symfonysymfony6

How do I use Symfony custom authenticator to protect routes from unauthenticated users?


I am using Symfony 6 and PHP League OAuth package.

I have built a custom authenticator to log in a user - I am able to authenticate a user just fine.

However, I am having trouble setting protection for the other routes of my application, and can still access them when not authenticated.

Custom Authenticator:

public function authenticate(Request $request): Passport
{
    $session = $request->getSession();

    $provider = new Google([
        'clientId'     => 'myclientid',
        'clientSecret' => 'myclientsecret',
        'redirectUri'  => 'mycallback',
    ]);

    if (!empty($request->query->get('error'))) {
        // I do a bunch of error checks in here

    } else {

        // We got an access token, let's now get the owner details
        $googleUser = $provider->getResourceOwner($token);

        $user = $this->userRepository->updateOrCreate([
            'email' => $googleUser->getEmail(),
            'name' => $googleUser->getName(),
            'google_id' => $googleUser->getId(),
            'google_token' =>  $token->getToken(),
        ]);

        return new SelfValidatingPassport(
            new UserBadge($token->getToken())
        );
    }
...

In my security.yaml

providers:
    user_provider:
        entity:
            class: App\Entity\User
            property: google_token
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    public:
        pattern: ^/(|connect/google)/
        security: false
    google_authenticator:
        custom_authenticator: App\Security\GoogleAuthenticator
        logout:
            path: app_logout
access_control:
    - { path: ^/, roles: PUBLIC_ACCESS }
    - { path: /connect/google, roles: PUBLIC_ACCESS }
    - { path: /dashboard, roles: IS_AUTHENTICATED_FULLY }

What I am trying to prove here, is that I shouldn't be able to access /dashboard unless I am authenticated, but it's letting me access that page regardless. When I am logged in, I can dump out the authenticated user from the session, so I know I can authenticate users.

  • What do I need to change about my access control to protect my /dashboard route?
  • Is a custom authenticator meant to just be ran on only the login (to authenticate the user) or if it should be ran in every request (to check that there is a logged in user)?

Thanks.


Solution

  • Symfony stops its checks at the first "validated" rule:

    For each incoming request, Symfony checks each access_control entry to find one that matches the current request. As soon as it finds a matching access_control entry, it stops - only the first matching access_control is used to enforce access.

    Reference: https://symfony.com/doc/current/security/access_control.html

    In your case, it matches everytime the first rule { path: ^/, roles: PUBLIC_ACCESS }.

    To solve your issue, move { path: ^/, roles: PUBLIC_ACCESS } at the end of your configuration:

    access_control:
        - { path: /connect/google, roles: PUBLIC_ACCESS }
        - { path: /dashboard, roles: IS_AUTHENTICATED_FULLY }
        - { path: ^/, roles: PUBLIC_ACCESS }