Basic Authentication with the Browsermob Proxy
Sometimes there is a good reason for having applications tucked behind an old school basic authentication check. You remember them, its the username and password popup that comes up before any resource is displayed to the user. And sometimes you need to automate something that is in that situation.
WebDriver doesn’t really have a nice way to deal with this — which kinda makes sense since that is ‘of’ the browser, not ‘in’ the browser. But it doesn’t make our life easy. Part of the solution to this is understanding what happens when you fill in those fields. Essentially what happens is that the browser will send a specifically formatted header with every request back to the server that asked for the credentials.
Authentication: Basic base64UsernameAndPasswordSeparatedByAColon
Ah-ha! A header! Which means we can use the Browsermob Proxy to set it. (You are, of course, already running your scripts through the proxy, right?)
<pre lang="php"><?php require_once('PHPWebDriver/WebDriver.php');
require_once('PHPWebDriver/WebDriverProxy.php');
require_once('PHPBrowserMobProxy/Client.php');
class ProxyTest extends PHPUnit_Framework_TestCase {
protected static $driver;
protected static $client;
public function setUp() {
self::$driver = new PHPWebDriver_WebDriver();
self::$client = new PHPBrowserMobProxy_Client("a.flyingmonkey.ca:8080");
}
public function tearDown() {
$this-?>session->close();
self::$client->close();
}
/**
* @group proxy
* @group firefox
* @group sauce
*/
public function testAuthentication() {
$additional_capabilities = array();
$proxy = new PHPWebDriver_WebDriverProxy();
$proxy->httpProxy = self::$client->url;
$proxy->add_to_capabilities($additional_capabilities);
$this->session = self::$driver->session('firefox', $additional_capabilities);
$headers = array("Authorization" => ucfirst($GLOBALS['settings']['authentication']['type']) . " " . base64_encode($GLOBALS['settings']['authentication']['username'] . ':' . $GLOBALS['settings']['authentication']['password']));
$this->client->headers($headers);
$this->session->open("http://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.992212271085009");
}
}
?>
Credit to Kevin at Mogotest for reminding me that the BMP can set headers.
But setting headers in this way sets them for every request that flows through it. Which means the username and password are going to be sent to Google for their hosted JS libraries, to Github for gist embeds, etc. Ignoring the possible security problems of this, if you are pulling something in that also needs basic auth, it is likely to fail, which could have ‘interesting’ side effects. For instance, we broke YouTube integration with a particular client this week because of this.
The BMP does provide a nicer solution for Basic Authentication though which until this week wasn’t exposed via the REST API. First, you will need at least BMP 0.8 — which hasn’t been released yet, but which I have put my locally compiled one on Dropbox. And once you have that, you will need, for Python, browsermob-proxy 0.4.0 or PHP, PHPBrowserMobProxy 1.0.4. The rest of the bindings, dunno…
With a current BMP and binding, you can have this as your script.
<pre lang="php">
/**
* @group proxy
* @group firefox
* @group sauce
*/
public function testAuthentication() {
$additional_capabilities = array();
$proxy = new PHPWebDriver_WebDriverProxy();
$proxy->httpProxy = self::$client->url;
$proxy->add_to_capabilities($additional_capabilities);
$this->session = self::$driver->session('firefox', $additional_capabilities);
// for all hosts in the domain, just do httpwatch.com.
// can be as loose or specific as you want/need
self::$client->basic_auth('www.httpwatch.com', array('username' => 'httpwatch', 'password' => 'blah'));
$this->session->open("http://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.992212271085009");
}
}
?>
Thanks to Dave for doing the work on the BMP to expose this.