Search code examples
phpsymfonysecurityauthenticationbasic-authentication

Symfony http basic auth for dev environment without user authentication


I have a usual Symfony application including firewalls, user providers, and login via form (FOSUserBundle), custom token authentication (for an app) and even an http basic auth for one part of the application. Of course only one of them is used at a time. Specific areas are secured by different firewalls that connect to those providers.

Extract of the security.yml

security:

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    providers:
        authentication_provider:
            entity:
                class: MyAppBundle:User
                property: apiKey
        fos_userbundle:
            id: fos_user.user_provider.username

...

Usually there is no app_dev.php on the live system as this is a huge security hole. But it also is a great way to do debugging on the live system if you need to. The idea I had was to just secure the dev environment with an additional http basic auth. This way the dev environment would still be secure but you could access it when needed.

But unfortunately I couldn't get it to work, because as soon as you add the http basic auth to the site in general and log in, your user is authenticated for the Symfony application. This is of course the hole point of doing an authentication in the first place but a deal breaker for the idea because the application can't differentiate between a form login user and a basic auth one (I know I could differentiate between the user types but this would be an additional layer of complexity I'm reluctant to add because it has to be checked in the hole application).

My question is whether it's possible to have a Symfony native http basic auth without the authentication part. So that after completing the basic auth the browser is still sending the http basic auth but you're still anonymous and still have to login via a form (for example) to be authenticated and have access to otherwise restricted content.

EDIT: I know that there are ways to configure this outside of Symfony in the server configuration like in Apache, but I'm searching for a Symfony solution (works without hosting changes incl. locally, can be pushed to source control, ...)

EDIT 2: After a deep dive I've seen that all authentication processes have to return some kind of token. So it might not be as simple as just overwriting a provider and return a Anonymus Token.


Solution

  • Apparently there is no way to archive the searched result because of the way the hole security components are built up. They all turn around the idea that there is a user that is authenticated. Without it the components involved don't really make sense.

    After hours of trail and error I found an alternative solution. I've added a separate environment debugging for this use case. It has the debug flag set to true in the AppKernel.php. The configuration and security components look similar to the dev environment with the following changes:

    ``

    config_debugging.yml

    imports:
        - { resource: config.yml }
        - { resource: security_debugging.yml }
        - ...
    
    services:
        my.debugging.exception_listener:
            class: My\Bundle\AppBundle\EventListener\DebuggingExceptionListener
            tags:
                - { name: kernel.event_listener, event: kernel.exception }
        my.debugging.access_listener:
            class: My\Bundle\AppBundle\EventListener\DebuggingAccessListener
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
            arguments: [ '@security.authorization_checker', '@security.token_storage' ]
    

    security_debugging.yml

    security:
        firewalls:
            app_resources:
                pattern:  ^/(_(profiler|wdt)|css|images|js)/
                security: false
            ...
            main:
                pattern: ^/
                http_basic:
                    provider: fos_userbundle
    
    
        access_control:
            - { path: ^/admin/, role: ROLE_ADMIN }
            - { path: ^/, role: ROLE_DEBUGGING }
    
        role_hierarchy:
            ROLE_ADMIN:         [ROLE_USER,ROLE_DEBUGGING,ROLE_SONATA_ADMIN]
            ROLE_SUPER_ADMIN:   ROLE_ADMIN
    

    The two listener are there to check for the right role ROLE_DEBUGGING. Every admin is then always able to login and when debugging a specific user, the use has to have the specific role set for him.

    DebuggingExceptionListener.php

    ...
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // You get the exception object from the received event
        $exception = $event->getException();
    
        if ($exception instanceof AccessDeniedHttpException) {
            $event->setResponse(new RedirectResponse('/'));
        }
    }
    ...
    

    DebuggingAccessListener.php

    ...
    public function onKernelRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }
    
        if ($this->tokenStorage->getToken() === null) {
            return;
        }
    
        if ($this->authorizationChecker->isGranted('ROLE_DEBUGGING')) {
            return;
        }
    
        $event->setResponse(new RedirectResponse('/'));
    }
    ...
    

    This way it's possible to check nearly the hole application still secured by a login. It's not possible to debug all components that involve the usual login components or any AccessDeniedHttpExceptions. But for now it's the best solution I could come up with. I still hope there is a better solution out there.