Skip to content

User checkers are not considered #718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tydoo opened this issue May 11, 2025 · 0 comments
Open

User checkers are not considered #718

tydoo opened this issue May 11, 2025 · 0 comments

Comments

@tydoo
Copy link

tydoo commented May 11, 2025

Version(s) affected

5.2

Description

User check is not considered when login

How to reproduce

UserChecker check if user have "login" in role array

<?php

namespace App\Security;

use App\Entity\User as AppUser;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;

class UserChecker implements UserCheckerInterface {

    public function __construct(private readonly TranslatorInterface $translator) {
    }

    public function checkPreAuth(UserInterface $user): void {
        if (!$user instanceof AppUser) {
            return;
        }

        if (!in_array('login', $user->getRoles())) {
            throw new CustomUserMessageAuthenticationException(
                $this->translator->trans('Not privileged to request the resource.', [], 'security')
            );
        }
    }

    public function checkPostAuth(UserInterface $user): void {
        if (!$user instanceof AppUser) {
            return;
        }
    }
}

Security configuration

security:
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: username
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            user_checker: App\Security\UserChecker
            logout:
                path: security.logout
                target: security.login
            custom_authenticators:
                - App\Security\LoginFormAuthenticator
                - App\Security\CustomWebAuthnAuthenticator
            entry_point: App\Security\LoginFormAuthenticator
    access_control:
        - { path: ^/(login|logout|fr|en|reset-password), requires_channel: 'https',  roles: PUBLIC_ACCESS }
        - { path: ^/,                     requires_channel: 'https',  roles: IS_AUTHENTICATED_FULLY }

App\Security\CustomWebAuthnAuthenticator

<?php

namespace App\Security;

use App\Repository\UserRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Contracts\Translation\TranslatorInterface;
use Webauthn\Bundle\Security\Authentication\WebauthnBadge;
use Webauthn\Bundle\Security\Authentication\WebauthnPassport;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;

/**
 * @see https://symfony.com/doc/current/security/custom_authenticator.html
 */
class CustomWebAuthnAuthenticator extends AbstractAuthenticator {

    public function __construct(
        private readonly UrlGeneratorInterface $urlGenerator,
        private readonly RouterInterface $router,
        private readonly UserRepository $userRepository,
        private readonly TranslatorInterface $translator
    ) {
    }

    public function supports(Request $request): ?bool {
        return $request->isMethod('POST') && $request->getPathInfo() === $this->router->generate('webauthn.controller.request.response.login');
    }

    public function authenticate(Request $request): Passport {
        return new WebauthnPassport( #Dedicated Passport
            new WebauthnBadge( # Dedicated badge
                $request->getHost(),
                $request->getContent() // From the login form. See below
            )
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response {
        return new JsonResponse([
            'status' => 'ok',
            'message' => 'Authentication successful',
            'user' => $token->getUserIdentifier(),
        ], Response::HTTP_OK);
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response {
        $data = ['message' => strtr($exception->getMessageKey(), $exception->getMessageData())];

        return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
    }
}

Possible Solution

No response

Additional Context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant