0

I am using Basic Auth in my API (Silex), an endpoint receives user+pw from client, validates the user via basic auth and then returns the token to be used for further requests. Now when my app makes an AJAX call, if the credentials are right, everything works smooth. If the credentials are wrong, the API returns a 401 and a set WWW-Authenticate header. This causes the browsers to automatically show the default browser login form.

I don't want that to happen. In StackOverflow, they say the only two solutions are to either return a 400 instead of a 401, or to change the WWW-Authenticate header to something like 'FormBased'.

Both the statusCode is set to 401 and the WWW-Authenticate to "Basic ..." in the BasicAuthenticationEntryPoint.php in the Security Component.

If I apply the changes there, it works... but I need to have that as part of my project ofc... How should I overwrite Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint.php to adapt it to my needs? any idea if there's a workaround? I understand this should be a very common problem, how is it generally solved?

mezod
  • 2,313
  • 3
  • 21
  • 31
  • So you dont want to use http auth from the webserver but rather your own login system? – prodigitalson Feb 02 '15 at 13:14
  • I do want to use Basic Auth in the webserver. But just for the request where I return the token. Given the credentials, I am authenticating the user and returning the token so that I don't need to keep state in the backend, but the js app will just need to add the token with each future request. – mezod Feb 02 '15 at 13:18
  • 1
    Ok sounds like you would just create your own [provider](http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-authentication-provider) then, and using it to set up a [series of custom classes](http://symfony.com/doc/current/components/security/firewall.html) which will allow you to define your own entry point class. Im not sure how the orchestration works exactly so I cannot currently give you details, I can only say it is possible and point you in this general direction. – prodigitalson Feb 02 '15 at 13:28

1 Answers1

0

Ok so here's what I did in case someone wonders:

First in my Security folder, I created my own version of the BasicAuthenticationEntryPoint.php

<?php

/*
 * Redefinition of the Symfony's BasicAuthenticationEntryPoint
 */

namespace multikanban\multikanban\Security\Http\EntryPoint;

use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;

/**
 * BasicAuthenticationEntryPoint starts an HTTP Basic authentication.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class BasicAuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
    private $realmName;

    public function __construct($realmName)
    {
        $this->realmName = $realmName;
    }

    /**
     * {@inheritdoc}
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $response = new Response();
        $response->headers->set('WWW-Authenticate', 'FormBased');
        $response->setStatusCode(401);

        return $response;
    }
}

Note that I did two things:

  1. Add the use for the AuthenticationEntryPointInterface.
  2. Change the WWW-Authenticate value to 'FormBased', this being the actual modification to the original file, so that the browsers don't show the default prompt when the server returns a 401 Unauthorized. (You could also return a 400 but then you wouldn't really be complying with the standard)

Second, I defined the service in my Silex Application like so:

    $this['security.entry_point.main.http'] = $this->share(function() {
        return new BasicAuthenticationEntryPoint('main');
    });

'main' being my firewall name.

Obviously, I also added the use at the top of the Application.php:

use multikanban\multikanban\Security\Http\EntryPoint\BasicAuthenticationEntryPoint;
mezod
  • 2,313
  • 3
  • 21
  • 31
  • Looks like it's what I need but I don't get where to put the "$this['security.entry_point.main.http'] = $this->share..." line of code ? AppKernel.php ? Sorry if I missed something. The only Application.php I have is the one in vendor/symfony and I believe you never want to change this file... – vivien.destpern Aug 22 '16 at 07:41
  • Just in case someone is looking for a detailled answer to this question : https://github.com/symfony/symfony/issues/10035 TL;DR : you can do this by setting your own "entry point" via the entry_point configuration under a firewall: http://symfony.com/doc/current/reference/configuration/security.html. Just create a class that implements AuthenticationEntryPointInterface, register it as a service, and put the id there – vivien.destpern Aug 22 '16 at 08:13