8000 [RFC][Security] Load user during CheckPassportEvent · Issue #37436 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[RFC][Security] Load user during CheckPassportEvent #37436
Closed
@wouterj

Description

@wouterj

Current situation

The authenticators load a user in the authenticate(Request $request): PassportInterface method and return a passport with the user instance and any credentials/badges.

public function authenticate(Request $request): PassportInterface
{
    $user = $this->userRepository->findOneByUsername($request->get('_username'));
    if (!$user) {
        throw new UsernameNotFoundException();
    }

    return new Passport($user, ...);
}

This method was chosen as it allowed to merge the getCredentials(), getUser() and checkCredentials() methods of Guard into 1 method, making things easier to understand and use.

The limitations

However, while working on some new features, I found a major limitation of this idea: The first place to hook into the authentication process is after the user is loaded.
This prevents things like checking CSRF or login throttling before interacting with the database (preventing server load in spam requests) and allowing login throttling to work for invalid usernames (currently, a login throttling listener on CheckPassportEvent needs a valid username in order to be called).

The solution

A possible solution I can think of is also making user loading part of the badge system, creating 2 default ways to load users:

  1. Using the preconfigured UserProviderInterface:
    public function authenticate(Request $request): PassportInterface
    {
        return new Passport(
            new UserFromProvider($request->get('_username')),
            new PasswordCredentials($request->get('_password')),
            [new LoginThrottlingBadge($request->get('_username')]
        );
    }
  2. Using a closure:
    public function authenticate(Request $request): PassportInterface
    {
        return new Passport(
            new UserFromClosure($request->get('_username'), [$this, 'loadUser']),
            new PasswordCredentials($request->get('_password')),
            [new LoginThrottlingBadge($request->get('_username')]
        );
    }
    
    public function loadUser(string $username): UserInterface
    {
        return $this->userRepository->findOneByUsername($username);
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRFC = Request For Comments (proposals about features that you want to be discussed)Security

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0