PHP Digest HTTP Authentication example not working, why?

Erik B picture Erik B · Mar 2, 2011 · Viewed 13.4k times · Source

So I decided to use digest HTTP authentication for my REST API. I googled it and found an entry in the PHP manual with an example of how to do it. So I copy the script, put it in index.php on my server, open the page in the browser, after entering my credentials the browser asks for them again and I'm stuck in an infinite loop of entering credentials. The script is included below and can be found here.

Example #7 Digest HTTP Authentication example

<?php
$realm = 'Restricted area';

//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');


if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.
           '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

    die('Text to send if user hits Cancel button');
}


// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
    !isset($users[$data['username']]))
    die('Wrong Credentials!');


// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] != $valid_response)
    die('Wrong Credentials!');

// ok, valid username & password
echo 'Your are logged in as: ' . $data['username'];


// function to parse the http auth header
function http_digest_parse($txt)
{
    // protect against missing data
    $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
    $data = array();
    $keys = implode('|', array_keys($needed_parts));

    preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

    foreach ($matches as $m) {
        $data[$m[1]] = $m[3] ? $m[3] : $m[4];
        unset($needed_parts[$m[1]]);
    }

    return $needed_parts ? false : $data;
}
?>

I also tried basic authentication and that worked perfectly.

Example #6 Basic HTTP Authentication example

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Text to send if user hits Cancel button';
    exit;
} else {
    echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
?>

It seems like $_SERVER['PHP_AUTH_DIGEST'] is always empty.

Is there something wrong with the script or is my environment funky and if so how do I fix it?

EDIT: There is nothing wrong with the script. The fault lies with the server. If anyone knows what could possibly cause this, please let me know.

Answer

DanMan picture DanMan · Apr 20, 2011

Digest authentication is a separate Apache module that's often not installed on servers - especially with cheap hosting companies. It needs to be enabled to make use of it though.

http://httpd.apache.org/docs/current/mod/mod_auth_digest.html

It depends on your distribution how to install it, if you're using Linux. On my Ubuntu Linux it was enough to create a symbolic link of the module from the deactivated modules folder to the activated modules one run sudo a2enmod auth_digest on the shell. That, and restarting Apache, of course.

I don't know about Windows.