E
E
EVOSandru62021-09-27 14:37:10
symfony
EVOSandru6, 2021-09-27 14:37:10

Why doesn't the authenticator work when symfony tests?

Hey guys.

There is an authenticator for api requests that works correctly (a user from the security is formed) with CUrl requests like:

curl --location --request GET 'http://localhost:8081/api/v1/account/userinfo' \
--header 'Authorization: Bearer %api_token%'


But in the test, null arrives instead of User in the AccountController::getUser method : Test:


class UserinfoTest extends DbWebTestCase
{
    protected EntityManagerInterface $em;
    protected KernelBrowser $client;

    private const API_PREFIX = '/api/v1';

    protected function setUp(): void
    {
        $this->client = static::createClient();
        $this->client->disableReboot();
        $kernel = self::bootKernel();
        DatabasePrimer::prime($kernel);
        $this->em = $kernel->getContainer()->get('doctrine')->getManager();
    }

    public function test_userinfo_success()
    {
        $userRepository = $this->em->getRepository(User::class);
        $testUser = $userRepository->findOneBy(['phone' => '+7 (000) 000-00-01']);

        $token = $testUser->getTokens()[0]->getToken();

        $this->client->request('GET', self::API_PREFIX . '/account/userinfo', [
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
            ],
        ]);
        ...
    }
}


Configuration:

security:
  enable_authenticator_manager: true

  password_hashers:
    App\Model\User\Entity\User\User:
      algorithm: 'auto'

  providers:
    app_user_provider:
      entity:
        class: App\Model\User\Entity\User\User
        property: id

  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false
    main:
      lazy: true
      provider: app_user_provider
      custom_authenticators:
        - App\Security\Authenticator\ApiTokenAuthenticator
  access_control:


Authenticator:

class ApiTokenAuthenticator extends AbstractAuthenticator
{
    public function __construct(
        private ApiTokenRepository $repository
    )
    {
    }

    public function supports(Request $request): ?bool
    {
        $authHeader = $request->headers->get('Authorization');

        return $authHeader && str_starts_with($authHeader, 'Bearer ');
    }

    public function authenticate(Request $request): PassportInterface
    {
        $authHeader = $request->headers->get('Authorization');
        $token = substr($authHeader, 7);

        $apiToken = $this->repository->findOneBy(['token' => $token]) ??
            throw new CustomUserMessageAuthenticationException('Invalid API Token');

        if ($apiToken->isExpired()) {
            throw new CustomUserMessageAuthenticationException('Token expired');
        }

        $user = $apiToken->getUser();

        return new Passport(
            new UserBadge($user->getId()),
            new CustomCredentials(
                fn($credentials, UserInterface $user) => !$credentials->isExpired(),
                $apiToken
            )
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
    {
        return new JsonResponse([
            'message' => $exception->getMessageKey()
        ], Response::HTTP_UNAUTHORIZED);
    }
}


Controller:

#[Route('api/v1/account')]
class AccountController extends AbstractController
{
    public function __construct(
        private LoggerInterface $logger
    )
    {
    }

    protected function getUser(): User
    {
        return parent::getUser();
    }

    #[Route('/userinfo')]
    public function userInfo(): JsonResponse
    {
        // dump('СЮДА попадаю почему то при тестировании, хотя аутентификатор не должен пускать');

        //  *** При курле все норм ***
        //  !!! При запуске теста поломка, возвращается null вместо User !!!
        $user = $this->getUser();

        $this->logger->warning(json_encode($user));

        return $this->json([
            'code' => 200,
            'data' => [
                'user' => [
                    'username' => (string)$user->getUsername(),
                    'email' => (string)$user->getEmail(),
                    'phone' => (string)$user->getPhone(),
                ]
            ]
        ], 200, []);
    }
}


I would like that when the header arrives:
Authorization: Bearer ... The
authenticator would always work out

Answer the question

In order to leave comments, you need to log in

2 answer(s)
M
Maxim Fedorov, 2021-09-27
@EVOSandru6

Decision

$this->client->request('GET', self::API_PREFIX . '/account/userinfo', [
                'Authorization' => 'Bearer ' . $token,
        ]);

change to
$this->client->request(
    method: 'GET', 
    uri: self::API_PREFIX . '/account/userinfo',  
    server: ['HTTP_Authorization' => 'Bearer ' . $token,]
);

Proofs:
https://github.com/symfony/symfony/issues/5074
https://github.com/symfony/browser-kit/blob/5.3/Ab...

D
Dmitry Gordinskiy, 2021-09-27
@DmitriyGordinskiy

Most likely, the settings for the test and normal environments are different. Different keys for generating a token, or configs for connecting to a storage with users.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question