sebastian_t
sebastian_t

Reputation: 2869

Basic Authentication in phpunit test case

I came up with a briliant idea of using Basic Auth in my small script since I thought it's gonna be faster than using jwt ... well I was wrong. I cannot test my endpoint now because I constantly get 401.

Source code from below is also available here: https://github.com/tarach/blog-api/tree/feature/posts-resource-test

public function testShouldCreatePost(): void
    {
        $client = static::createClient();

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => 'Basic ' . base64_encode('test:qwe123'),
        ];

        dump($headers);

        $client->request('POST', '/api/posts', [
            'headers' => $headers,
            'json' => [],
        ]);

        dump([
            'response' => [
                'status' => $client->getResponse()->getStatusCode(),
                'body' => $client->getResponse()->getContent(),
            ],
        ]);
    }

enter image description here

security.yaml

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    encoders:
        App\Infrastructure\Symfony\User\EnvironmentUser:
            algorithm: plaintext
    providers:
        environment_user_provider:
            id: App\Infrastructure\Symfony\User\EnvironmentUserProvider
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/api
            stateless: true
            anonymous: true
            provider: environment_user_provider
            http_basic:
                realm: Protected

    access_control:
         - { path: ^/api/posts, methods: ["GET"], roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

EnvironmentUserProvider uses two env. variables USER_NAME and USER_PASSWORD. via Postman it works correctly: enter image description here

Solution:

public function testShouldCreatePost(): void
    {
        // Method A)
        $client = static::createClient([], [
            'PHP_AUTH_USER' => 'test',
            'PHP_AUTH_PW'   => 'qwe123',
        ]);

        // Method B)
        $client->setServerParameter('HTTP_AUTHORIZATION', 'Basic ' . base64_encode('test:qwe123'));

        $content = json_encode([
            'title' => 'Some title',
            'body' => '<strong>Some Body</strong>'
        ]);

        $client->request(
            'POST',
            '/api/posts',
            [],
            [],
            [
                'CONTENT_TYPE' => 'application/json',
            ],
            $content
        );

        $this->assertEquals(201, $client->getResponse()->getStatusCode());
    }

Upvotes: 0

Views: 1771

Answers (1)

dbrumann
dbrumann

Reputation: 17166

If you use a recent Symfony version (5.1 or higher) there is a neat helper on client that you can use:

$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);

// retrieve the test user
$testUser = $userRepository->findOneByEmail('[email protected]');

// simulate $testUser being logged in
$client->loginUser($testUser);

// test e.g. the profile page
$client->request('GET', '/profile');

Example taken from the docs for Logging in Users in Tests.

If you use basic auth, you can pass in the credentials for the auth dialog like this:

$client = static::createClient([], [
    'PHP_AUTH_USER' => 'username',
    'PHP_AUTH_PW'   => 'pa$$word',
]);

As seen in the old docs for How to Simulate HTTP Authentication in a Functional Test.

If you want to submit the headers from your example, things are a bit tricky because you will need to pass in the internal representation as can be seen in the BrowserKit Browser.

In your case the headers should probably change like this:

$headers = [
    'CONTENT_TYPE' => 'application/json',
    'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('test:qwe123'),
];

You can see the which internal representation Symfony uses in the code for the HttpFoundation ServerBag.

Upvotes: 3

Related Questions