1

I am using PHPUnit test to write functional test for specific endpoint.

Problem with it is that for the authorization process I have to set X-API-KEY in headers section of the request.

I keep getting an error:

Authentication Required

I am using an valid api key form my test database table and it return this specific error I mentioned above.

 public function testDoItSuccessful()
{
    $client = static::createClient(
        [],
        ['HTTP_x-api-key' => 'clWD0ggquG1Ok2xOVLIcMmPJtu1uYWG']
    );

    $client->request(
        Request::METHOD_POST,
        '/api/v1/do-it',
        [],
        [],
        [
            'CONTENT_TYPE' => 'application/json',
            'ACCEPT' => 'application/json',
        ],
        json_encode($myArray)
    );

   return $client;
 }

As I am new, primarily with authorization process in test env any help is highly appreciated.

Note: I am using Symfony 4.4

I tried THIS.

Error is coming for these two functions in TokenAuthenticator class.

public function supports(Request $request)
{
    $hasApiKey = true;
    $requestHeaders = $this->getLowerCasedHeaders();
    if (!isset($requestHeaders['x-api-key'])) {
        $hasApiKey = false;
    }
    return $hasApiKey;
}

private function getLowerCasedHeaders()
{
    $requestHeaders = getallheaders();
    return array_change_key_case($requestHeaders, CASE_LOWER);
}


public function getCredentials(Request $request)
{
    $requestHeaders = $this->getLowerCasedHeaders();
    return $requestHeaders['x-api-key'];
}

public function getUser($credentials, UserProviderInterface $userProvider)
{
    if (null === $credentials) {
        // Code 401 "Unauthorized"
        return null;
    }

    return $this->entityManager->getRepository(Client::class)->findOneBy(['apiKey' => $credentials]);
}

public function checkCredentials($credentials, UserInterface $user)
{
    if ($user->getStatus() != Client::STATUS_ACTIVE) {
        throw new AuthenticationException("USER_NOT_ACTIVE", 403);
    }
    $user->setLastSeen(new DateTime('now'));
    $this->entityManager->persist($user);
    $this->entityManager->flush();
    return true;
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    return null;
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
    $data = [
        'message' => 'Authentication failed, wrong api key'
    ];
    if ($exception->getCode() == 403) {
        $data = ['message' => $exception->getMessage()];
    }

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

public function start(Request $request, AuthenticationException $authException = null)
{
    $data = [
        // you might translate this message
        'message' => 'Authentication Required'
    ];

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

public function supportsRememberMe()
{
    return false;
}

I have changed apache_request_headers() to getallheaders() and tests are passing with Authentication Required message.

When print_r() the:

$requestHeaders = $this->getLowerCasedHeaders();

I can not see my defined headers from request? It returns Array()..

2 Answers 2

1

You need to check how the Client Class parse the headers, specially on the test, there is a part where the code execute a search for the word HTTP_ for the custom header. So basically on your test instead of call only x-api-key you need to add the prefix HTTP_. Try with this:

TokenAuthenticator

public function supports(Request $request)
{
    return $request->headers->has('x-api-key');
}

On your test:

public function testDoItSuccessful()
{
     crawler = $this->client->request(
         Request::METHOD_POST,
         '/api/v1/do-it',
         [],
         [],
         ['HTTP_x-api-key' => self::TOKEN, 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json']
     );
     $status = $this->client->getResponse()->getStatusCode();
}
Sign up to request clarification or add additional context in comments.

5 Comments

This works, but i am getting 401 (Authentication Required) error? @RicardoJesusRuiz
in this case, maybe the Authenticator class needs more changes, could you update the question?
I updated the issue. Please check. @RicardoJesusRuiz
in this case @misafe, I want to see the rest of the Authenticator class, why? because in some part of the code is throwing an exception or not returning the correct values. You can check the next video symfonycasts.com/screencast/symfony4-security/…
I understand. I posted entire class. Thanks a lot for your help. @RicardoJesusRuiz
1

For me what worked is to return a UserInterface with the role defined in your security:

access_control:
    - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api, roles: ROLE_API }

My firewall:

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    api:
        pattern: ^/api/
        custom_authenticators:
            - App\CrediFastCore\Infrastructure\Security\ApiKeyAuthenticator

Since we need ROLE_API

public function authenticate(Request $request): Passport
{
    $apiToken = $request->headers->get('X-AUTH-TOKEN');

    if (null === $apiToken) {
        throw new CustomUserMessageAuthenticationException('No API token provided');
    }

    if ($this->parameters->get('api_key') != $apiToken) {
        //I compare api token with the one I've set on my parameters.yaml
        throw new CustomUserMessageAuthenticationException('Wrong API token provided');
    }

    $user = new User();
    $user->setName('api');
    $user->setRoles(['ROLE_API']);

    return new SelfValidatingPassport(
        new UserBadge($apiToken, function () use ($user, $apiToken) {
            return $user;
        })
    );
}

This part was the "fix"

return new SelfValidatingPassport(
        new UserBadge($apiToken, function () use ($user, $apiToken) {
            return $user;
        })
    );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.