1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); $this->data = array(); $txt = explode(',', $txt); foreach ($txt as $param) { list($k, $v) = explode("=", $param, 2); $this->data[$k] = trim($v, "\"'"); unset($needed_parts[$k]); } if ($needed_parts) throw new InvalidArgumentException($txt, 1); } function __get($k) { if (!isset($this->data[$k])) throw new InvalidArgumentException("Digest does not contain '$k'", 2); return $this->data[$k]; } function valid_response($A1) { $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $this->data['uri']); $valid_response = md5( $A1 . ':' . $this->data['nonce'] . ':' . $this->data['nc'] . ':' . $this->data['cnonce'] . ':' . $this->data['qop'] . ':' . $A2 ); return $this->data['response'] == $valid_response; } } class authorizer { private $realm; private $users; // arrayIterator over array ('username' => 'password') private $plainpass; // TRUE if $users stores passwords in plain text function __construct($auth_realm, ArrayIterator $userIterator, $plainpw = TRUE) { $this->realm = $auth_realm; $this->users = $userIterator; $this->plainpass = $plainpw; } static private function parse_digest($txt) { // parse the http auth header // protect against missing data $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); $data = array(); $txt = explode(',', $txt); foreach ($txt as $param) { list($k, $v) = explode("=", $param, 2); $data[$k] = trim($v, "\"'"); unset($needed_parts[$k]); } return $needed_parts ? FALSE : $data; } private static function gen_nonce() { return date('Ymd') . uniqid(); } private static function nonceok($nonce) { return date('Ymd') == substr($nonce, 0, 8); } private static function unauth($status_text, $body) { header("HTTP/1.1 403 $status_text"); die($body); } private function getlogin() { // dies header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="' . $this->realm . '",qop="auth",nonce="' . self::gen_nonce() . '",opaque="' . md5($this->realm) . '"'); // display if user hits Cancel die('OK, you obviously know when you are beaten.'); } function check() { if (empty($_SERVER['PHP_AUTH_DIGEST'])) $this->getlogin(); // analyze the PHP_AUTH_DIGEST variable try { $d = new auth_digest($digest_text = $_SERVER['PHP_AUTH_DIGEST']); } catch (InvalidArgumentException $e) { self::unauth('Bad Credentials', "Parse error: $digest_text"); } if (!isset($this->users[$uname = $d->username])) self::unauth('Unauthorized user', "Wrong credentials: user '$uname' unknown."); // check valid response $A1 = $this->users[$uname]; if ($this->plainpass) $A1 = md5("$uname:$this->realm:$A1"); if (!$d->valid_response($A1)) self::unauth('Invalid Response', "Wrong credentials: digest=$digest_text"); if (!self::nonceok($d->nonce)) $this->getlogin(); return $uname; } }