adamnyberg
adamnyberg

Reputation: 88

How to use php 5.5 password api with Zend_Auth_Adapter_DbTable

Until now I have stored all passwords in plain text because the site is not live and I decided to wait for the new password api.

I have this code working for passwords in plain text:

<?php
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);

$authAdapter->setTableName('account')
    ->setIdentityColumn('account_id')
    ->setCredentialColumn('account_password');

// Get our authentication adapter and check credentials
$adapter = $authAdapter;
$adapter->setIdentity($values['account_id']);
$adapter->setCredential($values['password']);

$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
    $user = $adapter->getResultRowObject();
    $auth->getStorage()->write($user);
    return true;
}
return false;

According to docs I should implement my own adapter and probably just change to make use of password_verify().

I'm missing the big picture here to how everything is working together.

My question is:

  1. Witch object should I modify? $authAdaper or $auth

Any high level (or low level :D) example code would be appreciated.

All best Adam

Upvotes: 2

Views: 1207

Answers (2)

CDuv
CDuv

Reputation: 2260

Hashes created by password_hash() needs to be compared via ̀password_verify()` because 2 hashes for the same password are not always equals (at least not with BCRYPT or ARGON2).

<?php
$pass = 'foo';
var_dump(password_hash($pass, PASSWORD_BCRYPT) === password_hash($pass, PASSWORD_BCRYPT));
// bool(false)
var_dump(password_verify($pass, password_hash($pass, PASSWORD_BCRYPT)));
// bool(true)

Someone (s7anley) did a Zend_Auth_Adapter_DbTable extension that uses password_verify(), here it is (for reference):

<?php

class Base_Auth_Adapter_BcryptDbTable extends Zend_Auth_Adapter_DbTable
{
    /**
     * @inheritdoc
     */
    protected function _authenticateCreateSelect()
    {
        $dbSelect = clone $this->getDbSelect();
        $dbSelect->from($this->_tableName)
            ->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity);

        return $dbSelect;
    }

    /**
     * @inheritdoc
     */
    protected function _authenticateValidateResult($resultIdentity)
    {
        $passwordCheck = password_verify($this->_credential, $resultIdentity[$this->_credentialColumn]);

        if (!$passwordCheck) {
            $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
            $this->_authenticateResultInfo['messages'][] = 'Supplied credential is invalid.';
            return $this->_authenticateCreateAuthResult();
        }

        $this->_resultRow = $resultIdentity;

        $this->_authenticateResultInfo['code'] = Zend_Auth_Result::SUCCESS;
        $this->_authenticateResultInfo['messages'][] =  'Authentication successful.';
        return $this->_authenticateCreateAuthResult();
    }
}

The class name says "Bcrypt" but it will work just fine with any algorithm supported by password_hash().

You can use it like that:

$authAdapter = new Base_Auth_Adapter_BcryptDbTable($databaseAdapter, 'users', 'login', 'password');
$authAdapter
    ->setIdentity('my_username')
    ->setCredential('my_password') // "clear" password
    // ->setCredentialTreatment(null) // Can't set any treatment on password (would be ignored)
;
// For any additional filtering of returned rows, use getDbSelect()
$authAdapter->getDbSelect()->where('active = "TRUE"');

Upvotes: 1

AlexP
AlexP

Reputation: 9857

If you are looking to modify the way in which your authentication operates by adding the password_hash encryption then you will need to do so within PHP.

As you still wish to use database authentication I think recreating this as a new adapter would be overkill. You could however, extend the current database adapter, such as:

class My_Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable
{
  public function setCredential($credential)
  {
    $this->_credential = password_hash($credential);
    return $this;
  }
}

This means that any password provided to the adapter will always be encrypted with the password_hash function.

This could however be acomplished outside the adapter by hashing the password prior to the call to setCredential.

$options  = array('salt' => $config->passwordSalt);
$hashPassword = password_hash($plainTextPassword, PASSWORD_BCRYPT, $options);
$adpater->setCredential($hashPassword);

This method will allow you to modify the optional parameters before passing to the adapter.

Lastly, it is worth mentioning that the setCredentialTreatment method is normally used to provided password encryption, which is performed within the SQL statement (meaning you will need to use the MySQL commands and not password_hash).

$authAdapter->setTableName('user')
      ->setIdentityColumn('email')
      ->setCredentialColumn('password')
      ->setCredentialTreatment(sprintf("MD5(CONCAT(?,'%s'))", $config->passwordSalt));

Upvotes: 1

Related Questions