php - Symfony - protect one ressource with password / authenticate user only with password

I would like to implement a "share option" in a Symfony 6 project. Users can share certain resources with other people. The other people do not need to have an account in the application. But the sharing should still be protected with a password.

Unfortunately I haven't found anything about how to implement it using Symfony's methods.

I already tried to write a custom authenticator and check the password there. As far as I could find out, Symfony always needs a user object. This is exactly what I don't want. The people with the link should be authenticated only by a password and so call a subpage.

On the subpage there are different files available for download and the option to upload files. The "authentication" would have to serve for several requests, because the user should not always enter the password.

Each subpage can have a different password, which can be assigned by the user of the application. The password is stored in the database in the table of the resource. I am using the new Symfony Security Bundle with Passports. There I have tried the following:

public function authenticate(Request $request): Passport
{
    $password = $request->request->get("_password");
    $redirectPath = $request->getSession()->get("_security.upload.target_path");
    $pathItems = explode("/", $redirectPath);
    $hash = end($pathItems);

    $party = $this->em->getRepository(Party::class)->findOneBy(["slug" => $hash]);

    if (!$party) {
        throw new CustomUserMessageAuthenticationException('no party');
    }

    if (!$party->getPassword()) {
        throw new CustomUserMessageAuthenticationException("password wrong.");
    }

    return new Passport(
        new UserBadge("guest_user"), new CustomCredentials(
            function ($credentials, UserInterface $user) use ($party) {
                return $credentials === $party->getPassword();
            }, $password
        )
    );
}

In my opinion, however, this approach is very unclean.

Each resource has a UUID in the URL, which I can use to determine the resource. I first try to extract this from the URL and search for it in the database. The URL looks like this:

/upload/party/6c74d175-46ff-4004-a3b2-f9aa95badadc

My security configuration looks like this. I think I have some error here. Through dd() I tried to find out if the authenticator is called; unfortunately it is not. Each time the main firewall is called.

security:
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

        guest_user:
            entity:
                class: App\Entity\GuestUser

    role_hierarchy:
        ROLE_USER: ~
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        upload:
            pattern: ^/upload$
            provider: guest_user

            custom_authenticators:
                - App\Security\PasswordAuthenticator

            form_login:
                login_path: check_password
                check_path: check_password

        main:
            lazy: true
            provider: app_user_provider

            form_login:
                login_path: login
                check_path: login

            logout:
                path: app_logout

Has anyone had a challenge like this before?

Thanks a lot in advance!

Answer

Solution:

You can create a NullUser class and set an object of this class in the custom authenticator

<?php

declare(strict_types=1);

namespace App\Entity\User;

use Symfony\Component\Security\Core\User\UserInterface;

class NullUser implements UserInterface
{
    public function getUserIdentifier(): string
    {
        return '';
    }

    public function getRoles(): array
    {
        return [];
    }

    public function eraseCredentials(): void
    {
    }
}

Or use a custom voter instead of authenticators

Source