I have been using http digest auth for my API for a long time, seemingly it worked as I expected.
However it appears to error and prompt for re-auth three times. On the fourth it just logs the user for the email sent (as username) even though the passwords wrong
Im slightly confused as to why it doesnt keep re-authing until a valid password is sent.
<?php
class Model_Authentication_Init {
const REALM = 'theaudienceauth';
protected $user_id = NULL;
protected $user_permissions = array();
protected $user;
protected $db;
public static $instance;
const ADMIN_PERMISSION_ID = 1;
public function __construct() {
$this->db = Zend_Registry::get('db');
}
public static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function extractData() {
$realm = self::REALM;
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 ) . '"' );
exit ();
die ( 'Text to send if user hits Cancel button' );
}
//be careful username on account isnt the same as username in the db also, later
$data = http_digest_parse ( $_SERVER ['PHP_AUTH_DIGEST'] );
return $data;
}
protected function validateData($data, $password) {
$realm = self::REALM;
$A1 = md5 ( $data ['username'] . ':' . $realm . ':' . $password );
$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!' );
}
}
public function authData($data) {
if (isset ($data ['username'])) {
$account_q = $this->db->query (" SELECT `id`, `encrypted_password` FROM `User` WHERE `email` = '".$data['username']."' AND `enabled` = 1 AND deleted = 0 ");
$account = $account_q->fetch ( PDO::FETCH_OBJ );
$account_q->closeCursor();
if (is_object ( $account )) {
$password_fetched = decrypt_password ( $account->encrypted_password ); //@todo might need to decr.
if (! defined ( 'CONNECTED_ACCOUNT_ID' )) {
define ( 'CONNECTED_ACCOUNT_ID', $account->id );
}
$_SESSION ['logged_in'] = 1;
$_SESSION ['CONNECTED_ACCOUNT_ID'] = $account->id;
$_SESSION ['email'] = $data['username'];
$user_permissions = Model_User_Permission::listing ( $account->id );
$_SESSION['user_permissions'] = $user_permissions;
$user_id = $account->id;
$account = NULL;
} else {
die ( 'Could not auth with details passed: email: ' . $data ['username'] );
}
}
$this->user_id = $user_id;
$this->user_permissions = $user_permissions;
return array($user_id, $user_permissions, $password_fetched);
}
/**
* Main auth method
* @throws Model_Exception_Application
*/
public function auth() {
$userId = NULL;
$user_permissions = NULL;
if (! isset ( $_SESSION ['logged_in'] ) || $_SESSION ['logged_in'] != 1) {
$data = $this->extractData();
list($userId, $user_permissions, $password_fetched) = $this->authData($data);
// analyze the PHP_AUTH_DIGEST variable
if (! ($data)) {
throw new Model_Exception_Application ( 903 );
die ( 'Wrong Credentials!' );
}
$this->validateData($data, $password_fetched);
} else {
if (! defined ( 'CONNECTED_ACCOUNT_ID' )) {
define ( 'CONNECTED_ACCOUNT_ID', $_SESSION ['CONNECTED_ACCOUNT_ID'] );
}
$userId = $_SESSION ['CONNECTED_ACCOUNT_ID'];
//if user permissions are not set to a session, set it
$user_permissions = $_SESSION['user_permissions'] = Model_User_Permission::listing($userId);// isset($_SESSION['user_permissions']) && !empty($_SESSION['user_permissions']) ? $_SESSION['user_permissions'] : Model_User_Permission::listing($userId);
}
$this->user_id = $userId;
$this->user = $_SESSION['user'] = Model_Baseclass::load_by_fields(array('table_name' => 'User', 'id' => $userId)); //isset($_SESSION['user']) && !empty($_SESSION['user']) ? $_SESSION['user'] : Model_Baseclass::load_by_fields(array('table_name' => 'User', 'id' => $userId));
$this->user_permissions = $user_permissions;
}
}
The above is called by:
Model_Authentication_Init::getInstance($this->rest);
Edit: not sure what the cause was now, because it was a long time ago, but here we go:
<?php
class System_Authentication_Init {
const REALM = 'auth';
protected $user_permissions = array();
protected $user;
protected $db;
public static $instance;
const ADMIN_PERMISSION_ID = 1;
public function __construct() {
$this->db = Zend_Registry::get('db');
}
public static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function extractData() {
$realm = self::REALM;
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 ) . '"' );
exit ();
die ( 'Text to send if user hits Cancel button' );
}
//be careful username on account isnt the same as username in the db also, later
$data = http_digest_parse ( $_SERVER ['PHP_AUTH_DIGEST'] );
return $data;
}
protected function validateData($data, $password) {
$realm = self::REALM;
$A1 = md5 ( $data ['username'] . ':' . $realm . ':' . $password );
$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) {
session_destroy();
session_unset();
session_regenerate_id();
die ( 'Wrong Credentials!' );
}
}
public function authData($data) {
if (isset ($data ['username'])) {
$accountQuery = $this->db->query (" SELECT `id`, `encrypted_password` FROM `User` WHERE `email` = '".$data['username']."' AND `enabled` = 1 AND deleted = 0 ");
$account = $accountQuery->fetch ( PDO::FETCH_OBJ );
$accountQuery->closeCursor();
if (is_object ( $account )) {
$passwordReturned = decrypt_password ( $account->encrypted_password );
if (! defined ( 'CONNECTED_ACCOUNT_ID' )) {
define ('CONNECTED_ACCOUNT_ID', $account->id);
}
$_SESSION ['logged_in'] = 1;
$_SESSION ['CONNECTED_ACCOUNT_ID'] = $account->id;
$_SESSION ['email'] = $data['username'];
$user_permissions = Model_User_Permission::listing ( $account->id );
$_SESSION['user_permissions'] = $user_permissions;
$account = NULL;
} else {
die ( 'Could not auth with details passed: email: ' . $data ['username'] );
}
}
$this->user_permissions = $user_permissions;
return array($user_permissions, $passwordReturned);
}
/**
* Main auth method
* @throws System_Exception_Application
*/
public function authenticate() {
$user_permissions = NULL;
if (! isset ( $_SESSION ['logged_in'] ) || $_SESSION ['logged_in'] != 1) {
$data = $this->extractData();
list($user_permissions, $passwordReturned) = $this->authData($data);
// analyze the PHP_AUTH_DIGEST variable
if (! ($data)) {
throw new System_Exception_Application ( 903 );
die ( 'Wrong Credentials!' );
}
$this->validateData($data, $passwordReturned);
} else {
if (! defined ( 'CONNECTED_ACCOUNT_ID' )) {
define ( 'CONNECTED_ACCOUNT_ID', $_SESSION ['CONNECTED_ACCOUNT_ID'] );
}
//if user permissions are not set to a session, set it
$user_permissions = $_SESSION['user_permissions'] = Model_User_Permission::listing(CONNECTED_ACCOUNT_ID);// isset($_SESSION['user_permissions']) && !empty($_SESSION['user_permissions']) ? $_SESSION['user_permissions'] : Model_User_Permission::listing($userId);
}
$this->user = $_SESSION['user'] = Model_Baseclass::load_by_fields(array('table_name' => 'User', 'id' => CONNECTED_ACCOUNT_ID)); //isset($_SESSION['user']) && !empty($_SESSION['user']) ? $_SESSION['user'] : Model_Baseclass::load_by_fields(array('table_name' => 'User', 'id' => $userId));
$this->user_permissions = $user_permissions;
}
public function isAdmin() {
if (!$this->user_permissions || empty($this->user_permissions) ) {
$this->authenticate();
}
if ($this->user_permissions) {
foreach ($this->user_permissions as $user_permission) {
if (isset($user_permission->{'permission_id'}) && $user_permission->{'permission_id'} == self::ADMIN_PERMISSION_ID) {
return true;
}
}
}
return false;
}
/**
* returns user id
* @return User id
*/
public function getUserId() {
return $this->user->id;
}
/* Added by aaron
* returns the internal user object
* */
public function getUserObject() {
Zend_Registry::set('user', $this->user);
return $this->user;
}
/**
* returns user permissions
* @return _user_permissions User Permissions
*/
public function getUserPermissions() {
return $this->user_permissions;
}
}