3

I am writing a REST API endpoint in PHP / Symfony 4.0 and I want to use basic authentication, with an in-memory user whose password is blank. When I try to call the endpoint with these credentials, I get a BadCredentialsException saying 'The presented password cannot be empty'.

It appears that the exception is being thrown by the DaoAuthenticationProvider.

How can I configure basic authentication in Symfony to allow an empty password?

Context: I'm setting up basic authentication for a simple endpoint that will be used as a Prismic custom API for an integration field (see https://user-guides.prismic.io/en/articles/1401183-connect-to-a-custom-api). Prismic supports basic authentication for this, where you enter a key which will be used as the username, and the password will be left blank.

I am writing this in PHP / Symfony 4.0, and I have been following the docs at https://symfony.com/doc/current/security.html .

My config/packages/security.yaml file is:

security:
    firewalls:
        secured_area:
            pattern: ^/api/my-endpoint
            http_basic: ~
            security: true
        main:
            security: false

    providers:
        in_memory:
            memory:
                users:
                    test:
                        password: ''

    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

The full error message in my logs is:

Basic authentication failed for user. {"username":"test","exception":"[object] (Symfony\Component\Security\Core\Exception\BadCredentialsException(code: 0): Bad credentials. at my-project\vendor\symfony\security\Core\Authentication\Provider\UserAuthenticationProvider.php:84, Symfony\Component\Security\Core\Exception\BadCredentialsException(code: 0): The presented password cannot be empty. at my-project\vendor\symfony\security\Core\Authentication\Provider\DaoAuthenticationProvider.php:54)"} []

yivi
  • 42,438
  • 18
  • 116
  • 138
dans
  • 39
  • 4
  • Just for clarification: which Symfony version do you use? Have you tried updating it to the most recent version of 4.3? – Nico Haase Aug 15 '19 at 10:06
  • Symfony 4.0. I have tried updating to Symfony 4.3 and I get the same exception. – dans Aug 15 '19 at 11:32

1 Answers1

2

If you really don't want to have a proper authentication, and then authenticated user will do a call to a remote endpoint with the API key, and resolve the task in a more "dirty" way, then what you can do is:

  • create your own AuthenticationProvider, which will extend the original https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php

  • overwrite the checkAuthentication method where you will have same code as original, but without check on empty password (and perhaps everything else related in that else statement)

  • overwrite the service definition to point to your own created AuthenticationProvider

     <service id="security.authentication.provider.dao" class="XXXX Your class here XXXX" abstract="true">
         <argument /> <!-- User Provider -->
         <argument /> <!-- User Checker -->
         <argument /> <!-- Provider-shared Key -->
         <argument type="service" id="security.encoder_factory" />
         <argument>%security.authentication.hide_user_not_found%</argument>
     </service>
    
  • clear the cache and try

Overwrite the service:

services:
  ...
  ...
  security.authentication.provider.dao:
    alias: MyProject\Service\Security\OverriddenDaoAuthenticationProvider
    public: true

In your OverriddenDaoAuthenticationProvider class remember to call the original constructor to pass through the parameters:

public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, EncoderFactoryInterface $encoderFactory, bool $hideUserNotFoundExceptions = true)
{
    parent::__construct($userProvider, $userChecker, $providerKey, $encoderFactory, $hideUserNotFoundExceptions);
}
yivi
  • 42,438
  • 18
  • 116
  • 138
Anton
  • 417
  • 3
  • 9
  • Thanks, this looks really promising. I have created my own AuthenticationProvider. How do I overwrite the service definition? I have a `services.yaml` file which handles the dependency injection for classes within this project. Could I use that? – dans Aug 15 '19 at 11:35
  • @dans In a Symfony 4 services are mostly autowired, so the next link is explicitly showing the way how we lived before autowiring. [Link to version 3.1](https://symfony.com/doc/3.1/service_container.html#creating-configuring-services-in-the-container) . In Symfony 4 you still can use that method as described in the link to explicitly define (redefine) services. Important, that you use the service alias and set it as the service Id which you can see in the xml definition above, because in Symfony4 we can define service with and without alias. – Anton Aug 15 '19 at 11:50
  • when you will define a service with the alias = 'security.authentication.provider.dao' and you class, it will overwrite the original one – Anton Aug 15 '19 at 11:52
  • thanks very much for your help with this. I am trying to redefine the service using an alias as you suggest, but I am not sure if I have the right syntax. In my `services.yaml` file I now have: `MyProject\Service\Security\OverriddenDaoAuthenticationProvider: alias: security.authentication.provider.dao public: true` However it appears that I still haven't persuaded Symfony to use my OverriddenDaoAuthenticationProvider, as I am still getting the same error thrown by the original DaoAuthenticationProvider. Can you see if there is an error in my syntax? – dans Aug 15 '19 at 13:00
  • I feel like this must be getting closer. I tried the syntax you suggested and I now get a Symfony `OutOfBoundsException` which says "Service "security.authentication.provider.dao.secured_area": Cannot replace arguments if none have been configured yet." Do I need to change something else in my `security.yaml` file? – dans Aug 15 '19 at 13:29
  • @dans see the **Update2** section in the answer above. Perhaps the call to the original constructor is missed – Anton Aug 15 '19 at 14:15